Index: trunk/ippTools/src/camtool.c
===================================================================
--- trunk/ippTools/src/camtool.c	(revision 11024)
+++ trunk/ippTools/src/camtool.c	(revision 11075)
@@ -30,5 +30,6 @@
 static bool pendingimfileMode(pxConfig *config);
 static bool addprocessedexpMode(pxConfig *config);
-static bool faultexpMode(pxConfig *config);
+static bool processedexpMode(pxConfig *config);
+static bool updateprocessedexpMode(pxConfig *config);
 static bool blockMode(pxConfig *config);
 static bool maskedMode(pxConfig *config);
@@ -52,5 +53,6 @@
         MODECASE(P3TOOL_MODE_PENDINGIMFILE,     pendingimfileMode);
         MODECASE(P3TOOL_MODE_ADDPROCESSEDEXP,   addprocessedexpMode);
-        MODECASE(P3TOOL_MODE_FAULTEXP,          faultexpMode);
+        MODECASE(P3TOOL_MODE_PROCESSEDEXP,      processedexpMode);
+        MODECASE(P3TOOL_MODE_UPDATEPROCESSEDEXP,updateprocessedexpMode);
         MODECASE(P3TOOL_MODE_BLOCK,             blockMode);
         MODECASE(P3TOOL_MODE_MASKED,            maskedMode);
@@ -86,16 +88,10 @@
     }
 
-    bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
-        return false;
-    }
-
     psString query = psStringCopy(
             "SELECT"
             "   p3PendingExp.*,"
-            "   rawScienceExp.camera"
+            "   rawExp.camera"
             " FROM p3PendingExp"
-            " JOIN rawScienceExp"
+            " JOIN rawExp"
             "   USING(exp_tag)"
             " LEFT JOIN p3ProcessedExp"
@@ -114,12 +110,4 @@
     }
 
-    if (faulted) {
-        // list only faulted rows
-        psStringAppend(&query, " %s", "AND p3PendingExp.fault != 0");
-    } else {
-        // don't list faulted rows
-        psStringAppend(&query, " %s", "AND p3PendingExp.fault = 0");
-    }
-
     // treat limit == 0 as "no limit"
     if (limit) {
@@ -177,7 +165,7 @@
             "SELECT"
             "   p2ProcessedImfile.*,"
-            "   rawScienceExp.camera"   
+            "   rawExp.camera"   
             " FROM p3PendingExp"
-            " JOIN rawScienceExp"
+            " JOIN rawExp"
             "   USING(exp_tag)"
             " JOIN p2ProcessedImfile"
@@ -345,4 +333,5 @@
         return false;
     }
+
     // optional
     psString b1_uri = psMetadataLookupStr(&status, config->args, "-b1_uri");
@@ -354,4 +343,11 @@
     if (!status) {
         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -b2_uri");
+        return false;
+    }
+
+    // default
+    psS8 code = psMetadataLookupS8(&status, config->args, "-code");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
         return false;
     }
@@ -380,7 +376,15 @@
     }
     if (!psArrayLength(output)) {
-        // XXX check psError here
         psError(PS_ERR_UNKNOWN, false, "no p3PendingExp rows found");
+        psFree(output);
         return true;
+    }
+
+    // start a transaction so we don't end up with an exp_tag is both
+    // p3PendingExp & p3ProcessedExp
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
     }
 
@@ -404,69 +408,91 @@
         pendingRow->p2_version,
         pendingRow->p3_version,
-        pendingRow->label
+        pendingRow->label,
+        code
     );
-    psFree(pendingRow);
 
     // insert the new row into the p3ProcessedImfile table
     if (!p3ProcessedExpInsertObject(config->dbh, row)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(row);
-        return false;
-    }
-
+        psFree(pendingRow);
+        return false;
+    }
     psFree(row);
 
-    return true;
-}
-
-
-static bool faultexpMode(pxConfig *config)
+    // delete the p3PendingExp row from the database
+    if (!p3PendingExpDeleteObject(config->dbh, pendingRow)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(pendingRow);
+        return false;
+    }
+
+    psFree(pendingRow);
+
+    // point of no return for p3PendingExp -> p3ProcessedExp
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool processedexpMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
 
     bool status = false;
-    psS8 code = psMetadataLookupS8(&status, config->args, "-code");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
-        return false;
-    }
-
-    if (!pxSetFaultCode(config->dbh, "p3PendingExp", config->where, code)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
-        return false;
-    }
-
-    return true;
-}
-
-
-static bool blockMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    bool status = false;
-    psString label = psMetadataLookupStr(&status, config->args, "-label");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
-        return false;
-    }
-    if (!label) {
-        psError(PS_ERR_UNKNOWN, true, "-label is required");
-        return false;
-    }
-
-    if (!p3MaskInsert(config->dbh, label)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    return true;
-}
-
-static bool maskedMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    psString query = psStringCopy("SELECT * FROM p3Mask");
+    psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
+        return false;
+    }
+
+    bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
+        return false;
+    }
+
+    psString query = psStringCopy(
+            "SELECT"
+            "   p3ProcessedExp.*,"
+            "   rawExp.camera"
+            " FROM p3ProcessedExp"
+            " JOIN rawExp"
+            "   USING(exp_tag)"   
+            " WHERE"
+            "   p3ProcessedExp.exp_tag IS NOT NULL"
+    );
+
+    if (config->where) {
+        psString whereClause = psDBGenerateWhereConditionSQL(config->where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", "AND p3ProcessedExp.fault != 0");
+    } else {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", "AND p3ProcessedExp.fault = 0");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
 
     if (!p_psDBRunQuery(config->dbh, query)) {
@@ -484,5 +510,5 @@
     if (!psArrayLength(output)) {
         // XXX check psError here
-        psError(PS_ERR_UNKNOWN, false, "no p3Mask rows found");
+        psError(PS_ERR_UNKNOWN, false, "no p3ProcessedExp rows found");
         psFree(output);
         return true;
@@ -499,4 +525,95 @@
     }
 
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "p3ProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool updateprocessedexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psS8 code = psMetadataLookupS8(&status, config->args, "-code");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
+        return false;
+    }
+
+    if (!pxSetFaultCode(config->dbh, "p3ProcessedExp", config->where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    bool status = false;
+    psString label = psMetadataLookupStr(&status, config->args, "-label");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
+        return false;
+    }
+    if (!label) {
+        psError(PS_ERR_UNKNOWN, true, "-label is required");
+        return false;
+    }
+
+    if (!p3MaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString query = psStringCopy("SELECT * FROM p3Mask");
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        // XXX check psError here
+        psError(PS_ERR_UNKNOWN, false, "no p3Mask rows found");
+        psFree(output);
+        return true;
+    }
+
+    bool simple = false;
+    {
+        bool status = false;
+        simple = psMetadataLookupBool(&status, config->args, "-simple");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
+            return false;
+        }
+    }
+
     // negative simple so the default is true
     if (!ippdbPrintMetadatas(stdout, output, "p3Mask", !simple)) {
