Index: branches/simmosaic_branches/ippTools/src/pstamptool.c
===================================================================
--- branches/simmosaic_branches/ippTools/src/pstamptool.c	(revision 24860)
+++ branches/simmosaic_branches/ippTools/src/pstamptool.c	(revision 27839)
@@ -34,17 +34,22 @@
 static bool datastoreMode(pxConfig *config);
 static bool moddatastoreMode(pxConfig *config);
-static bool addReqMode(pxConfig *config);
-static bool completedReqMode(pxConfig *config);
-static bool listReqMode(pxConfig *config);
-static bool pendingReqMode(pxConfig *config);
-static bool updateReqMode(pxConfig *config);
-static bool revertReqMode(pxConfig *config);
-static bool addJobMode(pxConfig *config);
-static bool listJobMode(pxConfig *config);
-static bool pendingJobMode(pxConfig *config);
-static bool updateJobMode(pxConfig *config);
-static bool addProjectMode(pxConfig *config);
+static bool addreqMode(pxConfig *config);
+static bool completedreqMode(pxConfig *config);
+static bool listreqMode(pxConfig *config);
+static bool pendingreqMode(pxConfig *config);
+static bool updatereqMode(pxConfig *config);
+static bool revertreqMode(pxConfig *config);
+static bool addjobMode(pxConfig *config);
+static bool listjobMode(pxConfig *config);
+static bool pendingjobMode(pxConfig *config);
+static bool updatejobMode(pxConfig *config);
+static bool revertjobMode(pxConfig *config);
+static bool addprojectMode(pxConfig *config);
 static bool projectMode(pxConfig *config);
-static bool modProjectMode(pxConfig *config);
+static bool modprojectMode(pxConfig *config);
+static bool getdependentMode(pxConfig *config);
+static bool pendingdependentMode(pxConfig *config);
+static bool updatedependentMode(pxConfig *config);
+static bool revertdependentMode(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -70,17 +75,22 @@
         MODECASE(PSTAMPTOOL_MODE_DATASTORE, datastoreMode);
         MODECASE(PSTAMPTOOL_MODE_MODDATASTORE, moddatastoreMode);
-        MODECASE(PSTAMPTOOL_MODE_ADDREQ, addReqMode);
-        MODECASE(PSTAMPTOOL_MODE_COMPLETEDREQ, completedReqMode);
-        MODECASE(PSTAMPTOOL_MODE_LISTREQ, listReqMode);
-        MODECASE(PSTAMPTOOL_MODE_PENDINGREQ, pendingReqMode);
-        MODECASE(PSTAMPTOOL_MODE_UPDATEREQ, updateReqMode);
-        MODECASE(PSTAMPTOOL_MODE_REVERTREQ, revertReqMode);
-        MODECASE(PSTAMPTOOL_MODE_ADDJOB, addJobMode);
-        MODECASE(PSTAMPTOOL_MODE_LISTJOB, listJobMode);
-        MODECASE(PSTAMPTOOL_MODE_PENDINGJOB, pendingJobMode);
-        MODECASE(PSTAMPTOOL_MODE_UPDATEJOB, updateJobMode);
-        MODECASE(PSTAMPTOOL_MODE_ADDPROJECT, addProjectMode);
-        MODECASE(PSTAMPTOOL_MODE_MODPROJECT, modProjectMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDREQ, addreqMode);
+        MODECASE(PSTAMPTOOL_MODE_COMPLETEDREQ, completedreqMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTREQ, listreqMode);
+        MODECASE(PSTAMPTOOL_MODE_PENDINGREQ, pendingreqMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEREQ, updatereqMode);
+        MODECASE(PSTAMPTOOL_MODE_REVERTREQ, revertreqMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDJOB, addjobMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTJOB, listjobMode);
+        MODECASE(PSTAMPTOOL_MODE_PENDINGJOB, pendingjobMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEJOB, updatejobMode);
+        MODECASE(PSTAMPTOOL_MODE_REVERTJOB, revertjobMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDPROJECT, addprojectMode);
+        MODECASE(PSTAMPTOOL_MODE_MODPROJECT, modprojectMode);
         MODECASE(PSTAMPTOOL_MODE_PROJECT, projectMode);
+        MODECASE(PSTAMPTOOL_MODE_GETDEPENDENT, getdependentMode);
+        MODECASE(PSTAMPTOOL_MODE_PENDINGDEPENDENT, pendingdependentMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEDEPENDENT, updatedependentMode);
+        MODECASE(PSTAMPTOOL_MODE_REVERTDEPENDENT, revertdependentMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -108,8 +118,10 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    PXOPT_LOOKUP_STR(uri,         config->args, "-uri",           true, false);
-    PXOPT_LOOKUP_STR(outProduct,  config->args, "-out_product",   true, false);
-    PXOPT_LOOKUP_STR(lastFileset, config->args, "-last_fileset", false, false);
-    PXOPT_LOOKUP_STR(state,       config->args, "-state",         false, false);
+    PXOPT_LOOKUP_STR(uri,         config->args, "-set_uri",           true, false);
+    PXOPT_LOOKUP_STR(outProduct,  config->args, "-set_out_product",   true, false);
+    PXOPT_LOOKUP_STR(lastFileset, config->args, "-set_last_fileset", false, false);
+    PXOPT_LOOKUP_STR(state,       config->args, "-set_state",         false, false);
+    PXOPT_LOOKUP_STR(label,       config->args, "-set_label",         false, false);
+    PXOPT_LOOKUP_S32(pollInterval, config->args, "-set_poll_interval",false, false);
 
     if (!pstampDataStoreInsert(config->dbh,
@@ -117,6 +129,9 @@
             state,
             lastFileset,
+            NULL,       // timestamp
+            label,      // label
             outProduct,
-            uri
+            uri,
+            pollInterval
         )) {
         psError(PS_ERR_UNKNOWN, false, "database error");
@@ -134,9 +149,10 @@
     PXOPT_COPY_S64(config->args, where, "-ds_id", "ds_id", "==");
 
+    PXOPT_LOOKUP_BOOL(ready, config->args, "-ready", false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psString query = pxDataGet("pstamptool_datastore.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;
     }
@@ -148,4 +164,7 @@
     }
     psFree(where);
+    if (ready) {
+        psStringAppend(&query, " %s", "\nAND TIMESTAMPDIFF(SECOND, timestamp, now()) > pollInterval");
+    }
 
     if (!p_psDBRunQuery(config->dbh, query)) {
@@ -181,25 +200,33 @@
 
     PXOPT_LOOKUP_S64(ds_id,       config->args, "-ds_id",         true, false);
-    PXOPT_LOOKUP_STR(lastFileset, config->args, "-last_fileset",  false, false);
-    PXOPT_LOOKUP_STR(state,       config->args, "-state",         false, false);
-
-    if (!state && !lastFileset) {
-        psError(PS_ERR_UNKNOWN, true, "at least one of -last_fileset or -state is required");
-        return false;
-    }
-
-    char *query = psStringCopy ("UPDATE pstampDataStore SET");
-    bool needComma = false;
-    
+    PXOPT_LOOKUP_STR(lastFileset, config->args, "-set_last_fileset",  false, false);
+    PXOPT_LOOKUP_STR(state,       config->args, "-set_state",         false, false);
+    PXOPT_LOOKUP_STR(label,       config->args, "-set_label",         false, false);
+    PXOPT_LOOKUP_S32(pollInterval, config->args, "-set_poll_interval",         false, false);
+    PXOPT_LOOKUP_BOOL(update_timestamp, config->args, "-update_timestamp", false);
+
+    if (!state && !lastFileset && !pollInterval && !update_timestamp && !label) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of -last_fileset or -set_state is required");
+        return false;
+    }
+
+    char *query = psStringCopy ("UPDATE pstampDataStore SET timestamp = CURRENT_TIMESTAMP() ");
+
     if (lastFileset) {
-        psStringAppend(&query, " lastFileset = '%s'", lastFileset);
-        needComma = true;
+        psStringAppend(&query, " , lastFileset = '%s'", lastFileset);
     }
 
     if (state) {
-        psStringAppend(&query, "%s state = '%s'", needComma ? "," : " ", state);
-        needComma = true; // be ready in case we add another field
-    }
-                
+        psStringAppend(&query, ", state = '%s'", state);
+    }
+
+    if (label) {
+        psStringAppend(&query, ", label = '%s'", label);
+    }
+
+    if (pollInterval) {
+        psStringAppend(&query, ", pollInterval = %d", pollInterval);
+    }
+
     psStringAppend(&query, " WHERE ds_id = %" PRId64, ds_id);
 
@@ -212,5 +239,5 @@
     psU64 affected = psDBAffectedRows(config->dbh);
     if (affected != 1) {
-        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %"
                                         PRIu64 " rows were modified", affected);
         return false;
@@ -220,25 +247,25 @@
 }
 
-static bool addReqMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    PXOPT_LOOKUP_STR(uri,         config->args, "-uri",           true, false);
-    // PXOPT_LOOKUP_STR(outFileset,  config->args, "-out_fileset",   true, false);
-    PXOPT_LOOKUP_S64(ds_id,       config->args, "-ds_id",         false, false);
-
-    char *query ="INSERT INTO pstampRequest"
-                     " (state, uri, ds_id, fault)"
-                     " VALUES( 'new', '%s', %" PRId64 ", 0 )";
-    if (!p_psDBRunQueryF(config->dbh, query, uri, ds_id)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    psU64 affected = psDBAffectedRows(config->dbh);
-    if (affected != 1) {
-        psError(PS_ERR_UNKNOWN, false, 
-            "should have affected one row but %" PRIu64 " rows were modified",
-            affected);
+static bool addreqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(uri,         config->args, "-uri",   true, false);
+    PXOPT_LOOKUP_STR(name,        config->args, "-name",  false, false);
+    PXOPT_LOOKUP_STR(label,       config->args, "-label",  false, false);
+    PXOPT_LOOKUP_S64(ds_id,       config->args, "-ds_id", false, false);
+
+    if (!pstampRequestInsert(config->dbh,
+        0,      // req_id
+        ds_id,
+        "new",  //state
+        name,
+        NULL,   // reqType
+        label,
+        NULL,   // outProduct
+        uri,
+        0       // fault
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "failed to insert request");
         return false;
     }
@@ -251,5 +278,5 @@
 }
 
-static bool pendingReqMode(pxConfig *config)
+static bool pendingreqMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -257,4 +284,5 @@
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "label", "LIKE");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
@@ -263,5 +291,5 @@
     psString query = pxDataGet("pstamptool_pendingreq.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;
     }
@@ -311,15 +339,28 @@
 }
 
-static bool listReqMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    PXOPT_LOOKUP_S64(req_id,  config->args, "-req_id", true, false);
+static bool listreqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-not_req_id", "req_id", "!=");
+    PXOPT_COPY_STR(config->args, where, "-name", "name", "==");
+
     PXOPT_LOOKUP_U64(limit,   config->args, "-limit",  false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
-    psString query = NULL;
-    psStringAppend(&query, "SELECT * from pstampRequest WHERE req_id = %" PRId64, req_id);
-    
+    if (!psListLength(where->list)) {
+        psError(PS_ERR_UNKNOWN, true, "-req_id or -name must be supplied");
+        return false;
+    }
+
+    psString query = psStringCopy("SELECT * from pstampRequest");
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
     // treat limit == 0 as "no limit"
     if (limit) {
@@ -342,7 +383,10 @@
     }
     if (!psArrayLength(output)) {
-        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
-        psFree(output);
-        return true;
+        psTrace("pstamptool", PS_LOG_INFO, "request not found");
+        // This causes main to exit with PS_EXIT_DATA_ERROR which the script is looking for
+        psError(PXTOOLS_ERR_CONFIG, true, "request not found");
+        psFree(output);
+        // we return false so that the caller can determine that a request does not exist
+        return false;
     }
 
@@ -359,15 +403,22 @@
 }
 
-static bool completedReqMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
+static bool completedreqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs(config, where, "-label", "label", "LIKE");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
-    psString query = 
-        "SELECT * FROM pstampRequest WHERE state = 'run' AND "
-            "(SELECT count(*) FROM pstampJob WHERE pstampJob.req_id = pstampRequest.req_id "
-            " AND pstampJob.state != 'stop') = 0" ;
+    psString query = pxDataGet("pstamptool_completedreq.sql");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
 
     // treat limit == 0 as "no limit"
@@ -408,5 +459,5 @@
 }
 
-static bool updateReqMode(pxConfig *config)
+static bool updatereqMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -415,7 +466,7 @@
     PXOPT_LOOKUP_STR(state,      config->args, "-state",      false, false);
     PXOPT_LOOKUP_STR(outProduct, config->args, "-outProduct", false, false);
-    PXOPT_LOOKUP_STR(fault, 	 config->args, "-fault",      false, false);
-    PXOPT_LOOKUP_STR(uri,   	 config->args, "-uri",        false, false);
-    PXOPT_LOOKUP_STR(name,  	 config->args, "-name",       false, false);
+    PXOPT_LOOKUP_S16(fault,      config->args, "-fault",      false, false);
+    PXOPT_LOOKUP_STR(uri,        config->args, "-uri",        false, false);
+    PXOPT_LOOKUP_STR(name,       config->args, "-name",       false, false);
     PXOPT_LOOKUP_STR(reqType,    config->args, "-reqType",    false, false);
 
@@ -433,5 +484,5 @@
     }
     if (fault) {
-        psStringAppend(&query, "%c fault = %s", c, fault);
+        psStringAppend(&query, "%c fault = %d", c, fault);
         c = ',';
     }
@@ -464,5 +515,5 @@
     // note zero is not an error
     if (affected > 1) {
-        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %"
                                         PRIu64 " rows were modified", affected);
         return false;
@@ -472,68 +523,92 @@
 }
 
-static bool revertReqMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    PXOPT_LOOKUP_S64(req_id,     config->args, "-req_id",     true, false);
-
-    // printf("Revert request %" PRId64 "\n", req_id);
-    
-    if (!p_psDBRunQueryF(config->dbh, "DELETE FROM pstampJob where req_id = %" PRId64, req_id)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    if (!p_psDBRunQueryF(config->dbh, 
-        "UPDATE pstampRequest set state ='new', name=NULL, reqType=NULL, fault=0 where req_id = %" PRId64, req_id)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    return true;
-}
-
-static bool addJobMode(pxConfig *config)
+static bool revertreqMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-fault", "fault", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "pstampRequest.state", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "pstampRequest.label", "LIKE");
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psFree(where);
+
+    // delete any jobs that were queued by requests that didn't complete parsing (pstampRequest.state = 'new'
+    // If state =  'run' was supplied this will be a no-op
+    psString query = pxDataGet("pstamptool_revertreq_deletejobs.sql");
+    psStringAppend(&query, " AND %s", whereClause);
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    // clear fault for requests
+    query = pxDataGet("pstamptool_revertreq.sql");
+    psStringAppend(&query, " AND %s", whereClause);
+
+    psFree(whereClause);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool addjobMode(pxConfig *config)
 {
     bool    stampJob = false;
-    char   *query = NULL;
-
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    PXOPT_LOOKUP_S64(uri,         config->args, "-uri",        true, false);
+
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
     PXOPT_LOOKUP_S64(req_id,      config->args, "-req_id",     true, false);
     PXOPT_LOOKUP_STR(rownum,      config->args, "-rownum",     true, false);
     PXOPT_LOOKUP_STR(job_type,    config->args, "-job_type",   false, false);
-    PXOPT_LOOKUP_STR(outputBase,  config->args, "-outputBase", true,  false);
-    PXOPT_LOOKUP_STR(argString,   config->args, "-args",       false, false);
+    PXOPT_LOOKUP_STR(outputBase,  config->args, "-outputBase", false,  false);
     PXOPT_LOOKUP_STR(stateString, config->args, "-state",      false, false);
-    PXOPT_LOOKUP_STR(fault,       config->args, "-fault",      false, false);
+    PXOPT_LOOKUP_S16(fault,       config->args, "-fault",      false, false);
     PXOPT_LOOKUP_S64(exp_id,      config->args, "-exp_id",     false, false);
+    PXOPT_LOOKUP_S64(options,     config->args, "-options",     false, false);
+    PXOPT_LOOKUP_S64(dep_id,      config->args, "-dep_id",     false, false);
+
+    // unless the job is being inserted with stop state require outputBase
+    if (strcmp(stateString, "stop") && !outputBase) {
+        psError(PS_ERR_UNKNOWN, true, "-outputBase is required");
+        return false;
+    }
 
     // default value for job_type is defined in pstamptoolConfig.c
-    if (!strcmp(job_type, "get_image") || !strcmp(job_type, "detect_query")) {
-	stampJob = false;
+    if (!strcmp(job_type, "get_image") || !strcmp(job_type, "detect_query") || !strcmp(job_type, "none")) {
+        stampJob = false;
     } else if (!strcmp(job_type, "stamp")) {
-	stampJob = true;
+        stampJob = true;
     } else {
-	psError(PS_ERR_UNKNOWN, false, "unknown value for -job_type: %s", job_type);
-	return false;
-    }
-
-    if (stampJob && !argString) {
-	psError(PS_ERR_UNKNOWN, true, "-args is required for stamp job");
-	return false;
-    }
-
-    if (stampJob) {
-	query = pxDataGet("pstamptool_addjob_stampjob.sql");
-    } else {
-	query = pxDataGet("pstamptool_addjob_otherjob.sql");
-    }
-    if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
-    }
-
-    if (!p_psDBRunQueryF(config->dbh, query, req_id, rownum, stateString, job_type, uri, exp_id, outputBase, fault, argString)) {
+        psError(PS_ERR_UNKNOWN, false, "unknown value for -job_type: %s", job_type);
+        return false;
+    }
+    if (!pstampJobInsert(config->dbh,
+            0, // job_id
+            req_id,
+            rownum,
+            stateString,
+            job_type,
+            fault,
+            exp_id,
+            outputBase,
+            options,
+            dep_id
+            )) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         return false;
@@ -542,5 +617,5 @@
     psU64 affected = psDBAffectedRows(config->dbh);
     if (affected != 1) {
-        psError(PS_ERR_UNKNOWN, false, 
+        psError(PS_ERR_UNKNOWN, false,
             "should have affected one row but %" PRIu64 " rows were modified",
             affected);
@@ -554,38 +629,34 @@
 }
 
-static bool listJobMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    PXOPT_LOOKUP_S64(req_id, config->args, "-req_id", false, false);
-    PXOPT_LOOKUP_S64(job_id, config->args, "-job_id", false, false);
+static bool listjobMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-fault",  "fault", "==");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
-    psString query = NULL;
-
-    if (!req_id && !job_id) {
-        fprintf(stderr, "either req_id or job_id must be specified\n");
+    if (!psListLength(where->list)) {
+        fprintf(stderr, "search arguments are required\n");
         exit (1);
     }
 
-    if (req_id) {
-        psStringAppend(&query,
-                "SELECT"
-                " pstampJob.*, pstampRequest.name, pstampRequest.outProduct"
-                " FROM pstampJob"
-                " JOIN pstampRequest USING(req_id)"
-                " WHERE req_id = %" PRId64, req_id
-            );
-    } else {
-        psStringAppend(&query,
-                "SELECT"
-                " pstampJob.*, pstampRequest.name, pstampRequest.outProduct"
-                " FROM pstampJob"
-                " JOIN pstampRequest USING(req_id)"
-                " WHERE job_id = %" PRId64, job_id
-            );
-    } 
+    psString query = pxDataGet("pstamptool_listjob.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, "pstampJob");
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
 
     // treat limit == 0 as "no limit"
@@ -626,5 +697,5 @@
 }
 
-static bool pendingJobMode(pxConfig *config)
+static bool pendingjobMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -634,4 +705,6 @@
     PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
     PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "label", "LIKE");
+
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
@@ -639,5 +712,5 @@
     psString query = pxDataGet("pstamptool_pendingjob.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;
     }
@@ -645,5 +718,5 @@
     // use psDBGenerateWhereSQL because the SQL yields an intermediate table
     if (psListLength(where->list)) {
-        psString whereClause = psDBGenerateWhereConditionSQL(where, "pstampJob");
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
         psStringAppend(&query, " AND %s", whereClause);
         psFree(whereClause);
@@ -688,11 +761,22 @@
 }
 
-static bool updateJobMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    PXOPT_LOOKUP_S64(job_id,    config->args, "-job_id", true, false);
+static bool updatejobMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(job_id,    config->args, "-job_id", false, false);
+    PXOPT_LOOKUP_S64(dep_id,    config->args, "-dep_id", false, false);
+
+    if (!job_id && !dep_id) {
+        psError(PS_ERR_UNKNOWN, true, "at least -job_id or -dep_id is required");
+        return false;
+    }
     PXOPT_LOOKUP_STR(state,     config->args, "-state",  true, false);
-    PXOPT_LOOKUP_STR(fault,     config->args, "-fault",  false, false);
+    PXOPT_LOOKUP_S32(fault,     config->args, "-fault",  false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-dep_id", "dep_id", "==");
 
     psString faultStr = NULL;
@@ -700,12 +784,15 @@
         faultStr = psStringCopy("");
     } else {
-        psStringAppend(&faultStr, ", fault = '%s'", fault);
-    }
-
-    char *query ="UPDATE pstampJob"
-	" SET state = '%s' %s"
-	" WHERE job_id = %" PRId64;
-    
-    if (!p_psDBRunQueryF(config->dbh, query, state, faultStr, job_id)) {
+        psStringAppend(&faultStr, "\n, pstampJob.fault = %d", fault);
+    }
+
+    psString query = pxDataGet("pstamptool_updatejob.sql");
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, state, faultStr)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
@@ -713,15 +800,57 @@
     }
     psFree(faultStr);
+    psFree(query);
 
     psU64 affected = psDBAffectedRows(config->dbh);
-    if (affected != 1) {
-        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
-                                        PRIu64 " rows were modified", affected);
-        return false;
-    }
-
-    return true;
-}
-static bool addProjectMode(pxConfig *config)
+    psLogMsg("pstamptool", PS_LOG_INFO, "Updated %" PRIu64 " pstampJobs", affected);
+
+    return true;
+}
+
+static bool revertjobMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-fault",  "pstampJob.fault", "==");
+    PXOPT_COPY_S64(config->args, where, "-req_id_min",  "req_id", ">=");
+    pxAddLabelSearchArgs(config, where, "-label", "pstampRequest.label", "LIKE");
+
+    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault",  false, false);
+
+    // By default only revert faults < 10 which are our "ipp exit codes"
+    // codes larger than that are the pstamp request interface.
+    // Don't fault those unless -fault waa provided
+    psString faultClause = "";
+    if (!fault) {
+        faultClause = " \nAND (pstampJob.fault < 10)";
+    }
+
+    psString query = pxDataGet("pstamptool_revertjob.sql");
+    if (!psListLength(where->list) && !all) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters or -all are required");
+        return false;
+    }
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, faultClause)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool addprojectMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -763,5 +892,5 @@
     psString query = pxDataGet("pstamptool_project.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;
     }
@@ -801,5 +930,5 @@
     return true;
 }
-static bool modProjectMode(pxConfig *config)
+static bool modprojectMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -809,7 +938,7 @@
 
     char *query = psStringCopy ("UPDATE pstampProject SET");
-    
+
     psStringAppend(&query, " state = '%s'", state);
-                
+
     psStringAppend(&query, " WHERE proj_id = %" PRId64, proj_id);
 
@@ -822,5 +951,5 @@
     psU64 affected = psDBAffectedRows(config->dbh);
     if (affected != 1) {
-        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %" 
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %"
                                         PRIu64 " rows were modified", affected);
         return false;
@@ -830,2 +959,257 @@
 }
 
+static bool getdependentMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(stage,       config->args, "-stage",   true, false);
+    PXOPT_LOOKUP_S64(stage_id,    config->args, "-stage_id", true, false);
+    PXOPT_LOOKUP_STR(component,   config->args, "-component",  true, false);
+    PXOPT_LOOKUP_STR(imagedb,     config->args, "-imagedb",  true, false);
+    PXOPT_LOOKUP_STR(rlabel,      config->args, "-rlabel",  true, false);
+    PXOPT_LOOKUP_BOOL(need_magic, config->args, "-need_magic", false);
+    PXOPT_LOOKUP_BOOL(no_create,  config->args, "-no_create", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
+    PXOPT_COPY_STR(config->args, where, "-imagedb", "imagedb", "==");
+    PXOPT_COPY_S64(config->args, where, "-stage_id", "stage_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-component", "component", "==");
+
+    // start a transaction eraly so it will contain any row level locks
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psString query = pxDataGet("pstamptool_getdependent.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    if (!no_create) {
+        // This will lock the row until the transaction is committed
+        psStringAppend(&query, " AND %s FOR UPDATE", whereClause);
+    } else {
+        psStringAppend(&query, " AND %s", whereClause);
+    }
+    psFree(whereClause);
+    psFree(where);
+
+    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)) {
+        psMetadata *dep = output->data[0];
+        psS64 dep_id = psMetadataLookupS64(NULL, dep, "dep_id");
+        if (!dep_id) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(output);
+            return false;
+        }
+        printf("%" PRId64 "\n", dep_id);
+        psFree(output);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return true;
+    }
+    if (no_create) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        return true;
+    }
+    // no existing dependent that matches, insert one
+    // Since we have multiple processes running jobs we have a
+    // race condition here so that's why we need to lock the table
+
+    if (!pstampDependentInsert(
+        config->dbh,
+        0,              // dep_id
+        "new",          // state
+        stage,
+        stage_id,
+        component,
+        imagedb,
+        rlabel,
+        need_magic,
+        0               // fault
+        )) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to insert pstampDependent");
+        return false;
+    }
+
+    // if we try and get this after commit zero is returned
+    psS64 dep_id = psDBLastInsertID(config->dbh);
+    if (!dep_id) {
+        psError(PS_ERR_UNKNOWN, false, "psDBLastInsertID returned NULL");
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    printf("%" PRId64 "\n", dep_id);
+
+    return true;
+}
+
+static bool pendingdependentMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
+    PXOPT_COPY_STR(config->args, where, "-imagedb", "imagedb", "==");
+    PXOPT_COPY_S64(config->args, where, "-stage_id", "stage_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-rlabel", "rlabel", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "pstampRequest.label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("pstamptool_pendingdependent.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %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_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)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampDependent", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool updatedependentMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(dep_id,    config->args, "-dep_id", true, false);
+    PXOPT_LOOKUP_STR(state,     config->args, "-set_state",  false, false);
+    PXOPT_LOOKUP_S16(fault,     config->args, "-fault",  false, false);
+
+    if (!fault && !state) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of -set_state or fault is required");
+        return false;
+    }
+    psString query = psStringCopy("UPDATE pstampDependent SET");
+    bool needComma = false;
+    if (state) {
+        psStringAppend(&query, " state = '%s'", state);
+        needComma = true;
+    }
+    if (fault) {
+        psStringAppend(&query, "%s fault = %d", needComma ? ", " : "", fault);
+        needComma = true;
+    }
+    psStringAppend(&query, " WHERE dep_id = %" PRId64, dep_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected one row but %"
+                                        PRIu64 " rows were modified", affected);
+        return false;
+    }
+
+    return true;
+}
+static bool revertdependentMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-fault", "pstampDependent.fault", "==");
+    PXOPT_COPY_S64(config->args, where, "-dep_id", "dep_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "pstampRequest.label", "==");
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+    psString query = pxDataGet("pstamptool_revertdependent.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    return true;
+}
