summaryrefslogtreecommitdiff
path: root/project2/tablepatch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'project2/tablepatch.cpp')
-rw-r--r--project2/tablepatch.cpp271
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";
+}
+