diff options
Diffstat (limited to 'project2/tablepatch.cpp')
-rw-r--r-- | project2/tablepatch.cpp | 271 |
1 files changed, 271 insertions, 0 deletions
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"; +} + |