Index: trunk/ippTools/src/chiptool.c
===================================================================
--- trunk/ippTools/src/chiptool.c	(revision 8661)
+++ trunk/ippTools/src/chiptool.c	(revision 8667)
@@ -12,6 +12,8 @@
 static bool pendingimfileMode(pxConfig *config);
 static bool addprocessedimfileMode(pxConfig *config);
-static p2ProcessedImfileRow *p2pendingToProcessedImfile(pxConfig *config, p2PendingImfileRow *imfile);
+static p2ProcessedImfileRow *p2PendingToProcessedImfile(pxConfig *config, p2PendingImfileRow *imfile);
 static p2ProcessedExpRow *p2PendingToProcessedExp(pxConfig *config, p2PendingExpRow *pendingExp);
+static p3PendingExpRow *p2PendingToP3PendingExp(pxConfig *config, p2PendingExpRow *pendingExp);
+static bool p2ProcessedCompleteExp(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -227,5 +229,5 @@
         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "p2PendingImfile");
         psFree(where)
-        if (whereClaus) {
+        if (whereClause) {
             psStringAppend(&query, " AND %s", whereClause);
             psFree(whereClause);
@@ -247,22 +249,20 @@
     }
 
-    // find pending
-    psArray *pendingImfiles = p2searchPendingImfiles(config);
-    if (!pendingImfiles) {
-        psError(PS_ERR_UNKNOWN, false, "no p2PendingImfiles found");
-        return false;
-    }
-
     // start a transaction so we don't end up with an incremented iteration
     // count but no detInputExps
     if (!psDBTransaction(config->dbh)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(pendingImfiles);
-        return false;
-    }
-
-    // insert into done
-    for (long i = 0; i < psArrayLength(pendingImfiles); i++) {
-        p2ProcessedImfileRow *imfile = p2pendingToProcessedImfile(config, pendingImfiles->data[0]);
+        psFree(output);
+        return false;
+    }
+
+    // insert into p2ProcessedImfile
+    // remove p2PendingImfile entry
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+        // convert metadata into a p2PendingImfile object
+        p2PendingImfileRow *object = p2PendingImfileObjectFromMetadata(row);
+        // convert p2PendingImfile object into a p2ProcessedImfile object
+        p2ProcessedImfileRow *imfile = p2PendingToProcessedImfile(config, object);
         if (!imfile) {
             // rollback
@@ -271,7 +271,9 @@
             }
             psError(PS_ERR_UNKNOWN, false, "failed to convert p2PendingImfile to p2ProcessedImfile");
-            psFree(pendingImfiles);
-            return false;
-        }
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        // insert p2ProccessedImfile object into the database
         if (!p2ProcessedImfileInsertObject(config->dbh, imfile)) {
             // rollback
@@ -279,24 +281,27 @@
                 psError(PS_ERR_UNKNOWN, false, "database error");
             }
-            psError(PS_ERR_UNKNOWN, false, "dbh access failed");
+            psError(PS_ERR_UNKNOWN, false, "database error");
             psFree(imfile);
-            psFree(pendingImfiles);
+            psFree(object);
+            psFree(output);
             return false;
         }
         psFree(imfile);
-    }
-
-    // delete from pending
-    if (p2PendingImfileDeleteRowObjects(config->dbh, pendingImfiles, MAX_ROWS) < 0) {
-        // there must be atleast 1 Imfile to get this far
-        // rollback
-        if (!psDBRollback(config->dbh)) {
+        // delete the p2PendingImfile object from the database
+        if (!p2PendingImfileDeleteObject(config->dbh, object)) {
+            // there must be atleast 1 Imfile to get this far
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             psError(PS_ERR_UNKNOWN, false, "database error");
-        }
-        psError(PS_ERR_UNKNOWN, false, "dbh access failed");
-        psFree(pendingImfiles);
-        return false;
-    }
-    psFree(pendingImfiles);
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        psFree(object);
+    }
+
+    psFree(output);
 
     // XXX I've decided to make the transaction cover the Exp migration as
@@ -305,65 +310,12 @@
     // migration can't happen.
 
-    // look for pending exposures
-    psArray *pendingExps = p2searchPendingExp(config);
-    if (!pendingExps) {
+    if (!p2ProcessedCompleteExp(config)) {
         // rollback
         if (!psDBRollback(config->dbh)) {
             psError(PS_ERR_UNKNOWN, false, "database error");
         }
-        psError(PS_ERR_UNKNOWN, false, "no p2PendingExps found");
-        return false;
-    }
-
-    for (long i = 0; i < pendingExps->n; i++) {
-        p2PendingExpRow *pendingExp = pendingExps->data[i];
-
-        psMetadata *where = psMetadataAlloc();
-        psMetadataAddStr(where, PS_LIST_TAIL, "exp_id", 0, "==", pendingExp->exp_id);
-        psArray *pendingImfiles = p2PendingImfileSelectRowObjects(config->dbh, where, MAX_ROWS);
-        psFree(where)
-        if (!pendingImfiles) {
-            // exp has no coresponding imfiles
-            psMetadata *where = psMetadataAlloc();
-            psMetadataAddStr(where, PS_LIST_TAIL, "exp_id", 0, "==", pendingExp->exp_id);
-            long long count = p2PendingExpDelete(config->dbh, where, MAX_ROWS);
-            psFree(where);
-            if (count != 0) {
-                // rollback
-                if (!psDBRollback(config->dbh)) {
-                    psError(PS_ERR_UNKNOWN, false, "database error");
-                }
-                psError(PS_ERR_UNKNOWN, false, "dbh access failed");
-                psFree(pendingExps);
-                return false;
-            }
-
-            p2ProcessedExpRow *doneExp = p2PendingToProcessedExp(config, pendingExp);
-            if (!doneExp) {
-                // rollback
-                if (!psDBRollback(config->dbh)) {
-                    psError(PS_ERR_UNKNOWN, false, "database error");
-                }
-                psError(PS_ERR_UNKNOWN, false, "p2PendingExp -> p2ProcessedExp failed");
-                psFree(pendingExps);
-                return false;
-            }
-            if (!p2ProcessedExpInsertObject(config->dbh, doneExp)) {
-                // rollback
-                if (!psDBRollback(config->dbh)) {
-                    psError(PS_ERR_UNKNOWN, false, "database error");
-                }
-                psError(PS_ERR_UNKNOWN, false, "dbh access failed");
-                psFree(doneExp);
-                psFree(pendingExps);
-                return false;
-            }
-            psFree(doneExp);
-        }
-        // XXX skip phase 3 for the time being
-        psFree(pendingImfiles);
-    }
-
-    psFree(pendingExps);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
 
     // point of no return for p2PendingImfile -> p2ProcessedImfile 
@@ -373,21 +325,122 @@
         return false;
     }
-    /*
-    psArray *doneFrames = p2pendingToDone(config, pendingFrames);
-    if (!doneFrames) {
-        psError(PS_ERR_UNKNOWN, false, "p2pendingToDone() failed");
-        return false;
-    }
-    bool status = p2insertDoneFrames(config, doneFrames);
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, false, "p2insertDoneFrames() failed");
-        return false;
-    }
-    */
 
     return true;
 }
 
-static p2ProcessedImfileRow *p2pendingToProcessedImfile(pxConfig *config, p2PendingImfileRow *imfile)
+static bool p2ProcessedCompleteExp(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // look for completed p2PendingExp 
+    // migrate them to p2ProccessedExp & p3PendingExp
+
+    // select * from p2PendingExp
+    // where exp_id is not in p2ProcessedExp 
+    // where exp_id is not in p2PendingImfile
+    // where the number of entries in p2ProccessedImfile matches the .imfiles
+    // entry in rawScienceExp
+    psString query = psStringCopy(
+        "SELECT DISTINCT"
+        "   p2PendingExp.*,"
+        "   rawScienceExp.imfiles,"
+        "   p2ProcessedImfile.class_id"
+        " FROM p2PendingExp"
+        " JOIN rawScienceExp"
+        "   ON p2PendingExp.exp_id = rawScienceExp.exp_id"
+        " LEFT JOIN p2ProcessedExp"
+        "   ON p2PendingExp.exp_id = p2ProcessedExp.exp_id"
+        " LEFT JOIN p2PendingImfile"
+        "   ON p2PendingExp.exp_id = p2PendingImfile.exp_id"
+        " LEFT JOIN p2ProcessedImfile"
+        "   ON p2PendingExp.exp_id = p2ProcessedImfile.exp_id"
+        " WHERE"
+        "  p2ProcessedExp.exp_id IS NULL"
+        "  AND p2PendingImfile.exp_id IS NULL"
+        "  AND p2ProcessedImfile.exp_id IS NOT NULL"
+        " GROUP BY"
+        "   p2PendingExp.exp_id"
+        " HAVING rawScienceExp.imfiles = COUNT(p2ProcessedImfile.class_id)"
+        );
+
+    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) {
+        // XXX check psError here and decide if we need to rollback or commit
+        psError(PS_ERR_UNKNOWN, false, "no p2PendingExp rows found");
+        // XXX temporarily returning true here as typically this wouldn't be a
+        // fatal error.  false should be returned once psError() is fixed
+        //return false;
+        return true;
+    }
+
+    // insert into p2ProcessedExp
+    // insert into p3PendingExp
+    // remove p2PendingExp entry
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+        // convert metadata into a p2PendingExp object
+        p2PendingExpRow *object = p2PendingExpObjectFromMetadata(row);
+        // do both *Exp type conversion first so we don't insert one and then
+        // have to rollback the change as the 2nd failed
+        // convert p2PendingExp object into a p2ProcesseExp object
+        p2ProcessedExpRow *processedExp = p2PendingToProcessedExp(config, object);
+        if (!processedExp) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert p2PendingExp to p2ProcessedExp");
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        // convert p2PendingExp object into a p3PendingExp object
+        p3PendingExpRow *pendingExp = p2PendingToP3PendingExp(config, object);
+        if (!processedExp) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert p2PendingExp to p3PendingExp");
+            psFree(processedExp);
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        // insert p2ProccessedExp object into the database
+        if (!p2ProcessedExpInsertObject(config->dbh, processedExp)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(processedExp);
+            psFree(pendingExp);
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        psFree(processedExp);
+        // insert p3PendingExp object into the database
+        if (!p3PendingExpInsertObject(config->dbh, pendingExp)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(pendingExp);
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        psFree(pendingExp);
+        // delete the p2PendingExp object from the database
+        if (!p2PendingExpDeleteObject(config->dbh, object)) {
+            // there must be atleast 1 Imfile to get this far
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(object);
+            psFree(output);
+            return false;
+        }
+        psFree(object);
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static p2ProcessedImfileRow *p2PendingToProcessedImfile(pxConfig *config, p2PendingImfileRow *imfile)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
@@ -479,7 +532,17 @@
     return p2ProcessedExpRowAlloc(
         pendingExp->exp_id,
-        "my recipe",
         pendingExp->p1_version,
         pendingExp->p2_version
     );
 }
+
+static p3PendingExpRow *p2PendingToP3PendingExp(pxConfig *config, p2PendingExpRow *pendingExp)
+{
+    PS_ASSERT_PTR_NON_NULL(pendingExp, NULL);
+
+    return p3PendingExpRowAlloc(
+        pendingExp->exp_id,
+        pendingExp->p1_version,
+        pendingExp->p2_version
+    );
+}
