Index: /trunk/ippTools/src/pztool.c
===================================================================
--- /trunk/ippTools/src/pztool.c	(revision 20171)
+++ /trunk/ippTools/src/pztool.c	(revision 20172)
@@ -46,5 +46,5 @@
 static bool advanceMode(pxConfig *config);
 
-static bool copydoneCompleteExp(pxConfig *config, const char *exp_name, const char *camera, const char *telescope);
+static bool copydoneCompleteExp(pxConfig *config, const char *exp_name, const char *camera, const char *telescope, bool rowLock);
 static psArray *pzGetPendingCameras(pxConfig *config);
 static psArray *pzArrayZip(psArray *arraySet, psS64 limit);
@@ -397,4 +397,5 @@
     PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
     PXOPT_LOOKUP_STR(uri, config->args, "-uri", true, false);
+    PXOPT_LOOKUP_BOOL(row_lock, config->args, "-row_lock", false);
 
     // default values
@@ -423,5 +424,5 @@
     }
 
-    if (!copydoneCompleteExp(config, exp_name, camera, telescope)) {
+    if (!copydoneCompleteExp(config, exp_name, camera, telescope, row_lock)) {
         // rollback
         if (!psDBRollback(config->dbh)) {
@@ -445,5 +446,5 @@
 }
 
-static bool copydoneCompleteExp(pxConfig *config, const char *exp_name, const char *camera, const char *telescope)
+static bool copydoneCompleteExp(pxConfig *config, const char *exp_name, const char *camera, const char *telescope, bool row_lock)
 {
     // THIS FUNCTION MUST BE INVOKED FROM INSIDE A TRANSACTION!!!
@@ -463,4 +464,5 @@
     PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
 
+
     // find all exposures that have had all of their imfiles downloaded but do
     // not appear in newExp
@@ -498,11 +500,34 @@
     }
 
+    // query to get an excluse lock on all rows for this exposure in
+    // pzDownloadImfile
+    psString lock_query = NULL;
+    if (row_lock) {
+        lock_query = psStringCopy("SELECT * FROM pzDownloadImfile");
+    }
+
     if (psListLength(where->list)) {
         psString whereClause = psDBGenerateWhereSQL(where, NULL);
         psStringAppend(&query, " %s", whereClause);
+        if (row_lock) {
+            psStringAppend(&lock_query, " %s FOR UPDATE", whereClause);
+        }
         psFree(whereClause);
     }
     psFree(where);
 
+    // aquire lock on pzDownloadImfile rows
+    // lock persists until a transaction is committed
+    if (row_lock) {
+        if (!p_psDBRunQuery(config->dbh, lock_query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(lock_query);
+            psFree(query);
+            return false;
+        }
+        psFree(lock_query);
+    }
+
+    // find completed exps
     if (!p_psDBRunQuery(config->dbh, query)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
@@ -855,5 +880,5 @@
     }
 
-    if (!copydoneCompleteExp(config, NULL, NULL, NULL)) {
+    if (!copydoneCompleteExp(config, NULL, NULL, NULL, true)) {
         // rollback
         if (!psDBRollback(config->dbh)) {
Index: /trunk/ippTools/src/pztoolConfig.c
===================================================================
--- /trunk/ippTools/src/pztoolConfig.c	(revision 20171)
+++ /trunk/ippTools/src/pztoolConfig.c	(revision 20172)
@@ -95,4 +95,5 @@
     psMetadataAddStr(copydoneArgs, PS_LIST_TAIL, "-label",  0,        "define the label for the chip stage", NULL);
     psMetadataAddS16(copydoneArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddBool(copydoneArgs, PS_LIST_TAIL, "-row_lock", 0,     "lock pzDownImfile rows while advancing an exposure", false);
 
     // -copied
