Index: trunk/ippdb/src/ippdb.c
===================================================================
--- trunk/ippdb/src/ippdb.c	(revision 15533)
+++ 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;
+}
