Index: trunk/ippdb/src/ippdb.c
===================================================================
--- trunk/ippdb/src/ippdb.c	(revision 12426)
+++ trunk/ippdb/src/ippdb.c	(revision 12535)
@@ -20,5 +20,5 @@
 /*
  *
- * This file was generated by glueforge 1.00
+ * This file was generated by glueforge 1.01
  *
  * Do NOT directly edit this file.
@@ -51,4 +51,14 @@
 #define CAMPROCESSEDEXP_TABLE_NAME "camProcessedExp"
 #define CAMMASK_TABLE_NAME "camMask"
+#define WARPRUN_TABLE_NAME "warpRun"
+#define WARPINPUTEXP_TABLE_NAME "warpInputExp"
+#define WARPSKYCELLMAP_TABLE_NAME "warpSkyCellMap"
+#define WARPSKYFILE_TABLE_NAME "warpSkyfile"
+#define DIFFRUN_TABLE_NAME "diffRun"
+#define DIFFINPUTSKYFILE_TABLE_NAME "diffInputSkyfile"
+#define DIFFSKYFILE_TABLE_NAME "diffSkyfile"
+#define STACKRUN_TABLE_NAME "stackRun"
+#define STACKINPUTSKYFILE_TABLE_NAME "stackInputSkyfile"
+#define STACKSUMSKYFILE_TABLE_NAME "stackSumSkyfile"
 #define DETRUN_TABLE_NAME "detRun"
 #define DETINPUTEXP_TABLE_NAME "detInputExp"
@@ -62,14 +72,4 @@
 #define DETRESIDEXP_TABLE_NAME "detResidExp"
 #define DETRUNSUMMARY_TABLE_NAME "detRunSummary"
-#define WARPRUN_TABLE_NAME "warpRun"
-#define WARPINPUTEXP_TABLE_NAME "warpInputExp"
-#define WARPSKYCELLMAP_TABLE_NAME "warpSkyCellMap"
-#define WARPSKYFILE_TABLE_NAME "warpSkyfile"
-#define DIFFRUN_TABLE_NAME "diffRun"
-#define DIFFINPUTSKYFILE_TABLE_NAME "diffInputSkyfile"
-#define DIFFSKYFILE_TABLE_NAME "diffSkyfile"
-#define STACKRUN_TABLE_NAME "stackRun"
-#define STACKINPUTSKYFILE_TABLE_NAME "stackInputSkyfile"
-#define STACKSUMSKYFILE_TABLE_NAME "stackSumSkyfile"
 #define MAX_STRING_LENGTH 1024
 
@@ -7190,5 +7190,5 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key", 0)) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key fkey(chip_id) ref chipProcessedExp.chip_id)", 0)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
         psFree(md);
@@ -7607,5 +7607,5 @@
         return false;
     }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key", 0)) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key fkey(chip_id) ref chipProcessedExp.chip_id)", 0)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
         psFree(md);
@@ -8458,4 +8458,3623 @@
 
     psMetadata *md = camMaskMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpRunRowFree(warpRunRow *object);
+
+warpRunRow *warpRunRowAlloc(psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
+{
+    warpRunRow      *_object;
+
+    _object = psAlloc(sizeof(warpRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpRunRowFree);
+
+    _object->warp_id = warp_id;
+    _object->mode = psStringCopy(mode);
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->registered = psTimeCopy(registered);
+
+    return _object;
+}
+
+static void warpRunRowFree(warpRunRow *object)
+{
+    psFree(object->mode);
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->dvodb);
+    psFree(object->registered);
+}
+
+bool warpRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPRUN_TABLE_NAME);
+}
+
+bool warpRunInsert(psDB * dbh, psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, mode)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpRunInsertObject(psDB *dbh, warpRunRow *object)
+{
+    return warpRunInsert(dbh, object->warp_id, object->mode, object->state, object->workdir, object->dvodb, object->registered);
+}
+
+bool warpRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPRUN_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, WARPRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpRunMetadataFromObject(const warpRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, object->mode)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpRunRow *warpRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* mode = psMetadataLookupPtr(&status, md, "mode");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item mode");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+
+    return warpRunRowAlloc(warp_id, mode, state, workdir, dvodb, registered);
+}
+psArray *warpRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPRUN_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++) {
+        warpRunRow *object = warpRunObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpRunDeleteObject(psDB *dbh, const warpRunRow *object)
+{
+    psMetadata *where = warpRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
+        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, "warpRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpRunRow *object = objects->data[i];
+        psMetadata *where = warpRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpRunPrintObjects(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 = warpRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPRUN_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;
+}
+bool warpRunPrintObject(FILE *stream, warpRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpInputExpRowFree(warpInputExpRow *object);
+
+warpInputExpRow *warpInputExpRowAlloc(psS64 warp_id, psS64 cam_id, bool magiced)
+{
+    warpInputExpRow *_object;
+
+    _object = psAlloc(sizeof(warpInputExpRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpInputExpRowFree);
+
+    _object->warp_id = warp_id;
+    _object->cam_id = cam_id;
+    _object->magiced = magiced;
+
+    return _object;
+}
+
+static void warpInputExpRowFree(warpInputExpRow *object)
+{
+}
+
+bool warpInputExpCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id) ref warpRun(warp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key fkey(cam_id) ref camProcessedExp(cam_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPINPUTEXP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpInputExpDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPINPUTEXP_TABLE_NAME);
+}
+
+bool warpInputExpInsert(psDB * dbh, psS64 warp_id, psS64 cam_id, bool magiced)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, magiced)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPINPUTEXP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpInputExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpInputExpInsertObject(psDB *dbh, warpInputExpRow *object)
+{
+    return warpInputExpInsert(dbh, object->warp_id, object->cam_id, object->magiced);
+}
+
+bool warpInputExpInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpInputExpInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpInputExpInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPINPUTEXP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPINPUTEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPINPUTEXP_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, WARPINPUTEXP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpInputExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPINPUTEXP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpInputExpMetadataFromObject(const warpInputExpRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, object->magiced)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpInputExpRow *warpInputExpObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
+        return false;
+    }
+    bool magiced = psMetadataLookupBool(&status, md, "magiced");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magiced");
+        return false;
+    }
+
+    return warpInputExpRowAlloc(warp_id, cam_id, magiced);
+}
+psArray *warpInputExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPINPUTEXP_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++) {
+        warpInputExpRow *object = warpInputExpObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpInputExpDeleteObject(psDB *dbh, const warpInputExpRow *object)
+{
+    psMetadata *where = warpInputExpMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
+        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, "warpInputExpRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpInputExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpInputExpRow *object = objects->data[i];
+        psMetadata *where = warpInputExpMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpInputExpPrintObjects(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 = warpInputExpMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPINPUTEXP_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;
+}
+bool warpInputExpPrintObject(FILE *stream, warpInputExpRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpInputExpMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpSkyCellMapRowFree(warpSkyCellMapRow *object);
+
+warpSkyCellMapRow *warpSkyCellMapRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
+{
+    warpSkyCellMapRow *_object;
+
+    _object = psAlloc(sizeof(warpSkyCellMapRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpSkyCellMapRowFree);
+
+    _object->warp_id = warp_id;
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->cam_id = cam_id;
+    _object->class_id = psStringCopy(class_id);
+    _object->fault = fault;
+
+    return _object;
+}
+
+static void warpSkyCellMapRowFree(warpSkyCellMapRow *object)
+{
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->class_id);
+}
+
+bool warpSkyCellMapCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id, cam_id) ref warpInputExp(warp_id, cam_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        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, "fault", PS_DATA_S16, "Key", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpSkyCellMapDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPSKYCELLMAP_TABLE_NAME);
+}
+
+bool warpSkyCellMapInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        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, "fault", PS_DATA_S16, NULL, fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpSkyCellMapDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyCellMapInsertObject(psDB *dbh, warpSkyCellMapRow *object)
+{
+    return warpSkyCellMapInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->cam_id, object->class_id, object->fault);
+}
+
+bool warpSkyCellMapInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpSkyCellMapInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpSkyCellMapInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPSKYCELLMAP_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPSKYCELLMAP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYCELLMAP_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, WARPSKYCELLMAP_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpSkyCellMapSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYCELLMAP_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpSkyCellMapMetadataFromObject(const warpSkyCellMapRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
+        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, "fault", PS_DATA_S16, NULL, object->fault)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpSkyCellMapRow *warpSkyCellMapObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
+        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;
+    }
+    psS16 fault = psMetadataLookupS16(&status, md, "fault");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
+        return false;
+    }
+
+    return warpSkyCellMapRowAlloc(warp_id, skycell_id, tess_id, cam_id, class_id, fault);
+}
+psArray *warpSkyCellMapSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_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++) {
+        warpSkyCellMapRow *object = warpSkyCellMapObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpSkyCellMapDeleteObject(psDB *dbh, const warpSkyCellMapRow *object)
+{
+    psMetadata *where = warpSkyCellMapMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
+        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, "warpSkyCellMapRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpSkyCellMapDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpSkyCellMapRow *object = objects->data[i];
+        psMetadata *where = warpSkyCellMapMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyCellMapPrintObjects(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 = warpSkyCellMapMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPSKYCELLMAP_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;
+}
+bool warpSkyCellMapPrintObject(FILE *stream, warpSkyCellMapRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpSkyCellMapMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void warpSkyfileRowFree(warpSkyfileRow *object);
+
+warpSkyfileRow *warpSkyfileRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
+{
+    warpSkyfileRow  *_object;
+
+    _object = psAlloc(sizeof(warpSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)warpSkyfileRowFree);
+
+    _object->warp_id = warp_id;
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+
+    return _object;
+}
+
+static void warpSkyfileRowFree(warpSkyfileRow *object)
+{
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->uri);
+}
+
+bool warpSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id, skycell_id, tess_id) ref warpSkyCellMap(warp_id, skycell_id, tess_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, WARPSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool warpSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, WARPSKYFILE_TABLE_NAME);
+}
+
+bool warpSkyfileInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, WARPSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long warpSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyfileInsertObject(psDB *dbh, warpSkyfileRow *object)
+{
+    return warpSkyfileInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->uri, object->bg, object->bg_stdev);
+}
+
+bool warpSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!warpSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool warpSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  WARPSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, WARPSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYFILE_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, WARPSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool warpSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *warpSkyfileMetadataFromObject(const warpSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+warpSkyfileRow *warpSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_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;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+
+    return warpSkyfileRowAlloc(warp_id, skycell_id, tess_id, uri, bg, bg_stdev);
+}
+psArray *warpSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, WARPSKYFILE_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++) {
+        warpSkyfileRow *object = warpSkyfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool warpSkyfileDeleteObject(psDB *dbh, const warpSkyfileRow *object)
+{
+    psMetadata *where = warpSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
+        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, "warpSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long warpSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        warpSkyfileRow *object = objects->data[i];
+        psMetadata *where = warpSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool warpSkyfilePrintObjects(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 = warpSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            WARPSKYFILE_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;
+}
+bool warpSkyfilePrintObject(FILE *stream, warpSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = warpSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void diffRunRowFree(diffRunRow *object);
+
+diffRunRow *diffRunRowAlloc(psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
+{
+    diffRunRow      *_object;
+
+    _object = psAlloc(sizeof(diffRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)diffRunRowFree);
+
+    _object->diff_id = diff_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->registered = psTimeCopy(registered);
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+
+    return _object;
+}
+
+static void diffRunRowFree(diffRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->dvodb);
+    psFree(object->registered);
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+}
+
+bool diffRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DIFFRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool diffRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DIFFRUN_TABLE_NAME);
+}
+
+bool diffRunInsert(psDB * dbh, psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DIFFRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long diffRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffRunInsertObject(psDB *dbh, diffRunRow *object)
+{
+    return diffRunInsert(dbh, object->diff_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
+}
+
+bool diffRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!diffRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool diffRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DIFFRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DIFFRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFRUN_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, DIFFRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool diffRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *diffRunMetadataFromObject(const diffRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+diffRunRow *diffRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+
+    return diffRunRowAlloc(diff_id, state, workdir, dvodb, registered, skycell_id, tess_id);
+}
+psArray *diffRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DIFFRUN_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++) {
+        diffRunRow *object = diffRunObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool diffRunDeleteObject(psDB *dbh, const diffRunRow *object)
+{
+    psMetadata *where = diffRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
+        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, "diffRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long diffRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        diffRunRow *object = objects->data[i];
+        psMetadata *where = diffRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffRunPrintObjects(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 = diffRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DIFFRUN_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;
+}
+bool diffRunPrintObject(FILE *stream, diffRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = diffRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void diffInputSkyfileRowFree(diffInputSkyfileRow *object);
+
+diffInputSkyfileRow *diffInputSkyfileRowAlloc(psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
+{
+    diffInputSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(diffInputSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)diffInputSkyfileRowFree);
+
+    _object->diff_id = diff_id;
+    _object->warp_id = warp_id;
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+    _object->kind = psStringCopy(kind);
+    _object->template = template;
+
+    return _object;
+}
+
+static void diffInputSkyfileRowFree(diffInputSkyfileRow *object)
+{
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+    psFree(object->kind);
+}
+
+bool diffInputSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id, skycell_id, tess_id) ref warpSkyfile(warp_id, skycell_id, tess_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool diffInputSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME);
+}
+
+bool diffInputSkyfileInsert(psDB * dbh, psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, kind)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, template)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long diffInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffInputSkyfileInsertObject(psDB *dbh, diffInputSkyfileRow *object)
+{
+    return diffInputSkyfileInsert(dbh, object->diff_id, object->warp_id, object->skycell_id, object->tess_id, object->kind, object->template);
+}
+
+bool diffInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!diffInputSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool diffInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DIFFINPUTSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DIFFINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFINPUTSKYFILE_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, DIFFINPUTSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool diffInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *diffInputSkyfileMetadataFromObject(const diffInputSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, object->kind)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, object->template)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+diffInputSkyfileRow *diffInputSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
+        return false;
+    }
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+    char* kind = psMetadataLookupPtr(&status, md, "kind");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item kind");
+        return false;
+    }
+    bool template = psMetadataLookupBool(&status, md, "template");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item template");
+        return false;
+    }
+
+    return diffInputSkyfileRowAlloc(diff_id, warp_id, skycell_id, tess_id, kind, template);
+}
+psArray *diffInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_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++) {
+        diffInputSkyfileRow *object = diffInputSkyfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool diffInputSkyfileDeleteObject(psDB *dbh, const diffInputSkyfileRow *object)
+{
+    psMetadata *where = diffInputSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
+        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, "diffInputSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long diffInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        diffInputSkyfileRow *object = objects->data[i];
+        psMetadata *where = diffInputSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffInputSkyfilePrintObjects(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 = diffInputSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DIFFINPUTSKYFILE_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;
+}
+bool diffInputSkyfilePrintObject(FILE *stream, diffInputSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = diffInputSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void diffSkyfileRowFree(diffSkyfileRow *object);
+
+diffSkyfileRow *diffSkyfileRowAlloc(psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
+{
+    diffSkyfileRow  *_object;
+
+    _object = psAlloc(sizeof(diffSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)diffSkyfileRowFree);
+
+    _object->diff_id = diff_id;
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+
+    return _object;
+}
+
+static void diffSkyfileRowFree(diffSkyfileRow *object)
+{
+    psFree(object->uri);
+}
+
+bool diffSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, DIFFSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool diffSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, DIFFSKYFILE_TABLE_NAME);
+}
+
+bool diffSkyfileInsert(psDB * dbh, psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, DIFFSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long diffSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffSkyfileInsertObject(psDB *dbh, diffSkyfileRow *object)
+{
+    return diffSkyfileInsert(dbh, object->diff_id, object->uri, object->bg, object->bg_stdev);
+}
+
+bool diffSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!diffSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool diffSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  DIFFSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, DIFFSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFSKYFILE_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, DIFFSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool diffSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *diffSkyfileMetadataFromObject(const diffSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+diffSkyfileRow *diffSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_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;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+
+    return diffSkyfileRowAlloc(diff_id, uri, bg, bg_stdev);
+}
+psArray *diffSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_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++) {
+        diffSkyfileRow *object = diffSkyfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool diffSkyfileDeleteObject(psDB *dbh, const diffSkyfileRow *object)
+{
+    psMetadata *where = diffSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
+        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, "diffSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long diffSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        diffSkyfileRow *object = objects->data[i];
+        psMetadata *where = diffSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool diffSkyfilePrintObjects(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 = diffSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            DIFFSKYFILE_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;
+}
+bool diffSkyfilePrintObject(FILE *stream, diffSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = diffSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void stackRunRowFree(stackRunRow *object);
+
+stackRunRow *stackRunRowAlloc(psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
+{
+    stackRunRow     *_object;
+
+    _object = psAlloc(sizeof(stackRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)stackRunRowFree);
+
+    _object->stack_id = stack_id;
+    _object->state = psStringCopy(state);
+    _object->workdir = psStringCopy(workdir);
+    _object->dvodb = psStringCopy(dvodb);
+    _object->registered = psTimeCopy(registered);
+    _object->skycell_id = psStringCopy(skycell_id);
+    _object->tess_id = psStringCopy(tess_id);
+
+    return _object;
+}
+
+static void stackRunRowFree(stackRunRow *object)
+{
+    psFree(object->state);
+    psFree(object->workdir);
+    psFree(object->dvodb);
+    psFree(object->registered);
+    psFree(object->skycell_id);
+    psFree(object->tess_id);
+}
+
+bool stackRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, STACKRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool stackRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, STACKRUN_TABLE_NAME);
+}
+
+bool stackRunInsert(psDB * dbh, psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, STACKRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long stackRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackRunInsertObject(psDB *dbh, stackRunRow *object)
+{
+    return stackRunInsert(dbh, object->stack_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
+}
+
+bool stackRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!stackRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool stackRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  STACKRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, STACKRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKRUN_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, STACKRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool stackRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, STACKRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *stackRunMetadataFromObject(const stackRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+stackRunRow *stackRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
+        return false;
+    }
+    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
+        return false;
+    }
+    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
+        return false;
+    }
+    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
+        return false;
+    }
+    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
+        return false;
+    }
+
+    return stackRunRowAlloc(stack_id, state, workdir, dvodb, registered, skycell_id, tess_id);
+}
+psArray *stackRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, STACKRUN_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++) {
+        stackRunRow *object = stackRunObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool stackRunDeleteObject(psDB *dbh, const stackRunRow *object)
+{
+    psMetadata *where = stackRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
+        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, "stackRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long stackRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        stackRunRow *object = objects->data[i];
+        psMetadata *where = stackRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackRunPrintObjects(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 = stackRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            STACKRUN_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;
+}
+bool stackRunPrintObject(FILE *stream, stackRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = stackRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void stackInputSkyfileRowFree(stackInputSkyfileRow *object);
+
+stackInputSkyfileRow *stackInputSkyfileRowAlloc(psS64 stack_id, psS64 warp_id)
+{
+    stackInputSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(stackInputSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)stackInputSkyfileRowFree);
+
+    _object->stack_id = stack_id;
+    _object->warp_id = warp_id;
+
+    return _object;
+}
+
+static void stackInputSkyfileRowFree(stackInputSkyfileRow *object)
+{
+}
+
+bool stackInputSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key fkey(stack_id) ref stackRun(stack_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id) ref warpSkyfile(warp_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool stackInputSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, STACKINPUTSKYFILE_TABLE_NAME);
+}
+
+bool stackInputSkyfileInsert(psDB * dbh, psS64 stack_id, psS64 warp_id)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long stackInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackInputSkyfileInsertObject(psDB *dbh, stackInputSkyfileRow *object)
+{
+    return stackInputSkyfileInsert(dbh, object->stack_id, object->warp_id);
+}
+
+bool stackInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!stackInputSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool stackInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  STACKINPUTSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, STACKINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKINPUTSKYFILE_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, STACKINPUTSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool stackInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, STACKINPUTSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *stackInputSkyfileMetadataFromObject(const stackInputSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+stackInputSkyfileRow *stackInputSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
+        return false;
+    }
+    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
+        return false;
+    }
+
+    return stackInputSkyfileRowAlloc(stack_id, warp_id);
+}
+psArray *stackInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_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++) {
+        stackInputSkyfileRow *object = stackInputSkyfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool stackInputSkyfileDeleteObject(psDB *dbh, const stackInputSkyfileRow *object)
+{
+    psMetadata *where = stackInputSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
+        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, "stackInputSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long stackInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        stackInputSkyfileRow *object = objects->data[i];
+        psMetadata *where = stackInputSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackInputSkyfilePrintObjects(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 = stackInputSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            STACKINPUTSKYFILE_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;
+}
+bool stackInputSkyfilePrintObject(FILE *stream, stackInputSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = stackInputSkyfileMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void stackSumSkyfileRowFree(stackSumSkyfileRow *object);
+
+stackSumSkyfileRow *stackSumSkyfileRowAlloc(psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
+{
+    stackSumSkyfileRow *_object;
+
+    _object = psAlloc(sizeof(stackSumSkyfileRow));
+    psMemSetDeallocator(_object, (psFreeFunc)stackSumSkyfileRowFree);
+
+    _object->stack_id = stack_id;
+    _object->uri = psStringCopy(uri);
+    _object->bg = bg;
+    _object->bg_stdev = bg_stdev;
+
+    return _object;
+}
+
+static void stackSumSkyfileRowFree(stackSumSkyfileRow *object)
+{
+    psFree(object->uri);
+}
+
+bool stackSumSkyfileCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key fkey(stack_id) ref stackRun(stack_id)", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool stackSumSkyfileDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, STACKSUMSKYFILE_TABLE_NAME);
+}
+
+bool stackSumSkyfileInsert(psDB * dbh, psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long stackSumSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackSumSkyfileInsertObject(psDB *dbh, stackSumSkyfileRow *object)
+{
+    return stackSumSkyfileInsert(dbh, object->stack_id, object->uri, object->bg, object->bg_stdev);
+}
+
+bool stackSumSkyfileInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!stackSumSkyfileInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool stackSumSkyfileInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  STACKSUMSKYFILE_TABLE_NAME
+    if (!psFitsMoveExtName(fits, STACKSUMSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKSUMSKYFILE_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, STACKSUMSKYFILE_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool stackSumSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, STACKSUMSKYFILE_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *stackSumSkyfileMetadataFromObject(const stackSumSkyfileRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_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;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+stackSumSkyfileRow *stackSumSkyfileObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_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;
+    }
+    psF64 bg = psMetadataLookupF64(&status, md, "bg");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
+        return false;
+    }
+    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
+        return false;
+    }
+
+    return stackSumSkyfileRowAlloc(stack_id, uri, bg, bg_stdev);
+}
+psArray *stackSumSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_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++) {
+        stackSumSkyfileRow *object = stackSumSkyfileObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool stackSumSkyfileDeleteObject(psDB *dbh, const stackSumSkyfileRow *object)
+{
+    psMetadata *where = stackSumSkyfileMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
+        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, "stackSumSkyfileRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long stackSumSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        stackSumSkyfileRow *object = objects->data[i];
+        psMetadata *where = stackSumSkyfileMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool stackSumSkyfilePrintObjects(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 = stackSumSkyfileMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            STACKSUMSKYFILE_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;
+}
+bool stackSumSkyfilePrintObject(FILE *stream, stackSumSkyfileRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = stackSumSkyfileMetadataFromObject(object);
 
     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
@@ -13570,3621 +17189,2 @@
     return true;
 }
-static void warpRunRowFree(warpRunRow *object);
-
-warpRunRow *warpRunRowAlloc(psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
-{
-    warpRunRow      *_object;
-
-    _object = psAlloc(sizeof(warpRunRow));
-    psMemSetDeallocator(_object, (psFreeFunc)warpRunRowFree);
-
-    _object->warp_id = warp_id;
-    _object->mode = psStringCopy(mode);
-    _object->state = psStringCopy(state);
-    _object->workdir = psStringCopy(workdir);
-    _object->dvodb = psStringCopy(dvodb);
-    _object->registered = psTimeCopy(registered);
-
-    return _object;
-}
-
-static void warpRunRowFree(warpRunRow *object)
-{
-    psFree(object->mode);
-    psFree(object->state);
-    psFree(object->workdir);
-    psFree(object->dvodb);
-    psFree(object->registered);
-}
-
-bool warpRunCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, WARPRUN_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool warpRunDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, WARPRUN_TABLE_NAME);
-}
-
-bool warpRunInsert(psDB * dbh, psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, mode)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, WARPRUN_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long warpRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpRunInsertObject(psDB *dbh, warpRunRow *object)
-{
-    return warpRunInsert(dbh, object->warp_id, object->mode, object->state, object->workdir, object->dvodb, object->registered);
-}
-
-bool warpRunInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!warpRunInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool warpRunInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  WARPRUN_TABLE_NAME
-    if (!psFitsMoveExtName(fits, WARPRUN_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPRUN_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, WARPRUN_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool warpRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, WARPRUN_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *warpRunMetadataFromObject(const warpRunRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, object->mode)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-warpRunRow *warpRunObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
-        return false;
-    }
-    char* mode = psMetadataLookupPtr(&status, md, "mode");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item mode");
-        return false;
-    }
-    char* state = psMetadataLookupPtr(&status, md, "state");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
-        return false;
-    }
-    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
-        return false;
-    }
-    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
-        return false;
-    }
-    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
-        return false;
-    }
-
-    return warpRunRowAlloc(warp_id, mode, state, workdir, dvodb, registered);
-}
-psArray *warpRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, WARPRUN_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++) {
-        warpRunRow *object = warpRunObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool warpRunDeleteObject(psDB *dbh, const warpRunRow *object)
-{
-    psMetadata *where = warpRunMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
-        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, "warpRunRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long warpRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        warpRunRow *object = objects->data[i];
-        psMetadata *where = warpRunMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpRunPrintObjects(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 = warpRunMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            WARPRUN_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;
-}
-bool warpRunPrintObject(FILE *stream, warpRunRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = warpRunMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void warpInputExpRowFree(warpInputExpRow *object);
-
-warpInputExpRow *warpInputExpRowAlloc(psS64 warp_id, psS64 cam_id, bool magiced)
-{
-    warpInputExpRow *_object;
-
-    _object = psAlloc(sizeof(warpInputExpRow));
-    psMemSetDeallocator(_object, (psFreeFunc)warpInputExpRowFree);
-
-    _object->warp_id = warp_id;
-    _object->cam_id = cam_id;
-    _object->magiced = magiced;
-
-    return _object;
-}
-
-static void warpInputExpRowFree(warpInputExpRow *object)
-{
-}
-
-bool warpInputExpCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, "Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, WARPINPUTEXP_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool warpInputExpDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, WARPINPUTEXP_TABLE_NAME);
-}
-
-bool warpInputExpInsert(psDB * dbh, psS64 warp_id, psS64 cam_id, bool magiced)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, magiced)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, WARPINPUTEXP_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long warpInputExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpInputExpInsertObject(psDB *dbh, warpInputExpRow *object)
-{
-    return warpInputExpInsert(dbh, object->warp_id, object->cam_id, object->magiced);
-}
-
-bool warpInputExpInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!warpInputExpInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool warpInputExpInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  WARPINPUTEXP_TABLE_NAME
-    if (!psFitsMoveExtName(fits, WARPINPUTEXP_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPINPUTEXP_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, WARPINPUTEXP_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool warpInputExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, WARPINPUTEXP_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *warpInputExpMetadataFromObject(const warpInputExpRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, object->magiced)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-warpInputExpRow *warpInputExpObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
-        return false;
-    }
-    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
-        return false;
-    }
-    bool magiced = psMetadataLookupBool(&status, md, "magiced");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magiced");
-        return false;
-    }
-
-    return warpInputExpRowAlloc(warp_id, cam_id, magiced);
-}
-psArray *warpInputExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, WARPINPUTEXP_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++) {
-        warpInputExpRow *object = warpInputExpObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool warpInputExpDeleteObject(psDB *dbh, const warpInputExpRow *object)
-{
-    psMetadata *where = warpInputExpMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
-        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, "warpInputExpRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long warpInputExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        warpInputExpRow *object = objects->data[i];
-        psMetadata *where = warpInputExpMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpInputExpPrintObjects(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 = warpInputExpMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            WARPINPUTEXP_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;
-}
-bool warpInputExpPrintObject(FILE *stream, warpInputExpRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = warpInputExpMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void warpSkyCellMapRowFree(warpSkyCellMapRow *object);
-
-warpSkyCellMapRow *warpSkyCellMapRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
-{
-    warpSkyCellMapRow *_object;
-
-    _object = psAlloc(sizeof(warpSkyCellMapRow));
-    psMemSetDeallocator(_object, (psFreeFunc)warpSkyCellMapRowFree);
-
-    _object->warp_id = warp_id;
-    _object->skycell_id = psStringCopy(skycell_id);
-    _object->tess_id = psStringCopy(tess_id);
-    _object->cam_id = cam_id;
-    _object->class_id = psStringCopy(class_id);
-    _object->fault = fault;
-
-    return _object;
-}
-
-static void warpSkyCellMapRowFree(warpSkyCellMapRow *object)
-{
-    psFree(object->skycell_id);
-    psFree(object->tess_id);
-    psFree(object->class_id);
-}
-
-bool warpSkyCellMapCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
-        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, "fault", PS_DATA_S16, "Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool warpSkyCellMapDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, WARPSKYCELLMAP_TABLE_NAME);
-}
-
-bool warpSkyCellMapInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
-        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, "fault", PS_DATA_S16, NULL, fault)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long warpSkyCellMapDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpSkyCellMapInsertObject(psDB *dbh, warpSkyCellMapRow *object)
-{
-    return warpSkyCellMapInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->cam_id, object->class_id, object->fault);
-}
-
-bool warpSkyCellMapInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!warpSkyCellMapInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool warpSkyCellMapInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  WARPSKYCELLMAP_TABLE_NAME
-    if (!psFitsMoveExtName(fits, WARPSKYCELLMAP_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYCELLMAP_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, WARPSKYCELLMAP_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool warpSkyCellMapSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYCELLMAP_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *warpSkyCellMapMetadataFromObject(const warpSkyCellMapRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
-        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, "fault", PS_DATA_S16, NULL, object->fault)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-warpSkyCellMapRow *warpSkyCellMapObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
-        return false;
-    }
-    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
-        return false;
-    }
-    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
-        return false;
-    }
-    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
-        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;
-    }
-    psS16 fault = psMetadataLookupS16(&status, md, "fault");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
-        return false;
-    }
-
-    return warpSkyCellMapRowAlloc(warp_id, skycell_id, tess_id, cam_id, class_id, fault);
-}
-psArray *warpSkyCellMapSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_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++) {
-        warpSkyCellMapRow *object = warpSkyCellMapObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool warpSkyCellMapDeleteObject(psDB *dbh, const warpSkyCellMapRow *object)
-{
-    psMetadata *where = warpSkyCellMapMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
-        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, "warpSkyCellMapRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long warpSkyCellMapDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        warpSkyCellMapRow *object = objects->data[i];
-        psMetadata *where = warpSkyCellMapMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpSkyCellMapPrintObjects(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 = warpSkyCellMapMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            WARPSKYCELLMAP_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;
-}
-bool warpSkyCellMapPrintObject(FILE *stream, warpSkyCellMapRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = warpSkyCellMapMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void warpSkyfileRowFree(warpSkyfileRow *object);
-
-warpSkyfileRow *warpSkyfileRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
-{
-    warpSkyfileRow  *_object;
-
-    _object = psAlloc(sizeof(warpSkyfileRow));
-    psMemSetDeallocator(_object, (psFreeFunc)warpSkyfileRowFree);
-
-    _object->warp_id = warp_id;
-    _object->skycell_id = psStringCopy(skycell_id);
-    _object->tess_id = psStringCopy(tess_id);
-    _object->uri = psStringCopy(uri);
-    _object->bg = bg;
-    _object->bg_stdev = bg_stdev;
-
-    return _object;
-}
-
-static void warpSkyfileRowFree(warpSkyfileRow *object)
-{
-    psFree(object->skycell_id);
-    psFree(object->tess_id);
-    psFree(object->uri);
-}
-
-bool warpSkyfileCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, WARPSKYFILE_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool warpSkyfileDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, WARPSKYFILE_TABLE_NAME);
-}
-
-bool warpSkyfileInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, WARPSKYFILE_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long warpSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpSkyfileInsertObject(psDB *dbh, warpSkyfileRow *object)
-{
-    return warpSkyfileInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->uri, object->bg, object->bg_stdev);
-}
-
-bool warpSkyfileInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!warpSkyfileInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool warpSkyfileInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  WARPSKYFILE_TABLE_NAME
-    if (!psFitsMoveExtName(fits, WARPSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYFILE_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, WARPSKYFILE_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool warpSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *warpSkyfileMetadataFromObject(const warpSkyfileRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-warpSkyfileRow *warpSkyfileObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
-        return false;
-    }
-    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
-        return false;
-    }
-    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_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;
-    }
-    psF64 bg = psMetadataLookupF64(&status, md, "bg");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
-        return false;
-    }
-    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
-        return false;
-    }
-
-    return warpSkyfileRowAlloc(warp_id, skycell_id, tess_id, uri, bg, bg_stdev);
-}
-psArray *warpSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, WARPSKYFILE_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++) {
-        warpSkyfileRow *object = warpSkyfileObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool warpSkyfileDeleteObject(psDB *dbh, const warpSkyfileRow *object)
-{
-    psMetadata *where = warpSkyfileMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
-        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, "warpSkyfileRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long warpSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        warpSkyfileRow *object = objects->data[i];
-        psMetadata *where = warpSkyfileMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool warpSkyfilePrintObjects(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 = warpSkyfileMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            WARPSKYFILE_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;
-}
-bool warpSkyfilePrintObject(FILE *stream, warpSkyfileRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = warpSkyfileMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void diffRunRowFree(diffRunRow *object);
-
-diffRunRow *diffRunRowAlloc(psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
-{
-    diffRunRow      *_object;
-
-    _object = psAlloc(sizeof(diffRunRow));
-    psMemSetDeallocator(_object, (psFreeFunc)diffRunRowFree);
-
-    _object->diff_id = diff_id;
-    _object->state = psStringCopy(state);
-    _object->workdir = psStringCopy(workdir);
-    _object->dvodb = psStringCopy(dvodb);
-    _object->registered = psTimeCopy(registered);
-    _object->skycell_id = psStringCopy(skycell_id);
-    _object->tess_id = psStringCopy(tess_id);
-
-    return _object;
-}
-
-static void diffRunRowFree(diffRunRow *object)
-{
-    psFree(object->state);
-    psFree(object->workdir);
-    psFree(object->dvodb);
-    psFree(object->registered);
-    psFree(object->skycell_id);
-    psFree(object->tess_id);
-}
-
-bool diffRunCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, DIFFRUN_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool diffRunDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, DIFFRUN_TABLE_NAME);
-}
-
-bool diffRunInsert(psDB * dbh, psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, DIFFRUN_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long diffRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool diffRunInsertObject(psDB *dbh, diffRunRow *object)
-{
-    return diffRunInsert(dbh, object->diff_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
-}
-
-bool diffRunInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!diffRunInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool diffRunInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  DIFFRUN_TABLE_NAME
-    if (!psFitsMoveExtName(fits, DIFFRUN_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFRUN_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, DIFFRUN_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool diffRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFRUN_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *diffRunMetadataFromObject(const diffRunRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-diffRunRow *diffRunObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
-        return false;
-    }
-    char* state = psMetadataLookupPtr(&status, md, "state");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
-        return false;
-    }
-    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
-        return false;
-    }
-    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
-        return false;
-    }
-    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
-        return false;
-    }
-    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
-        return false;
-    }
-    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
-        return false;
-    }
-
-    return diffRunRowAlloc(diff_id, state, workdir, dvodb, registered, skycell_id, tess_id);
-}
-psArray *diffRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, DIFFRUN_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++) {
-        diffRunRow *object = diffRunObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool diffRunDeleteObject(psDB *dbh, const diffRunRow *object)
-{
-    psMetadata *where = diffRunMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
-        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, "diffRunRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long diffRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        diffRunRow *object = objects->data[i];
-        psMetadata *where = diffRunMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool diffRunPrintObjects(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 = diffRunMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            DIFFRUN_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;
-}
-bool diffRunPrintObject(FILE *stream, diffRunRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = diffRunMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void diffInputSkyfileRowFree(diffInputSkyfileRow *object);
-
-diffInputSkyfileRow *diffInputSkyfileRowAlloc(psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
-{
-    diffInputSkyfileRow *_object;
-
-    _object = psAlloc(sizeof(diffInputSkyfileRow));
-    psMemSetDeallocator(_object, (psFreeFunc)diffInputSkyfileRowFree);
-
-    _object->diff_id = diff_id;
-    _object->warp_id = warp_id;
-    _object->skycell_id = psStringCopy(skycell_id);
-    _object->tess_id = psStringCopy(tess_id);
-    _object->kind = psStringCopy(kind);
-    _object->template = template;
-
-    return _object;
-}
-
-static void diffInputSkyfileRowFree(diffInputSkyfileRow *object)
-{
-    psFree(object->skycell_id);
-    psFree(object->tess_id);
-    psFree(object->kind);
-}
-
-bool diffInputSkyfileCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool diffInputSkyfileDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME);
-}
-
-bool diffInputSkyfileInsert(psDB * dbh, psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, kind)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, template)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long diffInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool diffInputSkyfileInsertObject(psDB *dbh, diffInputSkyfileRow *object)
-{
-    return diffInputSkyfileInsert(dbh, object->diff_id, object->warp_id, object->skycell_id, object->tess_id, object->kind, object->template);
-}
-
-bool diffInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!diffInputSkyfileInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool diffInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  DIFFINPUTSKYFILE_TABLE_NAME
-    if (!psFitsMoveExtName(fits, DIFFINPUTSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFINPUTSKYFILE_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, DIFFINPUTSKYFILE_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool diffInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFINPUTSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *diffInputSkyfileMetadataFromObject(const diffInputSkyfileRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, object->kind)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, object->template)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-diffInputSkyfileRow *diffInputSkyfileObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
-        return false;
-    }
-    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
-        return false;
-    }
-    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
-        return false;
-    }
-    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
-        return false;
-    }
-    char* kind = psMetadataLookupPtr(&status, md, "kind");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item kind");
-        return false;
-    }
-    bool template = psMetadataLookupBool(&status, md, "template");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item template");
-        return false;
-    }
-
-    return diffInputSkyfileRowAlloc(diff_id, warp_id, skycell_id, tess_id, kind, template);
-}
-psArray *diffInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_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++) {
-        diffInputSkyfileRow *object = diffInputSkyfileObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool diffInputSkyfileDeleteObject(psDB *dbh, const diffInputSkyfileRow *object)
-{
-    psMetadata *where = diffInputSkyfileMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
-        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, "diffInputSkyfileRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long diffInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        diffInputSkyfileRow *object = objects->data[i];
-        psMetadata *where = diffInputSkyfileMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool diffInputSkyfilePrintObjects(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 = diffInputSkyfileMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            DIFFINPUTSKYFILE_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;
-}
-bool diffInputSkyfilePrintObject(FILE *stream, diffInputSkyfileRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = diffInputSkyfileMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void diffSkyfileRowFree(diffSkyfileRow *object);
-
-diffSkyfileRow *diffSkyfileRowAlloc(psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
-{
-    diffSkyfileRow  *_object;
-
-    _object = psAlloc(sizeof(diffSkyfileRow));
-    psMemSetDeallocator(_object, (psFreeFunc)diffSkyfileRowFree);
-
-    _object->diff_id = diff_id;
-    _object->uri = psStringCopy(uri);
-    _object->bg = bg;
-    _object->bg_stdev = bg_stdev;
-
-    return _object;
-}
-
-static void diffSkyfileRowFree(diffSkyfileRow *object)
-{
-    psFree(object->uri);
-}
-
-bool diffSkyfileCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, DIFFSKYFILE_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool diffSkyfileDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, DIFFSKYFILE_TABLE_NAME);
-}
-
-bool diffSkyfileInsert(psDB * dbh, psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, DIFFSKYFILE_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long diffSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool diffSkyfileInsertObject(psDB *dbh, diffSkyfileRow *object)
-{
-    return diffSkyfileInsert(dbh, object->diff_id, object->uri, object->bg, object->bg_stdev);
-}
-
-bool diffSkyfileInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!diffSkyfileInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool diffSkyfileInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  DIFFSKYFILE_TABLE_NAME
-    if (!psFitsMoveExtName(fits, DIFFSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFSKYFILE_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, DIFFSKYFILE_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool diffSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *diffSkyfileMetadataFromObject(const diffSkyfileRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-diffSkyfileRow *diffSkyfileObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_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;
-    }
-    psF64 bg = psMetadataLookupF64(&status, md, "bg");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
-        return false;
-    }
-    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
-        return false;
-    }
-
-    return diffSkyfileRowAlloc(diff_id, uri, bg, bg_stdev);
-}
-psArray *diffSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_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++) {
-        diffSkyfileRow *object = diffSkyfileObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool diffSkyfileDeleteObject(psDB *dbh, const diffSkyfileRow *object)
-{
-    psMetadata *where = diffSkyfileMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
-        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, "diffSkyfileRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long diffSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        diffSkyfileRow *object = objects->data[i];
-        psMetadata *where = diffSkyfileMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool diffSkyfilePrintObjects(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 = diffSkyfileMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            DIFFSKYFILE_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;
-}
-bool diffSkyfilePrintObject(FILE *stream, diffSkyfileRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = diffSkyfileMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void stackRunRowFree(stackRunRow *object);
-
-stackRunRow *stackRunRowAlloc(psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
-{
-    stackRunRow     *_object;
-
-    _object = psAlloc(sizeof(stackRunRow));
-    psMemSetDeallocator(_object, (psFreeFunc)stackRunRowFree);
-
-    _object->stack_id = stack_id;
-    _object->state = psStringCopy(state);
-    _object->workdir = psStringCopy(workdir);
-    _object->dvodb = psStringCopy(dvodb);
-    _object->registered = psTimeCopy(registered);
-    _object->skycell_id = psStringCopy(skycell_id);
-    _object->tess_id = psStringCopy(tess_id);
-
-    return _object;
-}
-
-static void stackRunRowFree(stackRunRow *object)
-{
-    psFree(object->state);
-    psFree(object->workdir);
-    psFree(object->dvodb);
-    psFree(object->registered);
-    psFree(object->skycell_id);
-    psFree(object->tess_id);
-}
-
-bool stackRunCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, STACKRUN_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool stackRunDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, STACKRUN_TABLE_NAME);
-}
-
-bool stackRunInsert(psDB * dbh, psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, STACKRUN_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long stackRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool stackRunInsertObject(psDB *dbh, stackRunRow *object)
-{
-    return stackRunInsert(dbh, object->stack_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
-}
-
-bool stackRunInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!stackRunInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool stackRunInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  STACKRUN_TABLE_NAME
-    if (!psFitsMoveExtName(fits, STACKRUN_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKRUN_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, STACKRUN_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool stackRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, STACKRUN_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *stackRunMetadataFromObject(const stackRunRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-stackRunRow *stackRunObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
-        return false;
-    }
-    char* state = psMetadataLookupPtr(&status, md, "state");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
-        return false;
-    }
-    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
-        return false;
-    }
-    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
-        return false;
-    }
-    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
-        return false;
-    }
-    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
-        return false;
-    }
-    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
-        return false;
-    }
-
-    return stackRunRowAlloc(stack_id, state, workdir, dvodb, registered, skycell_id, tess_id);
-}
-psArray *stackRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, STACKRUN_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++) {
-        stackRunRow *object = stackRunObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool stackRunDeleteObject(psDB *dbh, const stackRunRow *object)
-{
-    psMetadata *where = stackRunMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
-        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, "stackRunRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long stackRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        stackRunRow *object = objects->data[i];
-        psMetadata *where = stackRunMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool stackRunPrintObjects(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 = stackRunMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            STACKRUN_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;
-}
-bool stackRunPrintObject(FILE *stream, stackRunRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = stackRunMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void stackInputSkyfileRowFree(stackInputSkyfileRow *object);
-
-stackInputSkyfileRow *stackInputSkyfileRowAlloc(psS64 stack_id, psS64 warp_id)
-{
-    stackInputSkyfileRow *_object;
-
-    _object = psAlloc(sizeof(stackInputSkyfileRow));
-    psMemSetDeallocator(_object, (psFreeFunc)stackInputSkyfileRowFree);
-
-    _object->stack_id = stack_id;
-    _object->warp_id = warp_id;
-
-    return _object;
-}
-
-static void stackInputSkyfileRowFree(stackInputSkyfileRow *object)
-{
-}
-
-bool stackInputSkyfileCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool stackInputSkyfileDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, STACKINPUTSKYFILE_TABLE_NAME);
-}
-
-bool stackInputSkyfileInsert(psDB * dbh, psS64 stack_id, psS64 warp_id)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long stackInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool stackInputSkyfileInsertObject(psDB *dbh, stackInputSkyfileRow *object)
-{
-    return stackInputSkyfileInsert(dbh, object->stack_id, object->warp_id);
-}
-
-bool stackInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!stackInputSkyfileInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool stackInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  STACKINPUTSKYFILE_TABLE_NAME
-    if (!psFitsMoveExtName(fits, STACKINPUTSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKINPUTSKYFILE_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, STACKINPUTSKYFILE_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool stackInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, STACKINPUTSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *stackInputSkyfileMetadataFromObject(const stackInputSkyfileRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-stackInputSkyfileRow *stackInputSkyfileObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
-        return false;
-    }
-    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
-        return false;
-    }
-
-    return stackInputSkyfileRowAlloc(stack_id, warp_id);
-}
-psArray *stackInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_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++) {
-        stackInputSkyfileRow *object = stackInputSkyfileObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool stackInputSkyfileDeleteObject(psDB *dbh, const stackInputSkyfileRow *object)
-{
-    psMetadata *where = stackInputSkyfileMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
-        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, "stackInputSkyfileRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long stackInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        stackInputSkyfileRow *object = objects->data[i];
-        psMetadata *where = stackInputSkyfileMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool stackInputSkyfilePrintObjects(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 = stackInputSkyfileMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            STACKINPUTSKYFILE_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;
-}
-bool stackInputSkyfilePrintObject(FILE *stream, stackInputSkyfileRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = stackInputSkyfileMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
-static void stackSumSkyfileRowFree(stackSumSkyfileRow *object);
-
-stackSumSkyfileRow *stackSumSkyfileRowAlloc(psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
-{
-    stackSumSkyfileRow *_object;
-
-    _object = psAlloc(sizeof(stackSumSkyfileRow));
-    psMemSetDeallocator(_object, (psFreeFunc)stackSumSkyfileRowFree);
-
-    _object->stack_id = stack_id;
-    _object->uri = psStringCopy(uri);
-    _object->bg = bg;
-    _object->bg_stdev = bg_stdev;
-
-    return _object;
-}
-
-static void stackSumSkyfileRowFree(stackSumSkyfileRow *object)
-{
-    psFree(object->uri);
-}
-
-bool stackSumSkyfileCreateTable(psDB *dbh)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key", 0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBCreateTable(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
-
-    psFree(md);
-
-    return status;
-}
-
-bool stackSumSkyfileDropTable(psDB *dbh)
-{
-    return psDBDropTable(dbh, STACKSUMSKYFILE_TABLE_NAME);
-}
-
-bool stackSumSkyfileInsert(psDB * dbh, psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-    bool status = psDBInsertOneRow(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
-    psFree(md);
-
-    return status;
-}
-
-long long stackSumSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
-        return count;
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool stackSumSkyfileInsertObject(psDB *dbh, stackSumSkyfileRow *object)
-{
-    return stackSumSkyfileInsert(dbh, object->stack_id, object->uri, object->bg, object->bg_stdev);
-}
-
-bool stackSumSkyfileInsertObjects(psDB *dbh, psArray *objects)
-{
-    for (long i = 0; i < psArrayLength(objects); i++) {
-        if (!stackSumSkyfileInsertObject(dbh, objects->data[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool stackSumSkyfileInsertFits(psDB *dbh, const psFits *fits)
-{
-    psArray         *rowSet;
-
-    // move to (the first?) extension named  STACKSUMSKYFILE_TABLE_NAME
-    if (!psFitsMoveExtName(fits, STACKSUMSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKSUMSKYFILE_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, STACKSUMSKYFILE_TABLE_NAME, rowSet)) {
-        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-bool stackSumSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-
-    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
-    if (!rowSet) {
-        return false;
-    }
-
-    // output to fits
-    if (!psFitsWriteTable(fits, NULL, rowSet, STACKSUMSKYFILE_TABLE_NAME)) {
-        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
-        psFree(rowSet);
-        return false;
-    }
-
-    psFree(rowSet);
-
-    return true;
-}
-
-psMetadata *stackSumSkyfileMetadataFromObject(const stackSumSkyfileRow *object)
-{
-    psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_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;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
-        psFree(md);
-        return false;
-    }
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
-        psFree(md);
-        return false;
-    }
-
-
-    return md;
-}
-
-stackSumSkyfileRow *stackSumSkyfileObjectFromMetadata(psMetadata *md)
-{
-
-bool status = false;
-    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_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;
-    }
-    psF64 bg = psMetadataLookupF64(&status, md, "bg");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
-        return false;
-    }
-    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
-    if (!status) {
-        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
-        return false;
-    }
-
-    return stackSumSkyfileRowAlloc(stack_id, uri, bg, bg_stdev);
-}
-psArray *stackSumSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
-{
-    psArray         *rowSet;
-    psArray         *returnSet;
-    psU64           i;
-
-    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_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++) {
-        stackSumSkyfileRow *object = stackSumSkyfileObjectFromMetadata(rowSet->data[i]);
-        psArrayAdd(returnSet, 0, object);
-        psFree(object);
-    }
-
-    psFree(rowSet);
-
-    return returnSet;
-}
-bool stackSumSkyfileDeleteObject(psDB *dbh, const stackSumSkyfileRow *object)
-{
-    psMetadata *where = stackSumSkyfileMetadataFromObject(object);
-    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, 0);
-    psFree(where);
-    if (count < 0) {
-        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
-        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, "stackSumSkyfileRow object matched more then one row.  Check your database schema");
-        return false;
-    }
-
-    return true;
-}
-long long stackSumSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
-{
-    long long       deleted = 0;
-
-    for (long long i = 0; i < objects->n; i++) {
-        stackSumSkyfileRow *object = objects->data[i];
-        psMetadata *where = stackSumSkyfileMetadataFromObject(object);
-        long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
-        psFree(where);
-        if (count < 0) {
-            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
-            return count;
-        }
-
-        deleted += count;
-    }
-
-    return deleted;
-}
-bool stackSumSkyfilePrintObjects(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 = stackSumSkyfileMetadataFromObject(objects->data[i]);
-        if (!psMetadataAddMetadata(
-            output,
-            PS_LIST_TAIL,
-            STACKSUMSKYFILE_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;
-}
-bool stackSumSkyfilePrintObject(FILE *stream, stackSumSkyfileRow *object, bool mdcf)
-{
-    PS_ASSERT_PTR_NON_NULL(object, false);
-
-    psMetadata *md = stackSumSkyfileMetadataFromObject(object);
-
-    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
-        psFree(md);
-    }
-
-    psFree(md);
-
-    return true;
-}
