Index: /trunk/ippdb/Makefile.am
===================================================================
--- /trunk/ippdb/Makefile.am	(revision 15568)
+++ /trunk/ippdb/Makefile.am	(revision 15569)
@@ -57,8 +57,10 @@
     $(top_builddir)/docs/man/man3/magicNodeResultRow.3 \
     $(top_builddir)/docs/man/man3/magicMaskRow.3 \
-    $(top_builddir)/docs/man/man3/magicSkyfileMaskRow.3 
+    $(top_builddir)/docs/man/man3/magicSkyfileMaskRow.3 \
+    $(top_builddir)/docs/man/man3/calDBRow.3 \
+    $(top_builddir)/docs/man/man3/calRunRow.3 
 
 
-docs/man/man3/ippdb.3 docs/man/man3/pzDataStoreRow.3 docs/man/man3/summitExpRow.3 docs/man/man3/summitImfileRow.3 docs/man/man3/pzPendingExpRow.3 docs/man/man3/pzPendingImfileRow.3 docs/man/man3/pzDoneExpRow.3 docs/man/man3/pzDoneImfileRow.3 docs/man/man3/newExpRow.3 docs/man/man3/newImfileRow.3 docs/man/man3/rawExpRow.3 docs/man/man3/rawImfileRow.3 docs/man/man3/guidePendingExpRow.3 docs/man/man3/chipRunRow.3 docs/man/man3/chipProcessedImfileRow.3 docs/man/man3/chipMaskRow.3 docs/man/man3/camRunRow.3 docs/man/man3/camProcessedExpRow.3 docs/man/man3/camMaskRow.3 docs/man/man3/warpRunRow.3 docs/man/man3/warpInputExpRow.3 docs/man/man3/warpSkyCellMapRow.3 docs/man/man3/warpSkyfileRow.3 docs/man/man3/diffRunRow.3 docs/man/man3/diffInputSkyfileRow.3 docs/man/man3/diffSkyfileRow.3 docs/man/man3/stackRunRow.3 docs/man/man3/stackInputSkyfileRow.3 docs/man/man3/stackSumSkyfileRow.3 docs/man/man3/detRunRow.3 docs/man/man3/detInputExpRow.3 docs/man/man3/detProcessedImfileRow.3 docs/man/man3/detProcessedExpRow.3 docs/man/man3/detStackedImfileRow.3 docs/man/man3/detNormalizedStatImfileRow.3 docs/man/man3/detNormalizedImfileRow.3 docs/man/man3/detNormalizedExpRow.3 docs/man/man3/detResidImfileRow.3 docs/man/man3/detResidExpRow.3 docs/man/man3/detRunSummaryRow.3 docs/man/man3/detRegisteredImfileRow.3 docs/man/man3/detCorrectedExpRow.3 docs/man/man3/detCorrectedImfileRow.3 docs/man/man3/magicRunRow.3 docs/man/man3/magicInputSkyfileRow.3 docs/man/man3/magicTreeRow.3 docs/man/man3/magicNodeResultRow.3 docs/man/man3/magicMaskRow.3 docs/man/man3/magicSkyfileMaskRow.3:
+docs/man/man3/ippdb.3 docs/man/man3/pzDataStoreRow.3 docs/man/man3/summitExpRow.3 docs/man/man3/summitImfileRow.3 docs/man/man3/pzPendingExpRow.3 docs/man/man3/pzPendingImfileRow.3 docs/man/man3/pzDoneExpRow.3 docs/man/man3/pzDoneImfileRow.3 docs/man/man3/newExpRow.3 docs/man/man3/newImfileRow.3 docs/man/man3/rawExpRow.3 docs/man/man3/rawImfileRow.3 docs/man/man3/guidePendingExpRow.3 docs/man/man3/chipRunRow.3 docs/man/man3/chipProcessedImfileRow.3 docs/man/man3/chipMaskRow.3 docs/man/man3/camRunRow.3 docs/man/man3/camProcessedExpRow.3 docs/man/man3/camMaskRow.3 docs/man/man3/warpRunRow.3 docs/man/man3/warpInputExpRow.3 docs/man/man3/warpSkyCellMapRow.3 docs/man/man3/warpSkyfileRow.3 docs/man/man3/diffRunRow.3 docs/man/man3/diffInputSkyfileRow.3 docs/man/man3/diffSkyfileRow.3 docs/man/man3/stackRunRow.3 docs/man/man3/stackInputSkyfileRow.3 docs/man/man3/stackSumSkyfileRow.3 docs/man/man3/detRunRow.3 docs/man/man3/detInputExpRow.3 docs/man/man3/detProcessedImfileRow.3 docs/man/man3/detProcessedExpRow.3 docs/man/man3/detStackedImfileRow.3 docs/man/man3/detNormalizedStatImfileRow.3 docs/man/man3/detNormalizedImfileRow.3 docs/man/man3/detNormalizedExpRow.3 docs/man/man3/detResidImfileRow.3 docs/man/man3/detResidExpRow.3 docs/man/man3/detRunSummaryRow.3 docs/man/man3/detRegisteredImfileRow.3 docs/man/man3/detCorrectedExpRow.3 docs/man/man3/detCorrectedImfileRow.3 docs/man/man3/magicRunRow.3 docs/man/man3/magicInputSkyfileRow.3 docs/man/man3/magicTreeRow.3 docs/man/man3/magicNodeResultRow.3 docs/man/man3/magicMaskRow.3 docs/man/man3/magicSkyfileMaskRow.3 docs/man/man3/calDBRow.3 docs/man/man3/calRunRow.3:
 	$(DOXYGEN)
 
Index: /trunk/ippdb/src/ippdb.c
===================================================================
--- /trunk/ippdb/src/ippdb.c	(revision 15568)
+++ /trunk/ippdb/src/ippdb.c	(revision 15569)
@@ -79,4 +79,6 @@
 #define MAGICMASK_TABLE_NAME "magicMask"
 #define MAGICSKYFILEMASK_TABLE_NAME "magicSkyfileMask"
+#define CALDB_TABLE_NAME "calDB"
+#define CALRUN_TABLE_NAME "calRun"
 #define MAX_STRING_LENGTH 1024
 
@@ -22042,2 +22044,658 @@
     return true;
 }
+static void calDBRowFree(calDBRow *object);
+
+calDBRow *calDBRowAlloc(psS64 cal_id, const char *catdir, const char *state)
+{
+    calDBRow        *_object;
+
+    _object = psAlloc(sizeof(calDBRow));
+    psMemSetDeallocator(_object, (psFreeFunc)calDBRowFree);
+
+    _object->cal_id = cal_id;
+    _object->catdir = psStringCopy(catdir);
+    _object->state = psStringCopy(state);
+
+    return _object;
+}
+
+static void calDBRowFree(calDBRow *object)
+{
+    psFree(object->catdir);
+    psFree(object->state);
+}
+
+bool calDBCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "catdir", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item catdir");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CALDB_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool calDBDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CALDB_TABLE_NAME);
+}
+
+bool calDBInsert(psDB * dbh, psS64 cal_id, const char *catdir, const char *state)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "catdir", PS_DATA_STRING, NULL, catdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item catdir");
+        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;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CALDB_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long calDBDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CALDB_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calDB");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calDBInsertObject(psDB *dbh, calDBRow *object)
+{
+    return calDBInsert(dbh, object->cal_id, object->catdir, object->state);
+}
+
+bool calDBInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!calDBInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool calDBInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CALDB_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CALDB_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CALDB_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, CALDB_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool calDBSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CALDB_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CALDB_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *calDBMetadataFromObject(const calDBRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, object->cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "catdir", PS_DATA_STRING, NULL, object->catdir)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item catdir");
+        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;
+    }
+
+
+    return md;
+}
+
+calDBRow *calDBObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 cal_id = psMetadataLookupS64(&status, md, "cal_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cal_id");
+        return false;
+    }
+    char* catdir = psMetadataLookupPtr(&status, md, "catdir");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item catdir");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+
+    return calDBRowAlloc(cal_id, catdir, state);
+}
+psArray *calDBSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CALDB_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++) {
+        calDBRow *object = calDBObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool calDBDeleteObject(psDB *dbh, const calDBRow *object)
+{
+    psMetadata *where = calDBMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CALDB_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calDB");
+        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, "calDBRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long calDBDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        calDBRow *object = objects->data[i];
+        psMetadata *where = calDBMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CALDB_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from calDB");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calDBPrintObjects(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 = calDBMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CALDB_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 calDBPrintObject(FILE *stream, calDBRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = calDBMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
+static void calRunRowFree(calRunRow *object);
+
+calRunRow *calRunRowAlloc(psS64 cal_id, const char *region, const char *last_step, const char *state)
+{
+    calRunRow       *_object;
+
+    _object = psAlloc(sizeof(calRunRow));
+    psMemSetDeallocator(_object, (psFreeFunc)calRunRowFree);
+
+    _object->cal_id = cal_id;
+    _object->region = psStringCopy(region);
+    _object->last_step = psStringCopy(last_step);
+    _object->state = psStringCopy(state);
+
+    return _object;
+}
+
+static void calRunRowFree(calRunRow *object)
+{
+    psFree(object->region);
+    psFree(object->last_step);
+    psFree(object->state);
+}
+
+bool calRunCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, "success or failure", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "last_step", PS_DATA_STRING, "fkey(cal_id) ref calDB(cal_id)", "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item last_step");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, "64")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, CALRUN_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool calRunDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, CALRUN_TABLE_NAME);
+}
+
+bool calRunInsert(psDB * dbh, psS64 cal_id, const char *region, const char *last_step, const char *state)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, region)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "last_step", PS_DATA_STRING, NULL, last_step)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item last_step");
+        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;
+    }
+
+    bool status = psDBInsertOneRow(dbh, CALRUN_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long calRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, CALRUN_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calRun");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calRunInsertObject(psDB *dbh, calRunRow *object)
+{
+    return calRunInsert(dbh, object->cal_id, object->region, object->last_step, object->state);
+}
+
+bool calRunInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!calRunInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool calRunInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  CALRUN_TABLE_NAME
+    if (!psFitsMoveExtName(fits, CALRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", CALRUN_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, CALRUN_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool calRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, CALRUN_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, CALRUN_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *calRunMetadataFromObject(const calRunRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "cal_id", PS_DATA_S64, NULL, object->cal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item cal_id");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "region", PS_DATA_STRING, NULL, object->region)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item region");
+        psFree(md);
+        return false;
+    }
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "last_step", PS_DATA_STRING, NULL, object->last_step)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item last_step");
+        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;
+    }
+
+
+    return md;
+}
+
+calRunRow *calRunObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psS64 cal_id = psMetadataLookupS64(&status, md, "cal_id");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cal_id");
+        return false;
+    }
+    char* region = psMetadataLookupPtr(&status, md, "region");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item region");
+        return false;
+    }
+    char* last_step = psMetadataLookupPtr(&status, md, "last_step");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item last_step");
+        return false;
+    }
+    char* state = psMetadataLookupPtr(&status, md, "state");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
+        return false;
+    }
+
+    return calRunRowAlloc(cal_id, region, last_step, state);
+}
+psArray *calRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, CALRUN_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++) {
+        calRunRow *object = calRunObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool calRunDeleteObject(psDB *dbh, const calRunRow *object)
+{
+    psMetadata *where = calRunMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, CALRUN_TABLE_NAME, where, 0);
+    psFree(where);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from calRun");
+        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, "calRunRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long calRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        calRunRow *object = objects->data[i];
+        psMetadata *where = calRunMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, CALRUN_TABLE_NAME, where, limit);
+        psFree(where);
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from calRun");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool calRunPrintObjects(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 = calRunMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            CALRUN_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 calRunPrintObject(FILE *stream, calRunRow *object, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(object, false);
+
+    psMetadata *md = calRunMetadataFromObject(object);
+
+    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(md);
+    }
+
+    psFree(md);
+
+    return true;
+}
Index: /trunk/ippdb/src/ippdb.h
===================================================================
--- /trunk/ippdb/src/ippdb.h	(revision 15568)
+++ /trunk/ippdb/src/ippdb.h	(revision 15569)
@@ -10700,4 +10700,409 @@
     bool            mdcf                ///< format as mdconfig or simple
 );
+/** calDBRow data structure
+ *
+ * Structure for representing a single row of calDB table data.
+ */
+
+typedef struct {
+    psS64           cal_id;
+    char            *catdir;
+    char            *state;
+} calDBRow;
+
+/** Creates a new calDBRow object
+ *
+ *  @return A new calDBRow object or NULL on failure.
+ */
+
+calDBRow *calDBRowAlloc(
+    psS64           cal_id,
+    const char      *catdir,
+    const char      *state
+);
+
+/** Creates a new calDB table
+ *
+ * @return true on success
+ */
+
+bool calDBCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a calDB table
+ *
+ * @return true on success
+ */
+
+bool calDBDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calDBInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           cal_id,
+    const char      *catdir,
+    const char      *state
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calDBDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single calDBRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calDBInsertObject(
+    psDB            *dbh,               ///< Database handle
+    calDBRow        *object             ///< calDBRow object
+);
+
+/** Insert an array of calDBRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calDBInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of calDBRow objects
+);
+
+/** Insert data from a binary FITS table calDBRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool calDBInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool calDBSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a calDBRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *calDBMetadataFromObject(
+    const calDBRow  *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A calDBRow pointer or NULL on error
+ */
+
+calDBRow *calDBObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as calDBRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *calDBSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an calDB
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool calDBDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const calDBRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calDBDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of calDBRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calDBPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of calDBRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an calDBRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calDBPrintObject(
+    FILE            *stream,            ///< a stream
+    calDBRow *object,    ///< an calDBRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** calRunRow data structure
+ *
+ * Structure for representing a single row of calRun table data.
+ */
+
+typedef struct {
+    psS64           cal_id;
+    char            *region;
+    char            *last_step;
+    char            *state;
+} calRunRow;
+
+/** Creates a new calRunRow object
+ *
+ *  @return A new calRunRow object or NULL on failure.
+ */
+
+calRunRow *calRunRowAlloc(
+    psS64           cal_id,
+    const char      *region,
+    const char      *last_step,
+    const char      *state
+);
+
+/** Creates a new calRun table
+ *
+ * @return true on success
+ */
+
+bool calRunCreateTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Deletes a calRun table
+ *
+ * @return true on success
+ */
+
+bool calRunDropTable(
+    psDB            *dbh                ///< Database handle
+);
+
+/** Insert a single row into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calRunInsert(
+    psDB            *dbh,               ///< Database handle
+    psS64           cal_id,
+    const char      *region,
+    const char      *last_step,
+    const char      *state
+);
+
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calRunDelete(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+
+/** Insert a single calRunRow object into a table
+ *
+ * This function constructs and inserts a single row based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calRunInsertObject(
+    psDB            *dbh,               ///< Database handle
+    calRunRow       *object             ///< calRunRow object
+);
+
+/** Insert an array of calRunRow object into a table
+ *
+ * This function constructs and inserts multiple rows based on it's parameters.
+ *
+ * @return true on success
+ */
+
+bool calRunInsertObjects(
+    psDB            *dbh,               ///< Database handle
+    psArray         *objects            ///< array of calRunRow objects
+);
+
+/** Insert data from a binary FITS table calRunRow into the database
+ *
+ * This function expects a psFits object with a FITS table as the first
+ * extension.  The table must have at least one row of data in it, that is of
+ * the appropriate format (number of columns and their type).  All other
+ * extensions are ignored.
+ *
+ * @return true on success
+ */
+
+bool calRunInsertFits(
+    psDB            *dbh,               ///< Database handle
+    const psFits    *fits               ///< psFits object
+);
+
+/** Selects up to limit from the database and returns them in a binary FITS table
+ *
+ * This function assumes an empty psFits object and will create a FITS table
+ * as the first extension.
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return true on success
+ */
+
+bool calRunSelectRowsFits(
+    psDB            *dbh,               ///< Database handle
+    psFits          *fits,              ///< psFits object
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+
+/** Convert a calRunRow into an equivalent psMetadata
+ *
+ * @return A psMetadata pointer or NULL on error
+ */
+
+psMetadata *calRunMetadataFromObject(
+    const calRunRow *object             ///< fooRow to convert into a psMetadata
+);
+
+/** Convert a psMetadata into an equivalent fooRow
+ *
+ * @return A calRunRow pointer or NULL on error
+ */
+
+calRunRow *calRunObjectFromMetadata(
+    psMetadata      *md                 ///< psMetadata to convert into a fooRow
+);
+/** Selects up to limit rows from the database and returns as calRunRow objects in a psArray
+ *
+ *  See psDBSelectRows() for documentation on the format of where.
+ *
+ * @return A psArray pointer or NULL on error
+ */
+
+psArray *calRunSelectRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psMetadata *where,            ///< Row match criteria
+    unsigned long long limit            ///< Maximum number of elements to return
+);
+/** Deletes a row from the database coresponding to an calRun
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+bool calRunDeleteObject(
+    psDB            *dbh,               ///< Database handle
+    const calRunRow *object    ///< Object to delete
+);
+/** Deletes up to limit rows from the database and returns the number of rows actually deleted. 
+ *
+ *  Note that a 'where' search psMetadata is constructed from each object and
+ *  used to find rows to delete.
+ *
+ * @return A The number of rows removed or a negative value on error
+ */
+
+long long calRunDeleteRowObjects(
+    psDB            *dbh,               ///< Database handle
+    const psArray   *objects,           ///< Array of objects to delete
+    unsigned long long limit            ///< Maximum number of elements to delete 
+);
+/** Formats and prints an array of calRunRow objects
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calRunPrintObjects(
+    FILE            *stream,            ///< a stream
+    psArray         *objects,           ///< An array of calRunRow objects
+    bool            mdcf                ///< format as mdconfig or simple
+);
+/** Formats and prints an calRunRow object
+ *
+ * When mdcf is set the formated output is in psMetadataConfig
+ * format, otherwise it is in a simple tabular format.
+ *
+ * @return true on success
+ */
+
+bool calRunPrintObject(
+    FILE            *stream,            ///< a stream
+    calRunRow *object,    ///< an calRunRow object
+    bool            mdcf                ///< format as mdconfig or simple
+);
 
 /// @}
@@ -10707,3 +11112,3 @@
 #endif
 
-#endif // MAGICSKYFILEMASK_DB_H
+#endif // CALRUN_DB_H
Index: /trunk/ippdb/tests/alloc.c
===================================================================
--- /trunk/ippdb/tests/alloc.c	(revision 15568)
+++ /trunk/ippdb/tests/alloc.c	(revision 15569)
@@ -2432,4 +2432,58 @@
     }
 
+    {
+        calDBRow        *object;
+
+        object = calDBRowAlloc(-64, "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->cal_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->catdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        calRunRow       *object;
+
+        object = calRunRowAlloc(-64, "a string", "a string", "a string"    );
+
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!object->cal_id == -64) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->region, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->last_step, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/createtable.c
===================================================================
--- /trunk/ippdb/tests/createtable.c	(revision 15568)
+++ /trunk/ippdb/tests/createtable.c	(revision 15569)
@@ -725,4 +725,34 @@
     }
 
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!calDBCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if(!calRunCreateTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/dbcleanup.c
===================================================================
--- /trunk/ippdb/tests/dbcleanup.c	(revision 15568)
+++ /trunk/ippdb/tests/dbcleanup.c	(revision 15569)
@@ -59,4 +59,6 @@
     p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicMask");
     p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS magicSkyfileMask");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calDB");
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calRun");
 
     psDBCleanup(dbh);
Index: /trunk/ippdb/tests/dbsetup.c
===================================================================
--- /trunk/ippdb/tests/dbsetup.c	(revision 15568)
+++ /trunk/ippdb/tests/dbsetup.c	(revision 15569)
@@ -157,4 +157,10 @@
     magicSkyfileMaskCreateTable(dbh);
 
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calDB");
+    calDBCreateTable(dbh);
+
+    p_psDBRunQuery(dbh, "DROP TABLE IF EXISTS calRun");
+    calRunCreateTable(dbh);
+
     psDBCleanup(dbh);
 
Index: /trunk/ippdb/tests/droptable.c
===================================================================
--- /trunk/ippdb/tests/droptable.c	(revision 15568)
+++ /trunk/ippdb/tests/droptable.c	(revision 15569)
@@ -725,4 +725,34 @@
     }
 
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunDropTable(dbh)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/insert.c
===================================================================
--- /trunk/ippdb/tests/insert.c	(revision 15568)
+++ /trunk/ippdb/tests/insert.c	(revision 15569)
@@ -725,4 +725,34 @@
     }
 
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBInsert(dbh, -64, "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunInsert(dbh, -64, "a string", "a string", "a string")) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/insertfits.c
===================================================================
--- /trunk/ippdb/tests/insertfits.c	(revision 15568)
+++ /trunk/ippdb/tests/insertfits.c	(revision 15569)
@@ -1257,4 +1257,56 @@
     }
 
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        // open a temp
+        fits = psFitsOpen(TMP_FILENAME, "r");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunInsertFits(dbh, fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!psFitsClose(fits)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psDBCleanup(dbh);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/insertobject.c
===================================================================
--- /trunk/ippdb/tests/insertobject.c	(revision 15568)
+++ /trunk/ippdb/tests/insertobject.c	(revision 15569)
@@ -1061,4 +1061,48 @@
     }
 
+    {
+        psDB            *dbh;
+        calDBRow        *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = calDBRowAlloc(-64, "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        calRunRow       *object;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        object = calRunRowAlloc(-64, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunInsertObject(dbh, object)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+        psDBCleanup(dbh);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/metadatafromobject.c
===================================================================
--- /trunk/ippdb/tests/metadatafromobject.c	(revision 15568)
+++ /trunk/ippdb/tests/metadatafromobject.c	(revision 15569)
@@ -2737,4 +2737,72 @@
     }
 
+    {
+        psMetadata      *md;
+        calDBRow        *object;
+        bool            status;
+
+        object = calDBRowAlloc(-64, "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = calDBMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "catdir"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
+    {
+        psMetadata      *md;
+        calRunRow       *object;
+        bool            status;
+
+        object = calRunRowAlloc(-64, "a string", "a string", "a string");
+        if (!object) {
+            exit(EXIT_FAILURE);
+        }
+
+        md = calRunMetadataFromObject(object);
+        if (!md) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "region"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "last_step"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(psMetadataLookupPtr(&status, md, "state"), "a string", MAX_STRING_LENGTH)) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/objectfrommetadata.c
===================================================================
--- /trunk/ippdb/tests/objectfrommetadata.c	(revision 15568)
+++ /trunk/ippdb/tests/objectfrommetadata.c	(revision 15569)
@@ -4314,4 +4314,92 @@
     }
 
+    {
+        psMetadata      *md;
+        calDBRow        *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "catdir", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = calDBObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->catdir, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
+    {
+        psMetadata      *md;
+        calRunRow       *object;
+
+        md = psMetadataAlloc();
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "region", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "last_step", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+        if (!psMetadataAddStr(md, PS_LIST_TAIL, "state", 0, NULL, "a string")) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        object = calRunObjectFromMetadata(md);
+        if (!object) {
+            psFree(md);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(md);
+
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->region, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->last_step, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+        if (strncmp(object->state, "a string", MAX_STRING_LENGTH)) {
+            psFree(object);
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(object);
+    }
+
     exit(EXIT_SUCCESS);
 }
Index: /trunk/ippdb/tests/selectrowsfits.c
===================================================================
--- /trunk/ippdb/tests/selectrowsfits.c	(revision 15568)
+++ /trunk/ippdb/tests/selectrowsfits.c	(revision 15569)
@@ -1063,4 +1063,48 @@
     }
 
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calDBSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
+    {
+        psDB            *dbh;
+        psFits          *fits;
+
+        dbh = psDBInit("localhost", "test", NULL, "test");
+        if (!dbh) {
+            exit(EXIT_FAILURE);
+        }
+
+        fits = psFitsOpen(TMP_FILENAME, "w");
+        if (!fits) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (!calRunSelectRowsFits(dbh, fits, NULL, 1)) {
+            exit(EXIT_FAILURE);
+        }
+
+        psFree(fits);
+        psDBCleanup(dbh);
+    }
+
     exit(EXIT_SUCCESS);
 }
