Index: branches/tap_branches/ippTools/src/magicdstool.c
===================================================================
--- branches/tap_branches/ippTools/src/magicdstool.c	(revision 25900)
+++ branches/tap_branches/ippTools/src/magicdstool.c	(revision 27838)
@@ -33,4 +33,5 @@
 
 static bool definebyqueryMode(pxConfig *config);
+static bool definecopyMode(pxConfig *config);
 static bool updaterunMode(pxConfig *config);
 static bool todestreakMode(pxConfig *config);
@@ -38,4 +39,5 @@
 static bool advancerunMode(pxConfig *config);
 static bool revertdestreakedfileMode(pxConfig *config);
+static bool clearstatefaultsMode(pxConfig *config);
 static bool getskycellsMode(pxConfig *config);
 static bool toremoveMode(pxConfig *config);
@@ -44,5 +46,5 @@
 static bool tocleanupMode(pxConfig *config);
 
-static bool setmagicDSRunState(pxConfig *config, psS64 magic_id, psMetadata *where, const char *state);
+static bool setmagicDSRunState(pxConfig *config, psS64 magic_id, psString extraSetString, psMetadata *where, const char *state);
 
 # define MODECASE(caseName, func) \
@@ -65,4 +67,5 @@
     switch (config->mode) {
         MODECASE(MAGICDSTOOL_MODE_DEFINEBYQUERY,       definebyqueryMode);
+        MODECASE(MAGICDSTOOL_MODE_DEFINECOPY,          definecopyMode);
         MODECASE(MAGICDSTOOL_MODE_UPDATERUN,           updaterunMode);
         MODECASE(MAGICDSTOOL_MODE_TODESTREAK,          todestreakMode);
@@ -70,4 +73,5 @@
         MODECASE(MAGICDSTOOL_MODE_ADVANCERUN,          advancerunMode);
         MODECASE(MAGICDSTOOL_MODE_REVERTDESTREAKEDFILE,revertdestreakedfileMode);
+        MODECASE(MAGICDSTOOL_MODE_CLEARSTATEFAULTS,    clearstatefaultsMode);
         MODECASE(MAGICDSTOOL_MODE_GETSKYCELLS,         getskycellsMode);
         MODECASE(MAGICDSTOOL_MODE_TOREMOVE,            toremoveMode);
@@ -110,5 +114,5 @@
     PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
     PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
-    PXOPT_LOOKUP_BOOL(dry_run, config->args, "-dry_run", false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
@@ -124,8 +128,8 @@
     PXOPT_COPY_S32(config->args, where, "-streaks_max","streaks", "<=");
 
-    pxAddLabelSearchArgs (config, where, "-label", "magicRun.label", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "magicRun.label", "=="); // define using magicRun label
 
     ippStage stageNum = ippStringToStage(stage);
-    
+
     psString query = NULL;
     switch (stageNum) {
@@ -147,8 +151,8 @@
     case IPP_STAGE_FAKE:
     case IPP_STAGE_STACK:
-        psError(PXTOOLS_ERR_DATA, true, "%sRuns do not need to be destreaked", stage);
+        psError(PXTOOLS_ERR_CONFIG, true, "%sRuns do not need to be destreaked", stage);
         return false;
     case IPP_STAGE_NONE:
-        psError(PXTOOLS_ERR_DATA, true, "%s is not a valid stage", stage);
+        psError(PXTOOLS_ERR_CONFIG, true, "%s is not a valid stage", stage);
         return false;
     default:
@@ -159,5 +163,5 @@
 
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
         return false;
     }
@@ -233,5 +237,5 @@
     // Parse the list of runs ready to be destreaked
 
-    if (!dry_run && !psDBTransaction(config->dbh)) {
+    if (!pretend && !psDBTransaction(config->dbh)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         return false;
@@ -249,5 +253,5 @@
         psString magicRunDataGroup = psMetadataLookupStr(NULL, row, "data_group");
         psString magicRunWorkdir = psMetadataLookupStr(NULL, row, "workdir");
-        
+
         // if workdir is not supplied use the magicRun's
         if (!workdir) {
@@ -282,5 +286,5 @@
         }
 
-        if (!dry_run) {
+        if (!pretend) {
             if (!magicDSRunInsertObject(config->dbh, run)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
@@ -301,5 +305,5 @@
     }
 
-    if (!dry_run && !psDBCommit(config->dbh)) {
+    if (!pretend && !psDBCommit(config->dbh)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         return false;
@@ -319,4 +323,224 @@
 
 
+// XXX This currently allows multiple destreak runs to be queued on the same exposure if there are multiple
+// magicRuns selected!
+static bool definecopyMode(pxConfig *config)
+{
+    // Required
+    PXOPT_LOOKUP_STR(stage, config->args, "-stage", true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+
+    // Optional
+    PXOPT_LOOKUP_STR(recoveryroot, config->args, "-recoveryroot", false, false);
+    PXOPT_LOOKUP_BOOL(noreplace, config->args, "-noreplace", false);
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(set_data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    // search args
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-exp_id",  "chipRun.exp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",  "camRun.cam_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpRun.warp_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-diff_id", "diffRun.diff_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-magic_id", "magicRun.magic_id", "==");
+    PXOPT_COPY_S32(config->args, where, "-streaks_max", "magicMask.streaks", "<=");
+
+    pxAddLabelSearchArgs (config, where, "-magic_label", "magicRun.label", "=="); // define magic label
+    psString labelName = NULL;                                                    // Name of label
+    psStringAppend(&labelName, "%sRun.label", stage);
+    pxAddLabelSearchArgs (config, where, "-stage_label", labelName, "=="); // define stageRun label
+    psFree(labelName);
+
+    ippStage stageNum = ippStringToStage(stage);
+
+    psString query = NULL;
+    switch (stageNum) {
+      case IPP_STAGE_RAW:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Raw stage is not appropriate for a copied destreak");
+        return false;
+      case IPP_STAGE_CHIP:
+        query = pxDataGet("magicdstool_definecopy_chip.sql");
+        break;
+      case IPP_STAGE_WARP:
+        query = pxDataGet("magicdstool_definecopy_warp.sql");
+        break;
+      case IPP_STAGE_CAMERA:
+      case IPP_STAGE_DIFF:
+      case IPP_STAGE_FAKE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "%s has not been coded.", stage);
+        return false;
+      case IPP_STAGE_STACK:
+        psError(PXTOOLS_ERR_CONFIG, true, "Stacks do not need to be destreaked");
+        return false;
+      case IPP_STAGE_NONE:
+        psError(PXTOOLS_ERR_CONFIG, true, "%s is not a valid stage", stage);
+        return false;
+      default:
+        psError(PXTOOLS_ERR_PROG, true, "ippStageToString returned %d for invalid stage %s",
+                stageNum, stage);
+        return false;
+    }
+
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+    const char *rerun_flag =  rerun ? "" : "\n"; // String to give query to activate (or not) rerun
+
+    if (stageNum != IPP_STAGE_DIFF) {
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, "\nAND %s", whereClause);
+            psFree(whereClause);
+        }
+        psFree(where);
+
+        // treat limit == 0 as "no limit"
+        if (limit) {
+            psString limitString = psDBGenerateLimitSQL(limit);
+            psStringAppend(&query, " %s", limitString);
+            psFree(limitString);
+        }
+        if (!p_psDBRunQueryF(config->dbh, query, set_label, rerun_flag)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            return false;
+        }
+    } else {
+        // diff stage query has two types bothways and !bothways
+        // so we need to send the rerun flag and the where data twice
+        psString whereString = psStringCopy("");
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&whereString, "\nAND %s", whereClause);
+            psFree(whereClause);
+        }
+        psFree(where);
+
+        // treat limit == 0 as "no limit"
+        if (limit) {
+            psString limitString = psDBGenerateLimitSQL(limit);
+            psStringAppend(&query, " %s", limitString);
+            psFree(limitString);
+        }
+
+        if (!p_psDBRunQueryF(config->dbh, query, rerun_flag, whereString, rerun_flag, whereString)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(whereString);
+            psFree(query);
+            return false;
+        }
+        psFree(whereString);
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+          case PS_ERR_DB_CLIENT:
+            psError(PXTOOLS_ERR_SYS, false, "database error");
+          case PS_ERR_DB_SERVER:
+            psError(PXTOOLS_ERR_PROG, false, "database error");
+          default:
+            psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("magictool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // Parse the list of runs ready to be destreaked
+
+    if (!pretend && !psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *list = psArrayAllocEmpty(16); // List of runs, to print
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i]; // Row of interest
+        psS64 stage_id = psMetadataLookupS64(NULL, row, "stage_id");
+        psS64 exp_id = psMetadataLookupS64(NULL, row, "exp_id");
+        psS64 magic_id = psMetadataLookupS64(NULL, row, "magic_id");
+        psS64 inv_magic_id = psMetadataLookupS64(NULL, row, "inv_magic_id");
+        psS64 cam_id = psMetadataLookupS64(NULL, row, "cam_id");
+        psString magicRunLabel = psMetadataLookupStr(NULL, row, "label");
+        psString magicRunDataGroup = psMetadataLookupStr(NULL, row, "data_group");
+
+        psString outroot = NULL;
+        // set outroot to workdir/exp_id/stage for example /somewhere/424242/chip
+        psStringAppend(&outroot, "%s/%" PRId64 "/%s", workdir, exp_id, stage);
+
+        // create a new magicRun for this group
+        magicDSRunRow *run = magicDSRunRowAlloc(
+                0, // magic_ds_id
+                magic_id,
+                inv_magic_id,
+                "new",
+                stage,
+                stage_id,
+                cam_id,
+                set_label ? set_label : magicRunLabel,
+                set_data_group ? set_data_group : magicRunDataGroup,
+                outroot,
+                recoveryroot,
+                noreplace ? 0 :1,   // re_place
+                0,      // remove
+                0,      // fault
+                note);  // remove
+
+        psFree(outroot);
+        if (!run) {
+            psAbort("failed to alloc magicDSRun object");
+        }
+
+        if (!pretend) {
+            if (!magicDSRunInsertObject(config->dbh, run)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                psFree(run);
+                psFree(output);
+                psFree(list);
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                return false;
+            }
+            psS64 magic_ds_id = psDBLastInsertID(config->dbh); // Assigned identifier
+            run->magic_ds_id = magic_ds_id;
+        }
+
+        psArrayAdd(list, list->n, run);
+        psFree(run);
+    }
+
+    if (!pretend && !psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(output);
+
+    if (!magicDSRunPrintObjects(stdout, list, !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(list);
+        return false;
+    }
+
+    psFree(list);
+
+    return true;
+}
+
+
 static bool updaterunMode(pxConfig *config)
 {
@@ -326,8 +550,19 @@
     PXOPT_LOOKUP_STR(state, config->args, "-set_state", true, false);
 
+    // optional
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(set_data_group, config->args, "-set_data_group", false, false);
+    psString setString = NULL;
+    if (set_label) {
+        psStringAppend(&setString, ", label = '%s'", set_label);
+    }
+    if (set_data_group) {
+        psStringAppend(&setString, ", data_group = '%s'", set_data_group);
+    }
+
     PXOPT_LOOKUP_S64(magic_ds_id, config->args, "-magic_ds_id", false, false);
     if (magic_ds_id) {
 
-        return setmagicDSRunState(config, magic_ds_id, NULL, state);
+        return setmagicDSRunState(config, magic_ds_id, setString, NULL, state);
 
     } else if (!strcmp(state, "full")) {
@@ -341,5 +576,6 @@
     PXOPT_COPY_S64(config->args, where, "-stage_id", "stage_id", "==");
     PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
-
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+    PXOPT_COPY_STR(config->args, where, "-data_group", "data_group", "LIKE");
 
     if (psListLength(where->list) < 2) {
@@ -353,5 +589,5 @@
         psMetadataAddS32(where, PS_LIST_TAIL, "re_place", 0, ">", 0);
     }
-    bool result = setmagicDSRunState(config, magic_ds_id, where, state);
+    bool result = setmagicDSRunState(config, magic_ds_id, setString, where, state);
     psFree(where);
 
@@ -379,5 +615,5 @@
     psString query = pxDataGet(sql_file);
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement from %s", sql_file);
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement from %s", sql_file);
         psFree(sql_file);
         return false;
@@ -445,5 +681,5 @@
 {
     // first query the magicDSRun to find the stage and the stage_id
-    psString query = "SELECT stage, stage_id from magicDSRun where magic_ds_id = %" PRId64;
+    psString query = "SELECT stage, stage_id, magic_id from magicDSRun where magic_ds_id = %" PRId64;
 
     if (!p_psDBRunQueryF(config->dbh, query, magic_ds_id)) {
@@ -472,4 +708,5 @@
     psString stage= psMetadataLookupStr(NULL, row, "stage");
     psS64 stage_id = psMetadataLookupS64(NULL, row, "stage_id");
+    psS64 magic_id = psMetadataLookupS64(NULL, row, "magic_id");
 
     ippStage stageNum = ippStringToStage(stage);
@@ -499,5 +736,5 @@
     }
 
-    if (!p_psDBRunQueryF(config->dbh, query, magic_ds_id, stage_id, component)) {
+    if (!p_psDBRunQueryF(config->dbh, query, magic_id, stage_id, component)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         return false;
@@ -518,5 +755,5 @@
 {
     // first query the magicDSRun to find the stage and the stage_id
-    psString query = "SELECT stage, stage_id from magicDSRun where magic_ds_id = %" PRId64;
+    psString query = "SELECT stage, stage_id, magic_id from magicDSRun where magic_ds_id = %" PRId64;
 
     if (!p_psDBRunQueryF(config->dbh, query, magic_ds_id)) {
@@ -545,4 +782,5 @@
     psString stage = psMetadataLookupStr(NULL, row, "stage");
     psS64 stage_id = psMetadataLookupS64(NULL, row, "stage_id");
+    psS64 magic_id = psMetadataLookupS64(NULL, row, "magic_id");
 
     ippStage stageNum = ippStringToStage(stage);
@@ -570,5 +808,5 @@
         return false;
     }
-    if (!p_psDBRunQueryF(config->dbh, query, magic_ds_id, stage_id)) {
+    if (!p_psDBRunQueryF(config->dbh, query, magic_id, stage_id)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         return false;
@@ -598,4 +836,7 @@
     PXOPT_LOOKUP_STR(recovery_path_base, config->args, "-recovery_path_base", false, false);
     PXOPT_LOOKUP_BOOL(setmagicked, config->args, "-setmagicked", false);
+    PXOPT_LOOKUP_F32(streak_frac, config->args, "-streak_frac", false, false);
+    PXOPT_LOOKUP_F32(nondiff_frac, config->args, "-nondiff_frac", false, false);
+    PXOPT_LOOKUP_F32(run_time, config->args, "-run_time", false, false);
 
     if (setmagicked && (fault != 0)) {
@@ -620,5 +861,15 @@
     }
 
-    if (!magicDSFileInsert(config->dbh, magic_ds_id, component, backup_path_base, recovery_path_base, fault, "full")) {
+    if (!magicDSFileInsert(config->dbh,
+            magic_ds_id,
+            component,
+            backup_path_base,
+            recovery_path_base,
+            streak_frac,
+            nondiff_frac,
+            run_time,
+            fault,
+            "full"  // data_state
+        )) {
             // rollback
         if (!psDBRollback(config->dbh)) {
@@ -651,5 +902,5 @@
     psString query = pxDataGet("magicdstool_completed_runs.sql");
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
         return false;
     }
@@ -699,5 +950,5 @@
 
         // set magicDSRun.state to 'full'
-        if (!setmagicDSRunState(config, magic_ds_id, NULL, "full")) {
+        if (!setmagicDSRunState(config, magic_ds_id, NULL, NULL, "full")) {
             psError(PS_ERR_UNKNOWN, false, "failed to change magicDSRun.state for magic_ds_id: %" PRId64,
                 magic_ds_id);
@@ -747,4 +998,49 @@
 
     if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to revert");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+    return true;
+}
+static bool clearstatefaultsMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    // new state
+    PXOPT_LOOKUP_STR(new_state, config->args, "-set_state", false, false);
+    // old state (required)
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+    PXOPT_COPY_S64(config->args, where, "-magic_ds_id", "magicDSRun.magic_ds_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "fault", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    psString query = pxDataGet("magicdstool_clearstatefaults.sql");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search arguments are required");
+        return false;
+    }
+    psFree(where);
+
+    if (!new_state) {
+        if (!strcmp(state, "failed_revert")) {
+            new_state = "new";
+        } else if (!strcmp(state, "failed_cleanup")) {
+            new_state = "goto_cleaned";
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "unexpected value for state: %s", state);
+            return false;
+        }
+    }
+    if (!p_psDBRunQueryF(config->dbh, query, new_state)) {
         psError(PS_ERR_UNKNOWN, false, "failed to revert");
         psFree(query);
@@ -809,6 +1105,6 @@
     for (int i=0; i<psArrayLength(output); i++) {
         psMetadata *row = output->data[i];
-        psS64 magic_ds_id = psMetadataLookupS64(NULL, row, "magic_ds_id"); 
-        psString old_state = psMetadataLookupStr(NULL, row, "state"); 
+        psS64 magic_ds_id = psMetadataLookupS64(NULL, row, "magic_ds_id");
+        psString old_state = psMetadataLookupStr(NULL, row, "state");
         psString new_state;
         if (!strcmp(old_state, "goto_censored")) {
@@ -838,4 +1134,5 @@
 
     psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-magic_ds_id", "magicDSRun.magic_ds_id", "==");
     PXOPT_COPY_STR(config->args, where, "-class_id",    "warpSkyCellMap.class_id", "==");
     PXOPT_COPY_STR(config->args, where, "-skycell_id",  "warpSkyCellMap.skycell_id", "==");
@@ -845,16 +1142,16 @@
     psString query = pxDataGet("magicdstool_getskycells.sql");
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
-    }
-
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = NULL;
     if (psListLength(where->list)) {
-        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&query, " AND %s", whereClause);
-        psFree(whereClause);
+        whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringPrepend(&whereClause, "\n AND ");
     }
     psFree(where);
 
-    if (!p_psDBRunQueryF(config->dbh, query, magic_ds_id)) {
+    if (!p_psDBRunQueryF(config->dbh, query, whereClause, whereClause)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
@@ -901,4 +1198,7 @@
     if (!((strcmp(state, "new") == 0) ||
           (strcmp(state, "full") == 0) ||
+          (strcmp(state, "drop") == 0) ||
+          (strcmp(state, "failed_revert") == 0) ||
+          (strcmp(state, "failed_cleanup") == 0) ||
           (strcmp(state, "restored") == 0) ||
           (strcmp(state, "censored") == 0) ||
@@ -914,5 +1214,5 @@
 }
 
-static bool setmagicDSRunState(pxConfig *config, psS64 magic_ds_id, psMetadata *where, const char *state)
+static bool setmagicDSRunState(pxConfig *config, psS64 magic_ds_id, psString extraSetStr, psMetadata *where, const char *state)
 {
     PS_ASSERT_PTR_NON_NULL(state, false);
@@ -924,5 +1224,6 @@
     }
 
-    psString query = psStringCopy("UPDATE magicDSRun SET state = '%s'");
+    psString query = NULL;
+    psStringAppend(&query, "UPDATE magicDSRun SET state = '%s' %s\n", state, extraSetStr ? extraSetStr : "");
     if (magic_ds_id) {
         psStringAppend(&query, " WHERE magic_ds_id = %" PRId64, magic_ds_id);
@@ -932,9 +1233,17 @@
         psFree(whereClause);
     } else {
-        psError(PS_ERR_UNKNOWN, true, "search arugments are required");
-        return false;
-    }
-
-    if (!p_psDBRunQueryF(config->dbh, query, state)) {
+        psError(PS_ERR_UNKNOWN, true, "search arguments are required");
+        return false;
+    }
+
+    if (!strcmp(state, "goto_cleaned")) {
+        // Don't set state back to goto_cleaned if it is already cleaned
+        psStringAppend(&query, " AND (magicDSRun.state != 'cleaned')");
+
+        // don't clean up magicDSRun's where stage is camera
+        psStringAppend(&query, " AND (magicDSRun.stage != 'camera')");
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
         psError(PS_ERR_UNKNOWN, false,
                 "failed to change state for magic_id %" PRId64, magic_ds_id);
@@ -960,5 +1269,5 @@
     psString query = pxDataGet("magicdstool_toremove.sql");
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
         return false;
     }
@@ -1038,5 +1347,5 @@
     psString query = pxDataGet(sql_file);
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement from %s", sql_file);
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement from %s", sql_file);
         psFree(sql_file);
         return false;
@@ -1106,5 +1415,5 @@
     psMetadata *where = psMetadataAlloc();
 
-    PXOPT_COPY_S64(config->args, where, "-stage", "stage", "==");
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
     PXOPT_COPY_S64(config->args, where, "-magic_ds_id", "magic_ds_id", "==");
     PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
@@ -1116,5 +1425,5 @@
     psString query = pxDataGet("magicdstool_tocleanup.sql");
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
         return false;
     }
