Index: trunk/ippTools/src/chiptool.c
===================================================================
--- trunk/ippTools/src/chiptool.c	(revision 21402)
+++ trunk/ippTools/src/chiptool.c	(revision 21463)
@@ -42,4 +42,5 @@
 static bool revertprocessedimfileMode(pxConfig *config);
 static bool updateprocessedimfileMode(pxConfig *config);
+static bool promoteexpMode(pxConfig *config);
 static bool blockMode(pxConfig *config);
 static bool maskedMode(pxConfig *config);
@@ -54,6 +55,4 @@
 static bool topurgedimfileMode(pxConfig *config);
 
-static bool chipProcessedCompleteExp(pxConfig *config);
-
 # define MODECASE(caseName, func) \
     case caseName: \
@@ -80,4 +79,5 @@
         MODECASE(CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
         MODECASE(CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
+        MODECASE(CHIPTOOL_MODE_PROMOTEEXP,              promoteexpMode);
         MODECASE(CHIPTOOL_MODE_BLOCK,                   blockMode);
         MODECASE(CHIPTOOL_MODE_MASKED,                  maskedMode);
@@ -495,4 +495,5 @@
     }
 
+#if 0
     // XXX I've decided to make the transaction cover the Exp migration as
     // well.  Otherwise, if the last imfile in an exp is moved and the exp
@@ -508,4 +509,5 @@
         return false;
     }
+#endif
 
     if (!psDBCommit(config->dbh)) {
@@ -1074,7 +1076,12 @@
 
 
-static bool chipProcessedCompleteExp(pxConfig *config)
+static bool promoteexpMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
 
     // look for completed chipPendingExp
@@ -1086,4 +1093,18 @@
     }
 
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %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");
@@ -1108,4 +1129,9 @@
 
         chipRunRow *chipRun = chipRunObjectFromMetadata(row);
+        if (!psDBTransaction(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
         // set chipRun.state to 'stop'
         if (!pxchipRunSetState(config, chipRun->chip_id, "full")) {
@@ -1119,4 +1145,9 @@
         // NULL for end_stage means go as far as possible
         if (chipRun->end_stage && psStrcasestr(chipRun->end_stage, "chip")) {
+            if (!psDBCommit(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                return false;
+            }
+
             psFree(chipRun);
             continue;
@@ -1136,6 +1167,5 @@
                     chipRun->end_stage
         )) {
-            // rollback
-            if (!psDBRollback(config->dbh)) {
+           if (!psDBRollback(config->dbh)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
             }
@@ -1145,4 +1175,8 @@
             return false;
         }
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
         psFree(chipRun);
     }
