Index: trunk/ippdb/src/ippdb.c
===================================================================
--- trunk/ippdb/src/ippdb.c	(revision 9970)
+++ trunk/ippdb/src/ippdb.c	(revision 10060)
@@ -32,6 +32,9 @@
 
 #define SUMMITEXP_TABLE_NAME "summitExp"
+#define SUMMITIMFILE_TABLE_NAME "summitImfile"
 #define PZPENDINGEXP_TABLE_NAME "pzPendingExp"
 #define PZPENDINGIMFILE_TABLE_NAME "pzPendingImfile"
+#define PZDONEEXP_TABLE_NAME "pzDoneExp"
+#define PZDONEIMFILE_TABLE_NAME "pzDoneImfile"
 #define NEWEXP_TABLE_NAME "newExp"
 #define NEWIMFILE_TABLE_NAME "newImfile"
@@ -234,5 +237,5 @@
 static void summitExpRowFree(summitExpRow *object);
 
-summitExpRow *summitExpRowAlloc(const char *exp_id, const char *camera, const char *telescope, psTime* time, const char *exp_type, const char *uri)
+summitExpRow *summitExpRowAlloc(const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, const char *uri, psS32 imfiles)
 {
     summitExpRow    *_object;
@@ -244,7 +247,8 @@
     _object->camera = psStringCopy(camera);
     _object->telescope = psStringCopy(telescope);
-    _object->time = psTimeCopy(time);
+    _object->dateobs = psTimeCopy(dateobs);
     _object->exp_type = psStringCopy(exp_type);
     _object->uri = psStringCopy(uri);
+    _object->imfiles = imfiles;
 
     return _object;
@@ -256,5 +260,5 @@
     psFree(object->camera);
     psFree(object->telescope);
-    psFree(object->time);
+    psFree(object->dateobs);
     psFree(object->exp_type);
     psFree(object->uri);
@@ -279,12 +283,12 @@
         return false;
     }
-    psTime* time = psTimeFromISO("", PS_TIME_UTC);
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "time", PS_DATA_TIME, NULL, time)) {
-        psFree(time);
-        psError(PS_ERR_UNKNOWN, false, "failed to add item time");
-        psFree(md);
-        return false;
-    }
-    psFree(time);
+    psTime* dateobs = psTimeFromISO("", PS_TIME_UTC);
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psFree(dateobs);
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    psFree(dateobs);
     if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
@@ -297,4 +301,9 @@
         return false;
     }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
 
     bool status = psDBCreateTable(dbh, SUMMITEXP_TABLE_NAME, md);
@@ -310,5 +319,5 @@
 }
 
-bool summitExpInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, psTime* time, const char *exp_type, const char *uri)
+bool summitExpInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, const char *uri, psS32 imfiles)
 {
     psMetadata *md = psMetadataAlloc();
@@ -328,6 +337,6 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "time", PS_DATA_TIME, NULL, time)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item time");
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
         psFree(md);
         return false;
@@ -340,4 +349,9 @@
     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, imfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
         psFree(md);
         return false;
@@ -366,5 +380,5 @@
 bool summitExpInsertObject(psDB *dbh, summitExpRow *object)
 {
-    return summitExpInsert(dbh, object->exp_id, object->camera, object->telescope, object->time, object->exp_type, object->uri);
+    return summitExpInsert(dbh, object->exp_id, object->camera, object->telescope, object->dateobs, object->exp_type, object->uri, object->imfiles);
 }
 
@@ -454,6 +468,6 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "time", PS_DATA_TIME, NULL, object->time)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item time");
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, object->dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
         psFree(md);
         return false;
@@ -469,4 +483,9 @@
         return false;
     }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, object->imfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
 
 
@@ -493,7 +512,7 @@
         return false;
     }
-    psTime* time = psMetadataLookupPtr(&status, md, "time");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item time");
+    psTime* dateobs = psMetadataLookupPtr(&status, md, "dateobs");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dateobs");
         return false;
     }
@@ -508,6 +527,11 @@
         return false;
     }
-
-    return summitExpRowAlloc(exp_id, camera, telescope, time, exp_type, uri);
+    psS32 imfiles = psMetadataLookupS32(&status, md, "imfiles");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item imfiles");
+        return false;
+    }
+
+    return summitExpRowAlloc(exp_id, camera, telescope, dateobs, exp_type, uri, imfiles);
 }
 psArray *summitExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
@@ -604,31 +628,37 @@
     return true;
 }
-static void pzPendingExpRowFree(pzPendingExpRow *object);
-
-pzPendingExpRow *pzPendingExpRowAlloc(const char *exp_id, const char *camera, const char *telescope, const char *exp_type, psS32 imfiles)
-{
-    pzPendingExpRow *_object;
-
-    _object = psAlloc(sizeof(pzPendingExpRow));
-    psMemSetDeallocator(_object, (psFreeFunc)pzPendingExpRowFree);
+static void summitImfileRowFree(summitImfileRow *object);
+
+summitImfileRow *summitImfileRowAlloc(const char *exp_id, const char *camera, const char *telescope, psS32 bytes, const char *md5sum, const char *class, const char *class_id, const char *uri)
+{
+    summitImfileRow *_object;
+
+    _object = psAlloc(sizeof(summitImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)summitImfileRowFree);
 
     _object->exp_id = psStringCopy(exp_id);
     _object->camera = psStringCopy(camera);
     _object->telescope = psStringCopy(telescope);
-    _object->exp_type = psStringCopy(exp_type);
-    _object->imfiles = imfiles;
+    _object->bytes = bytes;
+    _object->md5sum = psStringCopy(md5sum);
+    _object->class = psStringCopy(class);
+    _object->class_id = psStringCopy(class_id);
+    _object->uri = psStringCopy(uri);
 
     return _object;
 }
 
-static void pzPendingExpRowFree(pzPendingExpRow *object)
+static void summitImfileRowFree(summitImfileRow *object)
 {
     psFree(object->exp_id);
     psFree(object->camera);
     psFree(object->telescope);
-    psFree(object->exp_type);
-}
-
-bool pzPendingExpCreateTable(psDB *dbh)
+    psFree(object->md5sum);
+    psFree(object->class);
+    psFree(object->class_id);
+    psFree(object->uri);
+}
+
+bool summitImfileCreateTable(psDB *dbh)
 {
     psMetadata *md = psMetadataAlloc();
@@ -648,16 +678,31 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, PZPENDINGEXP_TABLE_NAME, md);
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, "32")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, SUMMITIMFILE_TABLE_NAME, md);
 
     psFree(md);
@@ -666,10 +711,10 @@
 }
 
-bool pzPendingExpDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, PZPENDINGEXP_TABLE_NAME);
-}
-
-bool pzPendingExpInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, const char *exp_type, psS32 imfiles)
+bool summitImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, SUMMITIMFILE_TABLE_NAME);
+}
+
+bool summitImfileInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, psS32 bytes, const char *md5sum, const char *class, const char *class_id, const char *uri)
 {
     psMetadata *md = psMetadataAlloc();
@@ -689,16 +734,31 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, imfiles)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, PZPENDINGEXP_TABLE_NAME, md);
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, bytes)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, md5sum)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, SUMMITIMFILE_TABLE_NAME, md);
     psFree(md);
 
@@ -706,11 +766,11 @@
 }
 
-long long pzPendingExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+long long summitImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
 {
     long long       deleted = 0;
 
-    long long count = psDBDeleteRows(dbh, PZPENDINGEXP_TABLE_NAME, where, limit);
+    long long count = psDBDeleteRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
     if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzPendingExp");
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitImfile");
         return count;
 
@@ -720,13 +780,13 @@
     return deleted;
 }
-bool pzPendingExpInsertObject(psDB *dbh, pzPendingExpRow *object)
-{
-    return pzPendingExpInsert(dbh, object->exp_id, object->camera, object->telescope, object->exp_type, object->imfiles);
-}
-
-bool pzPendingExpInsertObjects(psDB *dbh, psArray *objects)
+bool summitImfileInsertObject(psDB *dbh, summitImfileRow *object)
+{
+    return summitImfileInsert(dbh, object->exp_id, object->camera, object->telescope, object->bytes, object->md5sum, object->class, object->class_id, object->uri);
+}
+
+bool summitImfileInsertObjects(psDB *dbh, psArray *objects)
 {
     for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!pzPendingExpInsertObject(dbh, objects->data[i])) {
+        if (!summitImfileInsertObject(dbh, objects->data[i])) {
             return false;
         }
@@ -736,11 +796,11 @@
 }
 
-bool pzPendingExpInsertFits(psDB *dbh, const psFits *fits)
+bool summitImfileInsertFits(psDB *dbh, const psFits *fits)
 {
     psArray         *rowSet;
 
-    // move to (the first?) extension named  PZPENDINGEXP_TABLE_NAME
-    if (!psFitsMoveExtName(fits, PZPENDINGEXP_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZPENDINGEXP_TABLE_NAME);
+    // move to (the first?) extension named  SUMMITIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, SUMMITIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", SUMMITIMFILE_TABLE_NAME);
         return false;
     }
@@ -760,4 +820,357 @@
     }
 
+    if (!psDBInsertRows(dbh, SUMMITIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool summitImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, SUMMITIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *summitImfileMetadataFromObject(const summitImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, object->bytes)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, object->md5sum)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, object->class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+summitImfileRow *summitImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_id = psMetadataLookupPtr(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    psS32 bytes = psMetadataLookupS32(&status, md, "bytes");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bytes");
+        return false;
+    }
+    char* md5sum = psMetadataLookupPtr(&status, md, "md5sum");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item md5sum");
+        return false;
+    }
+    char* class = psMetadataLookupPtr(&status, md, "class");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+
+    return summitImfileRowAlloc(exp_id, camera, telescope, bytes, md5sum, class, class_id, uri);
+}
+psArray *summitImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        summitImfileRow *object = summitImfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool summitImfileDeleteObject(psDB *dbh, const summitImfileRow *object)
+{
+    psMetadata *where = summitImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, SUMMITIMFILE_TABLE_NAME, where, 0);
+    psFree(where)
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "summitImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long summitImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        summitImfileRow *object = objects->data[i];
+        psMetadata *where = summitImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, SUMMITIMFILE_TABLE_NAME, where, limit);
+        psFree(where)
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from summitImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool summitImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = summitImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            SUMMITIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+static void pzPendingExpRowFree(pzPendingExpRow *object);
+
+pzPendingExpRow *pzPendingExpRowAlloc(const char *exp_id, const char *camera, const char *telescope)
+{
+    pzPendingExpRow *_object;
+
+    _object = psAlloc(sizeof(pzPendingExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pzPendingExpRowFree);
+
+    _object->exp_id = psStringCopy(exp_id);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+
+    return _object;
+}
+
+static void pzPendingExpRowFree(pzPendingExpRow *object)
+{
+    psFree(object->exp_id);
+    psFree(object->camera);
+    psFree(object->telescope);
+}
+
+bool pzPendingExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PZPENDINGEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pzPendingExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PZPENDINGEXP_TABLE_NAME);
+}
+
+bool pzPendingExpInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PZPENDINGEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pzPendingExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PZPENDINGEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzPendingExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzPendingExpInsertObject(psDB *dbh, pzPendingExpRow *object)
+{
+    return pzPendingExpInsert(dbh, object->exp_id, object->camera, object->telescope);
+}
+
+bool pzPendingExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pzPendingExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pzPendingExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PZPENDINGEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PZPENDINGEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZPENDINGEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
     if (!psDBInsertRows(dbh, PZPENDINGEXP_TABLE_NAME, rowSet)) {
         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
@@ -810,14 +1223,4 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, object->exp_type)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, object->imfiles)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
-        psFree(md);
-        return false;
-    }
 
 
@@ -844,16 +1247,6 @@
         return false;
     }
-    char* exp_type = psMetadataLookupPtr(&status, md, "exp_type");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_type");
-        return false;
-    }
-    psS32 imfiles = psMetadataLookupS32(&status, md, "imfiles");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item imfiles");
-        return false;
-    }
-
-    return pzPendingExpRowAlloc(exp_id, camera, telescope, exp_type, imfiles);
+
+    return pzPendingExpRowAlloc(exp_id, camera, telescope);
 }
 psArray *pzPendingExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
@@ -952,5 +1345,5 @@
 static void pzPendingImfileRowFree(pzPendingImfileRow *object);
 
-pzPendingImfileRow *pzPendingImfileRowAlloc(const char *exp_id, const char *camera, const char *telescope, psS32 bytes, const char *md5sum, const char *class, const char *class_id, const char *uri)
+pzPendingImfileRow *pzPendingImfileRowAlloc(const char *exp_id, const char *camera, const char *telescope, const char *class, const char *class_id, const char *exp_tag)
 {
     pzPendingImfileRow *_object;
@@ -962,9 +1355,7 @@
     _object->camera = psStringCopy(camera);
     _object->telescope = psStringCopy(telescope);
-    _object->bytes = bytes;
-    _object->md5sum = psStringCopy(md5sum);
     _object->class = psStringCopy(class);
     _object->class_id = psStringCopy(class_id);
-    _object->uri = psStringCopy(uri);
+    _object->exp_tag = psStringCopy(exp_tag);
 
     return _object;
@@ -976,8 +1367,7 @@
     psFree(object->camera);
     psFree(object->telescope);
-    psFree(object->md5sum);
     psFree(object->class);
     psFree(object->class_id);
-    psFree(object->uri);
+    psFree(object->exp_tag);
 }
 
@@ -1000,15 +1390,5 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, "32")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, "64")) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, "Primary Key", "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item class");
         psFree(md);
@@ -1020,6 +1400,6 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, "Unique Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
         return false;
@@ -1038,5 +1418,5 @@
 }
 
-bool pzPendingImfileInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, psS32 bytes, const char *md5sum, const char *class, const char *class_id, const char *uri)
+bool pzPendingImfileInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, const char *class, const char *class_id, const char *exp_tag)
 {
     psMetadata *md = psMetadataAlloc();
@@ -1056,14 +1436,4 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, bytes)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, md5sum)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
-        psFree(md);
-        return false;
-    }
     if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, class)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item class");
@@ -1076,6 +1446,6 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
         return false;
@@ -1104,5 +1474,5 @@
 bool pzPendingImfileInsertObject(psDB *dbh, pzPendingImfileRow *object)
 {
-    return pzPendingImfileInsert(dbh, object->exp_id, object->camera, object->telescope, object->bytes, object->md5sum, object->class, object->class_id, object->uri);
+    return pzPendingImfileInsert(dbh, object->exp_id, object->camera, object->telescope, object->class, object->class_id, object->exp_tag);
 }
 
@@ -1192,14 +1562,4 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bytes", PS_DATA_S32, NULL, object->bytes)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bytes");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "md5sum", PS_DATA_STRING, NULL, object->md5sum)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item md5sum");
-        psFree(md);
-        return false;
-    }
     if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, object->class)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item class");
@@ -1212,6 +1572,6 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, object->exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
         return false;
@@ -1241,14 +1601,4 @@
         return false;
     }
-    psS32 bytes = psMetadataLookupS32(&status, md, "bytes");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bytes");
-        return false;
-    }
-    char* md5sum = psMetadataLookupPtr(&status, md, "md5sum");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item md5sum");
-        return false;
-    }
     char* class = psMetadataLookupPtr(&status, md, "class");
     if (!status) {
@@ -1261,11 +1611,11 @@
         return false;
     }
-    char* uri = psMetadataLookupPtr(&status, md, "uri");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
-        return false;
-    }
-
-    return pzPendingImfileRowAlloc(exp_id, camera, telescope, bytes, md5sum, class, class_id, uri);
+    char* exp_tag = psMetadataLookupPtr(&status, md, "exp_tag");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_tag");
+        return false;
+    }
+
+    return pzPendingImfileRowAlloc(exp_id, camera, telescope, class, class_id, exp_tag);
 }
 psArray *pzPendingImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
@@ -1362,67 +1712,47 @@
     return true;
 }
-static void newExpRowFree(newExpRow *object);
-
-newExpRow *newExpRowAlloc(const char *exp_tag, const char *exp_id, const char *camera, const char *telescope, const char *exp_type, psS32 imfiles)
-{
-    newExpRow       *_object;
-
-    _object = psAlloc(sizeof(newExpRow));
-    psMemSetDeallocator(_object, (psFreeFunc)newExpRowFree);
-
-    _object->exp_tag = psStringCopy(exp_tag);
+static void pzDoneExpRowFree(pzDoneExpRow *object);
+
+pzDoneExpRow *pzDoneExpRowAlloc(const char *exp_id, const char *camera, const char *telescope)
+{
+    pzDoneExpRow    *_object;
+
+    _object = psAlloc(sizeof(pzDoneExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pzDoneExpRowFree);
+
     _object->exp_id = psStringCopy(exp_id);
     _object->camera = psStringCopy(camera);
     _object->telescope = psStringCopy(telescope);
-    _object->exp_type = psStringCopy(exp_type);
-    _object->imfiles = imfiles;
 
     return _object;
 }
 
-static void newExpRowFree(newExpRow *object)
-{
-    psFree(object->exp_tag);
+static void pzDoneExpRowFree(pzDoneExpRow *object)
+{
     psFree(object->exp_id);
     psFree(object->camera);
     psFree(object->telescope);
-    psFree(object->exp_type);
-}
-
-bool newExpCreateTable(psDB *dbh)
+}
+
+bool pzDoneExpCreateTable(psDB *dbh)
 {
     psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, "Key", "64")) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, "Primary Key", "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
         psFree(md);
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, "64")) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
         psFree(md);
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, "64")) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
         psFree(md);
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, NEWEXP_TABLE_NAME, md);
+
+    bool status = psDBCreateTable(dbh, PZDONEEXP_TABLE_NAME, md);
 
     psFree(md);
@@ -1431,17 +1761,12 @@
 }
 
-bool newExpDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, NEWEXP_TABLE_NAME);
-}
-
-bool newExpInsert(psDB * dbh, const char *exp_tag, const char *exp_id, const char *camera, const char *telescope, const char *exp_type, psS32 imfiles)
+bool pzDoneExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PZDONEEXP_TABLE_NAME);
+}
+
+bool pzDoneExpInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope)
 {
     psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, exp_tag)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
-        psFree(md);
-        return false;
-    }
     if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, exp_id)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
@@ -1459,16 +1784,6 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, imfiles)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, NEWEXP_TABLE_NAME, md);
+
+    bool status = psDBInsertOneRow(dbh, PZDONEEXP_TABLE_NAME, md);
     psFree(md);
 
@@ -1476,11 +1791,11 @@
 }
 
-long long newExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+long long pzDoneExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
 {
     long long       deleted = 0;
 
-    long long count = psDBDeleteRows(dbh, NEWEXP_TABLE_NAME, where, limit);
+    long long count = psDBDeleteRows(dbh, PZDONEEXP_TABLE_NAME, where, limit);
     if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from newExp");
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDoneExp");
         return count;
 
@@ -1490,13 +1805,13 @@
     return deleted;
 }
-bool newExpInsertObject(psDB *dbh, newExpRow *object)
-{
-    return newExpInsert(dbh, object->exp_tag, object->exp_id, object->camera, object->telescope, object->exp_type, object->imfiles);
-}
-
-bool newExpInsertObjects(psDB *dbh, psArray *objects)
+bool pzDoneExpInsertObject(psDB *dbh, pzDoneExpRow *object)
+{
+    return pzDoneExpInsert(dbh, object->exp_id, object->camera, object->telescope);
+}
+
+bool pzDoneExpInsertObjects(psDB *dbh, psArray *objects)
 {
     for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!newExpInsertObject(dbh, objects->data[i])) {
+        if (!pzDoneExpInsertObject(dbh, objects->data[i])) {
             return false;
         }
@@ -1506,11 +1821,11 @@
 }
 
-bool newExpInsertFits(psDB *dbh, const psFits *fits)
+bool pzDoneExpInsertFits(psDB *dbh, const psFits *fits)
 {
     psArray         *rowSet;
 
-    // move to (the first?) extension named  NEWEXP_TABLE_NAME
-    if (!psFitsMoveExtName(fits, NEWEXP_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", NEWEXP_TABLE_NAME);
+    // move to (the first?) extension named  PZDONEEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PZDONEEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZDONEEXP_TABLE_NAME);
         return false;
     }
@@ -1530,4 +1845,747 @@
     }
 
+    if (!psDBInsertRows(dbh, PZDONEEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pzDoneExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PZDONEEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PZDONEEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pzDoneExpMetadataFromObject(const pzDoneExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pzDoneExpRow *pzDoneExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_id = psMetadataLookupPtr(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+
+    return pzDoneExpRowAlloc(exp_id, camera, telescope);
+}
+psArray *pzDoneExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PZDONEEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pzDoneExpRow *object = pzDoneExpObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pzDoneExpDeleteObject(psDB *dbh, const pzDoneExpRow *object)
+{
+    psMetadata *where = pzDoneExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PZDONEEXP_TABLE_NAME, where, 0);
+    psFree(where)
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDoneExp");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pzDoneExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pzDoneExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pzDoneExpRow *object = objects->data[i];
+        psMetadata *where = pzDoneExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PZDONEEXP_TABLE_NAME, where, limit);
+        psFree(where)
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDoneExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDoneExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pzDoneExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PZDONEEXP_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+static void pzDoneImfileRowFree(pzDoneImfileRow *object);
+
+pzDoneImfileRow *pzDoneImfileRowAlloc(const char *exp_id, const char *camera, const char *telescope, const char *class, const char *class_id, const char *exp_tag, const char *uri)
+{
+    pzDoneImfileRow *_object;
+
+    _object = psAlloc(sizeof(pzDoneImfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)pzDoneImfileRowFree);
+
+    _object->exp_id = psStringCopy(exp_id);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->class = psStringCopy(class);
+    _object->class_id = psStringCopy(class_id);
+    _object->exp_tag = psStringCopy(exp_tag);
+    _object->uri = psStringCopy(uri);
+
+    return _object;
+}
+
+static void pzDoneImfileRowFree(pzDoneImfileRow *object)
+{
+    psFree(object->exp_id);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->class);
+    psFree(object->class_id);
+    psFree(object->exp_tag);
+    psFree(object->uri);
+}
+
+bool pzDoneImfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, "Unique Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, PZDONEIMFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool pzDoneImfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, PZDONEIMFILE_TABLE_NAME);
+}
+
+bool pzDoneImfileInsert(psDB * dbh, const char *exp_id, const char *camera, const char *telescope, const char *class, const char *class_id, const char *exp_tag, const char *uri)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, PZDONEIMFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long pzDoneImfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, PZDONEIMFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDoneImfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDoneImfileInsertObject(psDB *dbh, pzDoneImfileRow *object)
+{
+    return pzDoneImfileInsert(dbh, object->exp_id, object->camera, object->telescope, object->class, object->class_id, object->exp_tag, object->uri);
+}
+
+bool pzDoneImfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!pzDoneImfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool pzDoneImfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  PZDONEIMFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, PZDONEIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", PZDONEIMFILE_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, PZDONEIMFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool pzDoneImfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, PZDONEIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, PZDONEIMFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *pzDoneImfileMetadataFromObject(const pzDoneImfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, object->exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, object->camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, object->telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, object->class)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, object->exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+pzDoneImfileRow *pzDoneImfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    char* exp_id = psMetadataLookupPtr(&status, md, "exp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_id");
+        return false;
+    }
+    char* camera = psMetadataLookupPtr(&status, md, "camera");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item camera");
+        return false;
+    }
+    char* telescope = psMetadataLookupPtr(&status, md, "telescope");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item telescope");
+        return false;
+    }
+    char* class = psMetadataLookupPtr(&status, md, "class");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class");
+        return false;
+    }
+    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
+        return false;
+    }
+    char* exp_tag = psMetadataLookupPtr(&status, md, "exp_tag");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_tag");
+        return false;
+    }
+    char* uri = psMetadataLookupPtr(&status, md, "uri");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
+        return false;
+    }
+
+    return pzDoneImfileRowAlloc(exp_id, camera, telescope, class, class_id, exp_tag, uri);
+}
+psArray *pzDoneImfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, PZDONEIMFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        pzDoneImfileRow *object = pzDoneImfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool pzDoneImfileDeleteObject(psDB *dbh, const pzDoneImfileRow *object)
+{
+    psMetadata *where = pzDoneImfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, PZDONEIMFILE_TABLE_NAME, where, 0);
+    psFree(where)
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDoneImfile");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "pzDoneImfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long pzDoneImfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        pzDoneImfileRow *object = objects->data[i];
+        psMetadata *where = pzDoneImfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, PZDONEIMFILE_TABLE_NAME, where, limit);
+        psFree(where)
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from pzDoneImfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool pzDoneImfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = pzDoneImfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            PZDONEIMFILE_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
+static void newExpRowFree(newExpRow *object);
+
+newExpRow *newExpRowAlloc(psU64 exp_tag, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, psS32 imfiles)
+{
+    newExpRow       *_object;
+
+    _object = psAlloc(sizeof(newExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)newExpRowFree);
+
+    _object->exp_tag = exp_tag;
+    _object->exp_id = psStringCopy(exp_id);
+    _object->camera = psStringCopy(camera);
+    _object->telescope = psStringCopy(telescope);
+    _object->dateobs = psTimeCopy(dateobs);
+    _object->exp_type = psStringCopy(exp_type);
+    _object->imfiles = imfiles;
+
+    return _object;
+}
+
+static void newExpRowFree(newExpRow *object)
+{
+    psFree(object->exp_id);
+    psFree(object->camera);
+    psFree(object->telescope);
+    psFree(object->dateobs);
+    psFree(object->exp_type);
+}
+
+bool newExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_U64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    psTime* dateobs = psTimeFromISO("", PS_TIME_UTC);
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psFree(dateobs);
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    psFree(dateobs);
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, NEWEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool newExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, NEWEXP_TABLE_NAME);
+}
+
+bool newExpInsert(psDB * dbh, psU64 exp_tag, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, psS32 imfiles)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_U64, NULL, exp_tag)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_id", PS_DATA_STRING, NULL, exp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "camera", PS_DATA_STRING, NULL, camera)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item camera");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "telescope", PS_DATA_STRING, NULL, telescope)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, exp_type)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "imfiles", PS_DATA_S32, NULL, imfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item imfiles");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, NEWEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long newExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, NEWEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from newExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool newExpInsertObject(psDB *dbh, newExpRow *object)
+{
+    return newExpInsert(dbh, object->exp_tag, object->exp_id, object->camera, object->telescope, object->dateobs, object->exp_type, object->imfiles);
+}
+
+bool newExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!newExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool newExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  NEWEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, NEWEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", NEWEXP_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
     if (!psDBInsertRows(dbh, NEWEXP_TABLE_NAME, rowSet)) {
         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
@@ -1565,5 +2623,5 @@
 {
     psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, object->exp_tag)) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_U64, NULL, object->exp_tag)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
@@ -1585,4 +2643,9 @@
         return false;
     }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dateobs", PS_DATA_TIME, NULL, object->dateobs)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
+        psFree(md);
+        return false;
+    }
     if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_type", PS_DATA_STRING, NULL, object->exp_type)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
@@ -1604,5 +2667,5 @@
 
 bool status = false;
-    char* exp_tag = psMetadataLookupPtr(&status, md, "exp_tag");
+    psU64 exp_tag = psMetadataLookupU64(&status, md, "exp_tag");
     if (!status) {
         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_tag");
@@ -1624,4 +2687,9 @@
         return false;
     }
+    psTime* dateobs = psMetadataLookupPtr(&status, md, "dateobs");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dateobs");
+        return false;
+    }
     char* exp_type = psMetadataLookupPtr(&status, md, "exp_type");
     if (!status) {
@@ -1635,5 +2703,5 @@
     }
 
-    return newExpRowAlloc(exp_tag, exp_id, camera, telescope, exp_type, imfiles);
+    return newExpRowAlloc(exp_tag, exp_id, camera, telescope, dateobs, exp_type, imfiles);
 }
 psArray *newExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
@@ -1763,5 +2831,5 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, NULL, "64")) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "class", PS_DATA_STRING, "Primary Key", "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item class");
         psFree(md);
