summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project2/console/p2consoleMain.cpp6
-rw-r--r--project2/fileRows.cpp6
-rw-r--r--project2/fileRows.h4
-rw-r--r--project2/iterate.cpp8
-rw-r--r--project2/iterate.h18
-rw-r--r--project2/noOutputExecute.h1
-rw-r--r--project2/perRowValues.h3
-rw-r--r--project2/rawView.cpp18
-rw-r--r--project2/rawView.h5
-rw-r--r--project2/rowBase.h14
-rw-r--r--project2/sourceObject.cpp6
-rw-r--r--project2/sourceObject.h1
-rw-r--r--project2/sqlMergeTask.cpp196
-rw-r--r--project2/sqlMergeTask.h119
-rw-r--r--project2/sqlRows.h4
-rw-r--r--project2/tablepatch.cpp271
-rw-r--r--project2/tablepatch.h47
-rw-r--r--project2/task.cpp3
-rw-r--r--project2/view.h12
19 files changed, 717 insertions, 25 deletions
diff --git a/project2/console/p2consoleMain.cpp b/project2/console/p2consoleMain.cpp
index a8d1ffc..3bc95e7 100644
--- a/project2/console/p2consoleMain.cpp
+++ b/project2/console/p2consoleMain.cpp
@@ -7,16 +7,22 @@ int main(int argc, char ** argv)
{
ConsoleEnvironment env(argc, argv);
BOOST_FOREACH(const boost::filesystem::path & file, env.todolist) {
+#if DEBUG
try {
+#endif
ConsoleApplicationEngine app(&env, file);
app.process();
+#if DEBUG
}
catch (const std::exception & e) {
std::cerr << "Kaboom!" << std::endl << std::endl << e.what() << std::endl;
+ throw;
}
catch (...) {
std::cerr << "Kaboom!" << std::endl << std::endl << "Unknown exception." << std::endl;
+ throw;
}
+#endif
}
}
diff --git a/project2/fileRows.cpp b/project2/fileRows.cpp
index 855136f..955767c 100644
--- a/project2/fileRows.cpp
+++ b/project2/fileRows.cpp
@@ -3,8 +3,8 @@
_FileRows::_FileRows(const xmlpp::Element * p) :
path(p->get_attribute_value("path")),
- fieldSep(p->get_attribute_value("fieldSeps")[0]),
- quoteChar(p->get_attribute_value("quoteChars")[0]),
+ fieldSep(p->get_attribute_value("fieldSep")[0]),
+ quoteChar(p->get_attribute_value("quoteChar")[0]),
newline(p->get_attribute_value("newline")),
encoding(p->get_attribute_value("encoding"))
{
@@ -83,7 +83,7 @@ _FileRows::execute() const
}
}
if (!mkCols) {
- while (curCol != columns.end()) {
+ while (values.size() < columns.size()) {
values.push_back(ValPtr(new Glib::ustring()));
curCol++;
}
diff --git a/project2/fileRows.h b/project2/fileRows.h
index 335e723..cf1158e 100644
--- a/project2/fileRows.h
+++ b/project2/fileRows.h
@@ -6,10 +6,9 @@
#include <map>
#include "view.h"
#include "iterate.h"
-#include "rowBase.h"
#include "fileStarGlibIoChannel.h"
-class _FileRows : public RowBase {
+class _FileRows : public PerRowValues {
public:
_FileRows(const xmlpp::Element * p);
~_FileRows();
@@ -19,6 +18,7 @@ class _FileRows : public RowBase {
const Glib::ustring & getColumnName(unsigned int col) const;
const Glib::ustring & getCurrentValue(const Glib::ustring & id) const;
const Glib::ustring & getCurrentValue(unsigned int col) const;
+ virtual void rowReady() const = 0;
typedef std::set<gunichar> CharSet;
const Glib::ustring path;
diff --git a/project2/iterate.cpp b/project2/iterate.cpp
index 3fc0e48..f6d4820 100644
--- a/project2/iterate.cpp
+++ b/project2/iterate.cpp
@@ -30,6 +30,14 @@ _Iterate::AddLoaders(Loaders & l, NoOutputExecutes & iterates)
}
void
+_Iterate::AddLoaders(Loaders & l, Iterates & iterates)
+{
+ l.insert(LoadersVT("sqliterate", _LoaderBase::Make<_SqlIterate, _Iterate, std::string, _SourceObject, &_SourceObject::name>(&iterates)));
+ l.insert(LoadersVT("fileiterate", _LoaderBase::Make<_FileIterate, _Iterate, std::string, _SourceObject, &_SourceObject::name>(&iterates)));
+ l.insert(LoadersVT("prociterate", _LoaderBase::Make<_ProcIterate, _Iterate, std::string, _SourceObject, &_SourceObject::name>(&iterates)));
+}
+
+void
_Iterate::executeChildren() const
{
try {
diff --git a/project2/iterate.h b/project2/iterate.h
index 53f7050..17a046c 100644
--- a/project2/iterate.h
+++ b/project2/iterate.h
@@ -19,13 +19,13 @@ class _Iterate : public virtual _SourceObject, public PerRowValues, public _NoOu
_Iterate(const xmlpp::Element * p);
virtual ~_Iterate();
- virtual void execute() const = 0;
- virtual const Glib::ustring & getCurrentValue(const Glib::ustring & id) const = 0;
static void AddLoaders(Loaders & l, NoOutputExecutes & vs);
+ static void AddLoaders(Loaders & l, Iterates & vs);
+
+ NoOutputExecutes subNOEs;
protected:
void executeChildren() const;
- NoOutputExecutes subNOEs;
};
template <class Driver>
@@ -33,10 +33,22 @@ class _GenericIterate : public _Iterate, public Driver {
public:
_GenericIterate(const xmlpp::Element * p);
+ virtual unsigned int columnCount() const
+ {
+ return Driver::columnCount();
+ }
+ virtual const Glib::ustring & getCurrentValue(const unsigned int id) const
+ {
+ return Driver::getCurrentValue(id);
+ }
virtual const Glib::ustring & getCurrentValue(const Glib::ustring & id) const
{
return Driver::getCurrentValue(id);
}
+ virtual const Glib::ustring & getColumnName(const unsigned int id) const
+ {
+ return Driver::getColumnName(id);
+ }
virtual void executeChildren() const;
virtual void execute() const;
virtual void rowReady() const;
diff --git a/project2/noOutputExecute.h b/project2/noOutputExecute.h
index 56e7b76..01abb5c 100644
--- a/project2/noOutputExecute.h
+++ b/project2/noOutputExecute.h
@@ -13,6 +13,7 @@ typedef std::map<unsigned int, NoOutputExecute> NoOutputExecutes;
class _NoOutputExecute : public virtual _SourceObject {
public:
_NoOutputExecute(const xmlpp::Element * p) : _SourceObject(p) { };
+ _NoOutputExecute(const std::string & n) : _SourceObject(n) { };
virtual ~_NoOutputExecute() { }
virtual void execute() const = 0;
};
diff --git a/project2/perRowValues.h b/project2/perRowValues.h
index 5b5b9ab..4d898ff 100644
--- a/project2/perRowValues.h
+++ b/project2/perRowValues.h
@@ -21,7 +21,10 @@ class PerRowValues {
PerRowValues();
virtual ~PerRowValues() = 0;
+ virtual unsigned int columnCount() const = 0;
+ virtual const Glib::ustring & getColumnName(unsigned int col) const = 0;
virtual const Glib::ustring & getCurrentValue(const Glib::ustring & id) const = 0;
+ virtual const Glib::ustring & getCurrentValue(unsigned int col) const = 0;
void use(const RowUser * r) const { rowUsers.insert(r); }
static const RowValuesStack & Stack() { return stack; }
diff --git a/project2/rawView.cpp b/project2/rawView.cpp
index 3fe925a..4d41e63 100644
--- a/project2/rawView.cpp
+++ b/project2/rawView.cpp
@@ -12,12 +12,30 @@ _RawView::_RawView(const xmlpp::Element * p) :
{
}
+unsigned int
+_RawView::columnCount() const
+{
+ throw std::runtime_error("_RawView::columnCount not implemented");
+}
+
+const Glib::ustring &
+_RawView::getCurrentValue(unsigned int id) const
+{
+ throw std::runtime_error("_RawView::getCurrentValue not implemented");
+}
+
const Glib::ustring &
_RawView::getCurrentValue(const Glib::ustring & id) const
{
throw std::runtime_error("_RawView::getCurrentValue not implemented");
}
+const Glib::ustring &
+_RawView::getColumnName(unsigned int id) const
+{
+ throw std::runtime_error("_RawView::getColumnName not implemented");
+}
+
void _RawView::execute(xmlpp::Element * par) const
{
BOOST_FOREACH(xmlpp::Node * node, copyRoot->get_children()) {
diff --git a/project2/rawView.h b/project2/rawView.h
index 8909927..33d0d1d 100644
--- a/project2/rawView.h
+++ b/project2/rawView.h
@@ -12,7 +12,10 @@ class _RawView : public _View {
public:
_RawView(const xmlpp::Element * p);
void execute(xmlpp::Element *) const;
- const Glib::ustring & getCurrentValue(const Glib::ustring & id) const;
+ virtual unsigned int columnCount() const;
+ virtual const Glib::ustring & getColumnName(unsigned int col) const;
+ virtual const Glib::ustring & getCurrentValue(const Glib::ustring & id) const;
+ virtual const Glib::ustring & getCurrentValue(unsigned int col) const;
private:
const xmlpp::Element * copyRoot;
};
diff --git a/project2/rowBase.h b/project2/rowBase.h
deleted file mode 100644
index c9397ef..0000000
--- a/project2/rowBase.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef ROWBASE_H
-#define ROWBASE_H
-
-class RowBase {
- public:
- virtual unsigned int columnCount() const = 0;
- virtual const Glib::ustring & getColumnName(unsigned int col) const = 0;
- virtual const Glib::ustring & getCurrentValue(const Glib::ustring & id) const = 0;
- virtual const Glib::ustring & getCurrentValue(unsigned int col) const = 0;
- virtual void rowReady() const = 0;
-};
-
-#endif
-
diff --git a/project2/sourceObject.cpp b/project2/sourceObject.cpp
index bb989b5..533ab5b 100644
--- a/project2/sourceObject.cpp
+++ b/project2/sourceObject.cpp
@@ -8,6 +8,12 @@ _SourceObject::_SourceObject(const xmlpp::Element * p) :
{
}
+_SourceObject::_SourceObject(const std::string & n) :
+ name(n),
+ order(loadOrder++)
+{
+}
+
_SourceObject::~_SourceObject()
{
}
diff --git a/project2/sourceObject.h b/project2/sourceObject.h
index b4472d4..da94614 100644
--- a/project2/sourceObject.h
+++ b/project2/sourceObject.h
@@ -7,6 +7,7 @@
class _SourceObject {
public:
_SourceObject(const xmlpp::Element * p);
+ _SourceObject(const std::string & name);
virtual ~_SourceObject() = 0;
const std::string name;
const unsigned int order;
diff --git a/project2/sqlMergeTask.cpp b/project2/sqlMergeTask.cpp
new file mode 100644
index 0000000..5ecd113
--- /dev/null
+++ b/project2/sqlMergeTask.cpp
@@ -0,0 +1,196 @@
+#include "sqlMergeTask.h"
+#include "appEngine.h"
+#include "rdbmsDataSource.h"
+#include <misc.h>
+#include <stdio.h>
+
+bool _SqlMergeTask::defaultUseTempTable = true;
+
+// Conversion logic
+_SqlMergeTask::_SqlMergeTask(const xmlpp::Element * p) :
+ _SourceObject(p),
+ _Task(p),
+ updateWhere(p->get_attribute_value("updatewhere")),
+ patchOrder(p->get_attribute_value("patchorder")),
+ earlyKeys(p->get_attribute_value("earlykeys") == "yes"),
+ tempTableCreated(false),
+ destdb(NULL),
+ dataSource(p->get_attribute_value("datasource")),
+ dtable(p->get_attribute_value("targettable")),
+ dtablet(stringf("tmp_%s_%d", dtable.c_str(), getpid()))
+{
+ Loaders loaders;
+ _Iterate::AddLoaders(loaders, sources);
+ _LoaderBase::collectAll(loaders, "project2", p, true, true);
+
+ BOOST_FOREACH(xmlpp::Node * psi, p->find("columns/column")) {
+ xmlpp::Element * e = static_cast<xmlpp::Element *>(psi);
+ cols.insert(e->get_child_text()->get_content());
+ }
+ BOOST_FOREACH(xmlpp::Node * psi, p->find("columns/column[@key='true']")) {
+ xmlpp::Element * e = static_cast<xmlpp::Element *>(psi);
+ keys.insert(e->get_child_text()->get_content());
+ }
+ BOOST_FOREACH(xmlpp::Node * psi, p->find("sql")) {
+ xmlpp::Element * e = static_cast<xmlpp::Element *>(psi);
+ if (e->get_child_text()) {
+ sqls.push_back(e->get_child_text()->get_content());
+ }
+ }
+}
+
+_SqlMergeTask::~_SqlMergeTask()
+{
+}
+
+void
+_SqlMergeTask::execute() const
+{
+ destdb = &ApplicationEngine::getCurrent()->dataSource<_RdbmsDataSource>(dataSource)->getWritable();
+ createTempTable();
+ if (earlyKeys) {
+ createTempKey();
+ copyToTempTable();
+ }
+ else {
+ copyToTempTable();
+ createTempKey();
+ }
+ TablePatch tp(*destdb, dtablet, dtable, cols);
+ foreach(Keys::const_iterator, keys, k) {
+ tp.addKey(*k);
+ }
+ tp.patch(updateWhere.empty() ? NULL : updateWhere.c_str(),
+ patchOrder.empty() ? NULL : patchOrder.c_str());
+ dropTempTable();
+}
+
+void
+_SqlMergeTask::createTempTable() const
+{
+ ModifyCommand(*destdb, stringf(
+ "CREATE TEMPORARY TABLE %s AS SELECT * FROM %s WHERE 0=1",
+ dtablet.c_str(),
+ dtable.c_str())).execute();
+ tempTableCreated = true;
+}
+void
+_SqlMergeTask::dropTempTable() const
+{
+ if (tempTableCreated) {
+ ModifyCommand(*destdb, "DROP TABLE " + dtablet).execute();
+ }
+}
+void
+_SqlMergeTask::createTempKey() const
+{
+ /* Primary key */
+ Buffer idx;
+ idx.appendf("ALTER TABLE %s ADD CONSTRAINT pk_%s PRIMARY KEY(",
+ dtablet.c_str(), dtablet.c_str());
+ foreach(Keys::const_iterator, keys, k) {
+ if (k != keys.begin()) {
+ idx.append(", ");
+ }
+ idx.appendf("%s", k->c_str());
+ }
+ idx.append(")");
+ ModifyCommand(*destdb, idx.c_str()).execute();
+ /* Indexes */
+ int n = 0;
+ foreach(Keys::const_iterator, indexes, i) {
+ ModifyCommand(*destdb, stringf(
+ "CREATE INDEX idx_%s_%d ON %s(%s)",
+ dtablet.c_str(), n, dtablet.c_str(), i->c_str())).execute();
+ n += 1;
+ }
+}
+ModifyCommand *
+_SqlMergeTask::insertCommand() const
+{
+ Buffer insC;
+ Buffer insV;
+ insC.appendf("INSERT INTO %s(", dtablet.c_str());
+ insV.append(") VALUES (");
+ foreach(Columns::const_iterator, cols, c) {
+ if (c != cols.begin()) {
+ insC.append(", ");
+ insV.append(", ");
+ }
+ insC.appendf("%s", c->c_str());
+ insV.append("?");
+ }
+ Buffer res;
+ res.appendf("%s%s)", insC.c_str(), insV.c_str());
+ return new ModifyCommand(*destdb, res.c_str());
+}
+
+class _Populate : public _NoOutputExecute {
+ public:
+ _Populate(Iterate i, ModifyCommand * c) :
+ _SourceObject(__FUNCTION__),
+ _NoOutputExecute(__FUNCTION__),
+ iter(i),
+ cmd(c)
+ {
+ }
+
+ void execute() const
+ {
+ if (idxs.empty()) {
+ idxs.resize(iter->columnCount());
+ // Sure this can be improved upon... but hey.
+ // Find each columns alphabetical index
+ unsigned int cols = iter->columnCount();
+ for (unsigned int c = 0; c < cols; c += 1) {
+ idxs[c] = 0;
+ for (unsigned int d = 0; d < cols; d += 1) {
+ if (c != d && iter->getColumnName(d) < iter->getColumnName(c)) {
+ idxs[c]++;
+ }
+ }
+ }
+ }
+ unsigned int cols = iter->columnCount();
+ for (unsigned int c = 0; c < cols; c += 1) {
+ cmd->bindParamS(idxs[c], iter->getCurrentValue(c));
+ }
+ cmd->execute();
+ }
+ private:
+ mutable std::vector<unsigned int> idxs;
+ Iterate iter;
+ ModifyCommand * cmd;
+};
+typedef boost::shared_ptr<_Populate> Populate;
+
+void
+_SqlMergeTask::copyToTempTable() const
+{
+ ModifyCommand * insert = insertCommand();
+ BOOST_FOREACH(const Iterates::value_type & i, sources) {
+ Populate p(new _Populate(i.second, insert));
+ i.second->subNOEs.insert(NoOutputExecutes::value_type(0, p));
+ i.second->execute();
+ i.second->subNOEs.erase(0);
+ }
+ delete insert;
+ BOOST_FOREACH(const std::string & sql, sqls) {
+ Buffer insC;
+ Buffer insV;
+ insC.appendf("INSERT INTO %s(", dtablet.c_str());
+ insV.append(") SELECT ");
+ BOOST_FOREACH(const std::string & c, cols) {
+ if (c != *cols.begin()) {
+ insC.append(", ");
+ insV.append(", ");
+ }
+ insC.append(c.c_str());
+ insV.append(c.c_str());
+ }
+ Buffer res;
+ res.appendf("%s%s FROM (%s) tmp_src", insC.c_str(), insV.c_str(), sql.c_str());
+ ModifyCommand(*destdb, res.c_str()).execute();
+ }
+}
+
diff --git a/project2/sqlMergeTask.h b/project2/sqlMergeTask.h
new file mode 100644
index 0000000..be5f6fa
--- /dev/null
+++ b/project2/sqlMergeTask.h
@@ -0,0 +1,119 @@
+#ifndef SQLMERGETASK_H
+#define SQLMERGETASK_H
+
+#include <connection.h>
+#include <modifycommand.h>
+#include <selectcommand.h>
+#include <buffer.h>
+#include "tablepatch.h"
+#include "task.h"
+#include "iterate.h"
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+
+class _SqlMergeTask : public _Task {
+ public:
+ typedef std::string Table;
+ typedef std::string Column;
+
+ typedef std::set<Column> Columns;
+ typedef Columns Keys;
+
+ _SqlMergeTask(const xmlpp::Element * p);
+ virtual ~_SqlMergeTask();
+
+ void execute() const;
+ Columns cols;
+ Keys keys;
+ Keys indexes;
+ const std::string updateWhere;
+ const std::string patchOrder;
+ bool earlyKeys;
+
+ private:
+ virtual void copyToTempTable() const;
+ void createTempTable() const;
+ void dropTempTable() const;
+ void createTempKey() const;
+
+ mutable bool tempTableCreated;
+ Iterates sources;
+ std::list<std::string> sqls;
+ protected:
+ ModifyCommand * insertCommand() const;
+
+ public:
+ mutable ODBC::Connection * destdb;
+ const std::string dataSource;
+ const Table dtable;
+ const Table dtablet;
+
+ static unsigned int defaultVerbosity;
+ static bool defaultUseTempTable;
+};
+
+/*
+class CopyBasedDbConversion : public DbConversion {
+ public:
+ CopyBasedDbConversion(const Connection & dest, const std::string dt);
+ virtual ~CopyBasedDbConversion();
+ private:
+ void copyToTempTable() const;
+ virtual SelectCommand * selectCommand() const = 0;
+};
+class SimpleDbConversion : public CopyBasedDbConversion {
+ public:
+ SimpleDbConversion(const Connection & src, const std::string st, const Connection & dest,
+ const std::string dt);
+ ~SimpleDbConversion();
+ SelectCommand * selectCommand() const;
+ private:
+ const Connection & srcdb;
+ const Table stable;
+ mutable SelectCommand * srcCmd;
+};
+class CustomDbConversion : public CopyBasedDbConversion {
+ public:
+ CustomDbConversion(SelectCommand * srcc, const Connection & dest, const std::string dt);
+ ~CustomDbConversion();
+ private:
+ SelectCommand * selectCommand() const;
+ SelectCommand * srcCmd;
+};
+class EasyFillDbConversion : public DbConversion {
+ public:
+ EasyFillDbConversion(const std::string src, const Connection & db, const std::string dt);
+ ~EasyFillDbConversion();
+
+ private:
+ void copyToTempTable() const;
+ const std::string srcSql;
+};
+class FileFillDbConversion : public DbConversion {
+ public:
+ FileFillDbConversion(const std::string src, const Connection & db, const std::string dt);
+ ~FileFillDbConversion();
+
+ std::list<ODBC::String> post;
+
+ private:
+ void copyToTempTable() const;
+ const std::string srcFile;
+};
+class CustomFillDbConversion : public DbConversion {
+ public:
+ typedef void(*FillFunc)(const CustomFillDbConversion *);
+
+ CustomFillDbConversion(FillFunc fill, const Connection & dest, const std::string dt);
+ ~CustomFillDbConversion();
+
+ private:
+ void copyToTempTable() const;
+ const FillFunc filler;
+};
+*/
+
+#endif
+
diff --git a/project2/sqlRows.h b/project2/sqlRows.h
index 98a20f3..eff32bc 100644
--- a/project2/sqlRows.h
+++ b/project2/sqlRows.h
@@ -8,11 +8,10 @@
#include "iterate.h"
#include "view.h"
#include "iHaveParameters.h"
-#include "rowBase.h"
class ApplicationEngine;
-class _SqlRows : public IHaveParameters, public RowBase {
+class _SqlRows : public IHaveParameters, public PerRowValues {
public:
_SqlRows(const xmlpp::Element * p);
~_SqlRows();
@@ -22,6 +21,7 @@ class _SqlRows : public IHaveParameters, public RowBase {
const Glib::ustring & getColumnName(unsigned int col) const;
const Glib::ustring & getCurrentValue(const Glib::ustring & id) const;
const Glib::ustring & getCurrentValue(unsigned int col) const;
+ virtual void rowReady() const = 0;
const std::string dataSource;
const Glib::ustring sql;
diff --git a/project2/tablepatch.cpp b/project2/tablepatch.cpp
new file mode 100644
index 0000000..3f057df
--- /dev/null
+++ b/project2/tablepatch.cpp
@@ -0,0 +1,271 @@
+#include "tablepatch.h"
+#include <syslog.h>
+#include <stdio.h>
+#include <misc.h>
+#include <selectcommand.h>
+#include <column.h>
+#include <buffer.h>
+
+TablePatch::TablePatch(const Connection & wdb, const TablePatch::Table & s, const TablePatch::Table & d,
+ const TablePatch::Columns & c) :
+ src(s),
+ dest(d),
+ cols(c),
+ db(wdb)
+{
+ if (!src.length()) {
+ throw PatchCheckFailure();
+ }
+ if (!dest.length()) {
+ throw PatchCheckFailure();
+ }
+ if (!db.inTx()) {
+ throw PatchCheckFailure();
+ }
+}
+
+void
+TablePatch::addKey(const TablePatch::Column & c)
+{
+ pk.insert(c);
+}
+
+void
+TablePatch::patch(const char * where, const char * order)
+{
+ if (pk.size() == 0) {
+ throw PatchCheckFailure();
+ }
+ doDeletes(where, order);
+ doUpdates(where, order);
+ doInserts(order);
+}
+
+void
+TablePatch::copyBind(const SelectCommand * src, ModifyCommand * dest, int cola, int colb)
+{
+ (*src)[cola].rebind(dest, colb);
+}
+
+void
+TablePatch::doDeletes(const char * where, const char * order)
+{
+ // -----------------------------------------------------------------
+ // Build SQL to select keys to delete ------------------------------
+ // -----------------------------------------------------------------
+ Buffer toDelSql;
+ toDelSql.append("SELECT ");
+ foreach (PKI, pk, pki) {
+ if (pki != pk.begin()) {
+ toDelSql.append(", ");
+ }
+ toDelSql.appendf("a.%s",
+ pki->c_str());
+ }
+ toDelSql.appendf(" FROM %s a LEFT OUTER JOIN %s b",
+ dest.c_str(), src.c_str());
+ foreach (PKI, pk, pki) {
+ if (pki == pk.begin()) {
+ toDelSql.append(" ON ");
+ }
+ else {
+ toDelSql.append(" AND ");
+ }
+ toDelSql.appendf(" a.%s = b.%s",
+ pki->c_str(), pki->c_str());
+ }
+ foreach (PKI, pk, pki) {
+ if (pki == pk.begin()) {
+ toDelSql.append(" WHERE ");
+ }
+ else {
+ toDelSql.append(" AND ");
+ }
+ toDelSql.appendf(" b.%s IS NULL",
+ pki->c_str());
+ }
+ if (where && *where) {
+ toDelSql.appendf(" AND %s", where);
+ }
+ if (order) {
+ toDelSql.appendf(" ORDER BY %s", order);
+ }
+ // -----------------------------------------------------------------
+ // Build SQL to delete keys ----------------------------------------
+ // -----------------------------------------------------------------
+ Buffer delSql;
+ delSql.appendf("DELETE FROM %s d WHERE (",
+ dest.c_str());
+ foreach (PKI, pk, pki) {
+ if (pki != pk.begin()) {
+ delSql.append(", ");
+ }
+ delSql.appendf("d.%s",
+ pki->c_str());
+ }
+ delSql.appendf(") IN (%s)", toDelSql.c_str());
+ ModifyCommand del(db, delSql.c_str());
+ del.execute();
+}
+
+void
+TablePatch::doUpdates(const char * where, const char * order)
+{
+ if (cols.size() == pk.size()) {
+ // Can't "change" anything... it's all part of the key
+ return;
+ }
+ // -----------------------------------------------------------------
+ // Build SQL for list of updates to perform ------------------------
+ // -----------------------------------------------------------------
+ Buffer toUpdSel;
+ Buffer ch;
+ Buffer k;
+ foreach (Columns::const_iterator, cols, col) {
+ if (pk.find(*col) == pk.end()) {
+ ch.appendf("b.%s, ",
+ col->c_str());
+ }
+ else {
+ if (k.length()) {
+ k.append(", ");
+ }
+ k.appendf("b.%s ",
+ col->c_str());
+ }
+ }
+ toUpdSel.appendf("SELECT %s %s FROM %s a, %s b",
+ ch.c_str(), k.c_str(), dest.c_str(), src.c_str());
+ foreach (PKI, pk, pki) {
+ if (pki == pk.begin()) {
+ toUpdSel.append(" WHERE ");
+ }
+ else {
+ toUpdSel.append(" AND ");
+ }
+ toUpdSel.appendf(" a.%s = b.%s",
+ pki->c_str(), pki->c_str());
+ }
+ if (where && *where) {
+ toUpdSel.appendf(" AND %s", where);
+ }
+ toUpdSel.append(" AND (");
+ bool first = true;
+ foreach (Columns::const_iterator, cols, col) {
+ if (pk.find(*col) == pk.end()) {
+ if (!first) {
+ toUpdSel.append(" OR ");
+ }
+ first = false;
+ toUpdSel.appendf(
+ " (((CASE WHEN (a.%s IS NULL AND b.%s IS NULL) THEN 1 ELSE 0 END) \
+ + (CASE WHEN(a.%s = b.%s) THEN 1 ELSE 0 END)) = 0)",
+ col->c_str(), col->c_str(), col->c_str(), col->c_str());
+ }
+ }
+ toUpdSel.append(")");
+ if (order) {
+ toUpdSel.appendf(" ORDER BY %s", order);
+ }
+ // -----------------------------------------------------------------
+ // Build SQL to perform updates ------------------------------------
+ // -----------------------------------------------------------------
+ Buffer updSql;
+ updSql.appendf("UPDATE %s SET ",
+ dest.c_str());
+ first = true;
+ foreach (Columns::const_iterator, cols, col) {
+ if (pk.find(*col) == pk.end()) {
+ if (!first) {
+ updSql.append(", ");
+ }
+ first = false;
+ updSql.appendf(" %s = ?",
+ col->c_str());
+ }
+ }
+ foreach (PKI, pk, pki) {
+ if (pki == pk.begin()) {
+ updSql.append(" WHERE ");
+ }
+ else {
+ updSql.append(" AND ");
+ }
+ updSql.appendf(" %s = ?",
+ pki->c_str());
+ }
+ // -----------------------------------------------------------------
+ // Iterator over update list make changes --------------------------
+ // -----------------------------------------------------------------
+ SelectCommand toUpd(db, toUpdSel.c_str());
+ ModifyCommand upd(db, updSql.c_str());
+ int cs = cols.size();
+ while (toUpd.fetch()) {
+ for (int c = 0; c < cs; c += 1) {
+ copyBind(&toUpd, &upd, c, c);
+ }
+ upd.execute(false);
+ }
+}
+
+void
+TablePatch::doInserts(const char * order)
+{
+ // -----------------------------------------------------------------
+ // Build SQL for copying new records -------------------------------
+ // -----------------------------------------------------------------
+ Buffer toInsSql;
+ toInsSql.appendf("INSERT INTO %s",
+ dest.c_str());
+ foreach (Columns::const_iterator, cols, col) {
+ if (col == cols.begin()) {
+ toInsSql.append("(");
+ }
+ else {
+ toInsSql.append(", ");
+ }
+ toInsSql.appendf("%s",
+ col->c_str());
+ }
+ toInsSql.append(") SELECT ");
+ foreach (Columns::const_iterator, cols, col) {
+ if (col != cols.begin()) {
+ toInsSql.append(", ");
+ }
+ toInsSql.appendf("b.%s",
+ col->c_str());
+ }
+ toInsSql.appendf(" FROM %s b LEFT OUTER JOIN %s a",
+ src.c_str(), dest.c_str());
+ foreach (PKI, pk, pki) {
+ if (pki == pk.begin()) {
+ toInsSql.append(" ON ");
+ }
+ else {
+ toInsSql.append(" AND ");
+ }
+ toInsSql.appendf(" a.%s = b.%s",
+ pki->c_str(), pki->c_str());
+ }
+ foreach (PKI, pk, pki) {
+ if (pki == pk.begin()) {
+ toInsSql.append(" WHERE ");
+ }
+ else {
+ toInsSql.append(" AND ");
+ }
+ toInsSql.appendf(" a.%s IS NULL",
+ pki->c_str());
+ }
+ if (order) {
+ toInsSql.appendf(" ORDER BY %s", order);
+ }
+ ModifyCommand(db, toInsSql.c_str()).execute();
+}
+
+const char *
+TablePatch::PatchCheckFailure::what() const throw()
+{
+ return "Santiy checks failed: check table names and keys";
+}
+
diff --git a/project2/tablepatch.h b/project2/tablepatch.h
new file mode 100644
index 0000000..533ee16
--- /dev/null
+++ b/project2/tablepatch.h
@@ -0,0 +1,47 @@
+#ifndef TABLEPATCH_H
+#define TABLEPATCH_H
+
+#include <string>
+#include <set>
+#include <map>
+#include <smartpointer.h>
+#include <connection.h>
+#include <modifycommand.h>
+#include <selectcommand.h>
+
+using namespace ODBC;
+
+class TablePatch {
+ public:
+ typedef std::string Table;
+ typedef std::string Column;
+ typedef std::set<Column> Columns;
+ typedef Columns PrimaryKey;
+ typedef PrimaryKey::const_iterator PKI;
+
+ class PatchCheckFailure : public std::exception {
+ public:
+ const char * what() const throw();
+ };
+
+ TablePatch(const Connection & db, const Table & src, const Table & dest, const Columns & cols);
+
+ void addKey(const Column & col);
+ void patch(const char * where, const char * order);
+
+ static void copyBind(const SelectCommand * src, ModifyCommand * dest, int cola, int colb);
+
+ private:
+ void doDeletes(const char * where, const char * order);
+ void doUpdates(const char * where, const char * order);
+ void doInserts(const char * order);
+
+ Table src;
+ Table dest;
+ PrimaryKey pk;
+ Columns cols;
+ const Connection &db;
+};
+
+#endif
+
diff --git a/project2/task.cpp b/project2/task.cpp
index 65ce359..ee5bb18 100644
--- a/project2/task.cpp
+++ b/project2/task.cpp
@@ -2,6 +2,7 @@
#include <boost/foreach.hpp>
#include "xmlObjectLoader.h"
#include "sqlTask.h"
+#include "sqlMergeTask.h"
#include "sessionClearTask.h"
#include "sessionSetTask.h"
#include "sendmailTask.h"
@@ -20,6 +21,7 @@ void
_Task::AddLoaders(Loaders & l, OrderedTasks & tasks)
{
l.insert(LoadersVT("sqltask", _LoaderBase::Make<_SqlTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
+ l.insert(LoadersVT("sqlmerge", _LoaderBase::Make<_SqlMergeTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
l.insert(LoadersVT("sessionclear", _LoaderBase::Make<_SessionClearTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
l.insert(LoadersVT("sessionset", _LoaderBase::Make<_SessionSetTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
l.insert(LoadersVT("sendmail", _LoaderBase::Make<_SendMailTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
@@ -29,6 +31,7 @@ void
_Task::AddLoaders(Loaders & l, NoOutputExecutes & tasks)
{
l.insert(LoadersVT("sqltask", _LoaderBase::Make<_SqlTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
+ l.insert(LoadersVT("sqlmerge", _LoaderBase::Make<_SqlMergeTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
l.insert(LoadersVT("sessionclear", _LoaderBase::Make<_SessionClearTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
l.insert(LoadersVT("sessionset", _LoaderBase::Make<_SessionSetTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
l.insert(LoadersVT("sendmail", _LoaderBase::Make<_SendMailTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));
diff --git a/project2/view.h b/project2/view.h
index 165ed12..23b7464 100644
--- a/project2/view.h
+++ b/project2/view.h
@@ -33,10 +33,22 @@ class _GenericView : public _View, public Driver {
public:
_GenericView(const xmlpp::Element * p);
+ virtual unsigned int columnCount() const
+ {
+ return Driver::columnCount();
+ }
+ virtual const Glib::ustring & getCurrentValue(const unsigned int id) const
+ {
+ return Driver::getCurrentValue(id);
+ }
virtual const Glib::ustring & getCurrentValue(const Glib::ustring & id) const
{
return Driver::getCurrentValue(id);
}
+ virtual const Glib::ustring & getColumnName(const unsigned int id) const
+ {
+ return Driver::getColumnName(id);
+ }
virtual void execute(xmlpp::Element * e) const;
virtual void rowReady() const;
private: