IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Mar 21, 2007, 5:14:51 PM (19 years ago)
Author:
jhoblitt
Message:

VERSION 1.1.19

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ippdb/src/ippdb.c

    r12426 r12535  
    2020/*
    2121 *
    22  * This file was generated by glueforge 1.00
     22 * This file was generated by glueforge 1.01
    2323 *
    2424 * Do NOT directly edit this file.
     
    5151#define CAMPROCESSEDEXP_TABLE_NAME "camProcessedExp"
    5252#define CAMMASK_TABLE_NAME "camMask"
     53#define WARPRUN_TABLE_NAME "warpRun"
     54#define WARPINPUTEXP_TABLE_NAME "warpInputExp"
     55#define WARPSKYCELLMAP_TABLE_NAME "warpSkyCellMap"
     56#define WARPSKYFILE_TABLE_NAME "warpSkyfile"
     57#define DIFFRUN_TABLE_NAME "diffRun"
     58#define DIFFINPUTSKYFILE_TABLE_NAME "diffInputSkyfile"
     59#define DIFFSKYFILE_TABLE_NAME "diffSkyfile"
     60#define STACKRUN_TABLE_NAME "stackRun"
     61#define STACKINPUTSKYFILE_TABLE_NAME "stackInputSkyfile"
     62#define STACKSUMSKYFILE_TABLE_NAME "stackSumSkyfile"
    5363#define DETRUN_TABLE_NAME "detRun"
    5464#define DETINPUTEXP_TABLE_NAME "detInputExp"
     
    6272#define DETRESIDEXP_TABLE_NAME "detResidExp"
    6373#define DETRUNSUMMARY_TABLE_NAME "detRunSummary"
    64 #define WARPRUN_TABLE_NAME "warpRun"
    65 #define WARPINPUTEXP_TABLE_NAME "warpInputExp"
    66 #define WARPSKYCELLMAP_TABLE_NAME "warpSkyCellMap"
    67 #define WARPSKYFILE_TABLE_NAME "warpSkyfile"
    68 #define DIFFRUN_TABLE_NAME "diffRun"
    69 #define DIFFINPUTSKYFILE_TABLE_NAME "diffInputSkyfile"
    70 #define DIFFSKYFILE_TABLE_NAME "diffSkyfile"
    71 #define STACKRUN_TABLE_NAME "stackRun"
    72 #define STACKINPUTSKYFILE_TABLE_NAME "stackInputSkyfile"
    73 #define STACKSUMSKYFILE_TABLE_NAME "stackSumSkyfile"
    7474#define MAX_STRING_LENGTH 1024
    7575
     
    71907190        return false;
    71917191    }
    7192     if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key", 0)) {
     7192    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key fkey(chip_id) ref chipProcessedExp.chip_id)", 0)) {
    71937193        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
    71947194        psFree(md);
     
    76077607        return false;
    76087608    }
    7609     if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key", 0)) {
     7609    if (!psMetadataAdd(md, PS_LIST_TAIL, "chip_id", PS_DATA_S64, "Primary Key fkey(chip_id) ref chipProcessedExp.chip_id)", 0)) {
    76107610        psError(PS_ERR_UNKNOWN, false, "failed to add item chip_id");
    76117611        psFree(md);
     
    84588458
    84598459    psMetadata *md = camMaskMetadataFromObject(object);
     8460
     8461    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     8462        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     8463        psFree(md);
     8464    }
     8465
     8466    psFree(md);
     8467
     8468    return true;
     8469}
     8470static void warpRunRowFree(warpRunRow *object);
     8471
     8472warpRunRow *warpRunRowAlloc(psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
     8473{
     8474    warpRunRow      *_object;
     8475
     8476    _object = psAlloc(sizeof(warpRunRow));
     8477    psMemSetDeallocator(_object, (psFreeFunc)warpRunRowFree);
     8478
     8479    _object->warp_id = warp_id;
     8480    _object->mode = psStringCopy(mode);
     8481    _object->state = psStringCopy(state);
     8482    _object->workdir = psStringCopy(workdir);
     8483    _object->dvodb = psStringCopy(dvodb);
     8484    _object->registered = psTimeCopy(registered);
     8485
     8486    return _object;
     8487}
     8488
     8489static void warpRunRowFree(warpRunRow *object)
     8490{
     8491    psFree(object->mode);
     8492    psFree(object->state);
     8493    psFree(object->workdir);
     8494    psFree(object->dvodb);
     8495    psFree(object->registered);
     8496}
     8497
     8498bool warpRunCreateTable(psDB *dbh)
     8499{
     8500    psMetadata *md = psMetadataAlloc();
     8501    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
     8502        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     8503        psFree(md);
     8504        return false;
     8505    }
     8506    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, "Key", "64")) {
     8507        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
     8508        psFree(md);
     8509        return false;
     8510    }
     8511    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
     8512        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     8513        psFree(md);
     8514        return false;
     8515    }
     8516    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
     8517        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     8518        psFree(md);
     8519        return false;
     8520    }
     8521    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
     8522        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     8523        psFree(md);
     8524        return false;
     8525    }
     8526    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
     8527        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     8528        psFree(md);
     8529        return false;
     8530    }
     8531
     8532    bool status = psDBCreateTable(dbh, WARPRUN_TABLE_NAME, md);
     8533
     8534    psFree(md);
     8535
     8536    return status;
     8537}
     8538
     8539bool warpRunDropTable(psDB *dbh)
     8540{
     8541    return psDBDropTable(dbh, WARPRUN_TABLE_NAME);
     8542}
     8543
     8544bool warpRunInsert(psDB * dbh, psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
     8545{
     8546    psMetadata *md = psMetadataAlloc();
     8547    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
     8548        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     8549        psFree(md);
     8550        return false;
     8551    }
     8552    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, mode)) {
     8553        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
     8554        psFree(md);
     8555        return false;
     8556    }
     8557    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
     8558        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     8559        psFree(md);
     8560        return false;
     8561    }
     8562    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
     8563        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     8564        psFree(md);
     8565        return false;
     8566    }
     8567    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
     8568        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     8569        psFree(md);
     8570        return false;
     8571    }
     8572    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
     8573        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     8574        psFree(md);
     8575        return false;
     8576    }
     8577
     8578    bool status = psDBInsertOneRow(dbh, WARPRUN_TABLE_NAME, md);
     8579    psFree(md);
     8580
     8581    return status;
     8582}
     8583
     8584long long warpRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     8585{
     8586    long long       deleted = 0;
     8587
     8588    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
     8589    if (count < 0) {
     8590        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
     8591        return count;
     8592
     8593        deleted += count;
     8594    }
     8595
     8596    return deleted;
     8597}
     8598bool warpRunInsertObject(psDB *dbh, warpRunRow *object)
     8599{
     8600    return warpRunInsert(dbh, object->warp_id, object->mode, object->state, object->workdir, object->dvodb, object->registered);
     8601}
     8602
     8603bool warpRunInsertObjects(psDB *dbh, psArray *objects)
     8604{
     8605    for (long i = 0; i < psArrayLength(objects); i++) {
     8606        if (!warpRunInsertObject(dbh, objects->data[i])) {
     8607            return false;
     8608        }
     8609    }
     8610
     8611    return true;
     8612}
     8613
     8614bool warpRunInsertFits(psDB *dbh, const psFits *fits)
     8615{
     8616    psArray         *rowSet;
     8617
     8618    // move to (the first?) extension named  WARPRUN_TABLE_NAME
     8619    if (!psFitsMoveExtName(fits, WARPRUN_TABLE_NAME)) {
     8620        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPRUN_TABLE_NAME);
     8621        return false;
     8622    }
     8623
     8624    // check HDU type
     8625    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     8626        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     8627        return false;
     8628    }
     8629
     8630    // read fits table
     8631    rowSet = psFitsReadTable(fits);
     8632    if (!rowSet) {
     8633        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     8634        psFree(rowSet);
     8635        return false;
     8636    }
     8637
     8638    if (!psDBInsertRows(dbh, WARPRUN_TABLE_NAME, rowSet)) {
     8639        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     8640        psFree(rowSet);
     8641        return false;
     8642    }
     8643
     8644    psFree(rowSet);
     8645
     8646    return true;
     8647}
     8648
     8649bool warpRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     8650{
     8651    psArray         *rowSet;
     8652
     8653    rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
     8654    if (!rowSet) {
     8655        return false;
     8656    }
     8657
     8658    // output to fits
     8659    if (!psFitsWriteTable(fits, NULL, rowSet, WARPRUN_TABLE_NAME)) {
     8660        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     8661        psFree(rowSet);
     8662        return false;
     8663    }
     8664
     8665    psFree(rowSet);
     8666
     8667    return true;
     8668}
     8669
     8670psMetadata *warpRunMetadataFromObject(const warpRunRow *object)
     8671{
     8672    psMetadata *md = psMetadataAlloc();
     8673    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
     8674        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     8675        psFree(md);
     8676        return false;
     8677    }
     8678    if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, object->mode)) {
     8679        psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
     8680        psFree(md);
     8681        return false;
     8682    }
     8683    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
     8684        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     8685        psFree(md);
     8686        return false;
     8687    }
     8688    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
     8689        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     8690        psFree(md);
     8691        return false;
     8692    }
     8693    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
     8694        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     8695        psFree(md);
     8696        return false;
     8697    }
     8698    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
     8699        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     8700        psFree(md);
     8701        return false;
     8702    }
     8703
     8704
     8705    return md;
     8706}
     8707
     8708warpRunRow *warpRunObjectFromMetadata(psMetadata *md)
     8709{
     8710
     8711bool status = false;
     8712    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
     8713    if (!status) {
     8714        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
     8715        return false;
     8716    }
     8717    char* mode = psMetadataLookupPtr(&status, md, "mode");
     8718    if (!status) {
     8719        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item mode");
     8720        return false;
     8721    }
     8722    char* state = psMetadataLookupPtr(&status, md, "state");
     8723    if (!status) {
     8724        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
     8725        return false;
     8726    }
     8727    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
     8728    if (!status) {
     8729        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
     8730        return false;
     8731    }
     8732    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
     8733    if (!status) {
     8734        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
     8735        return false;
     8736    }
     8737    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
     8738    if (!status) {
     8739        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
     8740        return false;
     8741    }
     8742
     8743    return warpRunRowAlloc(warp_id, mode, state, workdir, dvodb, registered);
     8744}
     8745psArray *warpRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     8746{
     8747    psArray         *rowSet;
     8748    psArray         *returnSet;
     8749    psU64           i;
     8750
     8751    rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
     8752    if (!rowSet) {
     8753        return NULL;
     8754    }
     8755
     8756    // convert psMetadata rows to row objects
     8757
     8758    returnSet = psArrayAllocEmpty(rowSet->n);
     8759
     8760    for (i = 0; i < rowSet->n; i++) {
     8761        warpRunRow *object = warpRunObjectFromMetadata(rowSet->data[i]);
     8762        psArrayAdd(returnSet, 0, object);
     8763        psFree(object);
     8764    }
     8765
     8766    psFree(rowSet);
     8767
     8768    return returnSet;
     8769}
     8770bool warpRunDeleteObject(psDB *dbh, const warpRunRow *object)
     8771{
     8772    psMetadata *where = warpRunMetadataFromObject(object);
     8773    long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, 0);
     8774    psFree(where);
     8775    if (count < 0) {
     8776        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
     8777        return false;
     8778    }
     8779    if (count > 1) {
     8780        // XXX should this be a psAbort() instead?  It is possible that
     8781        // having an object match multiple rows was by design.
     8782        psError(PS_ERR_UNKNOWN, true, "warpRunRow object matched more then one row.  Check your database schema");
     8783        return false;
     8784    }
     8785
     8786    return true;
     8787}
     8788long long warpRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     8789{
     8790    long long       deleted = 0;
     8791
     8792    for (long long i = 0; i < objects->n; i++) {
     8793        warpRunRow *object = objects->data[i];
     8794        psMetadata *where = warpRunMetadataFromObject(object);
     8795        long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
     8796        psFree(where);
     8797        if (count < 0) {
     8798            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
     8799            return count;
     8800        }
     8801
     8802        deleted += count;
     8803    }
     8804
     8805    return deleted;
     8806}
     8807bool warpRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
     8808{
     8809    PS_ASSERT_PTR_NON_NULL(objects, false);
     8810
     8811    psMetadata *output = psMetadataAlloc();
     8812    for (long i = 0; i < psArrayLength(objects); i++) {
     8813        psMetadata *md = warpRunMetadataFromObject(objects->data[i]);
     8814        if (!psMetadataAddMetadata(
     8815            output,
     8816            PS_LIST_TAIL,
     8817            WARPRUN_TABLE_NAME,
     8818            PS_META_DUPLICATE_OK,
     8819            NULL,
     8820            md
     8821        )) {
     8822            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     8823            psFree(md);
     8824            psFree(output);
     8825            return false;
     8826        }
     8827        psFree(md);
     8828    }
     8829
     8830    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     8831        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     8832        psFree(output);
     8833    }
     8834    psFree(output);
     8835
     8836    return true;
     8837}
     8838bool warpRunPrintObject(FILE *stream, warpRunRow *object, bool mdcf)
     8839{
     8840    PS_ASSERT_PTR_NON_NULL(object, false);
     8841
     8842    psMetadata *md = warpRunMetadataFromObject(object);
     8843
     8844    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     8845        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     8846        psFree(md);
     8847    }
     8848
     8849    psFree(md);
     8850
     8851    return true;
     8852}
     8853static void warpInputExpRowFree(warpInputExpRow *object);
     8854
     8855warpInputExpRow *warpInputExpRowAlloc(psS64 warp_id, psS64 cam_id, bool magiced)
     8856{
     8857    warpInputExpRow *_object;
     8858
     8859    _object = psAlloc(sizeof(warpInputExpRow));
     8860    psMemSetDeallocator(_object, (psFreeFunc)warpInputExpRowFree);
     8861
     8862    _object->warp_id = warp_id;
     8863    _object->cam_id = cam_id;
     8864    _object->magiced = magiced;
     8865
     8866    return _object;
     8867}
     8868
     8869static void warpInputExpRowFree(warpInputExpRow *object)
     8870{
     8871}
     8872
     8873bool warpInputExpCreateTable(psDB *dbh)
     8874{
     8875    psMetadata *md = psMetadataAlloc();
     8876    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id) ref warpRun(warp_id)", 0)) {
     8877        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     8878        psFree(md);
     8879        return false;
     8880    }
     8881    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key fkey(cam_id) ref camProcessedExp(cam_id)", 0)) {
     8882        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
     8883        psFree(md);
     8884        return false;
     8885    }
     8886    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, "Key", 0)) {
     8887        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
     8888        psFree(md);
     8889        return false;
     8890    }
     8891
     8892    bool status = psDBCreateTable(dbh, WARPINPUTEXP_TABLE_NAME, md);
     8893
     8894    psFree(md);
     8895
     8896    return status;
     8897}
     8898
     8899bool warpInputExpDropTable(psDB *dbh)
     8900{
     8901    return psDBDropTable(dbh, WARPINPUTEXP_TABLE_NAME);
     8902}
     8903
     8904bool warpInputExpInsert(psDB * dbh, psS64 warp_id, psS64 cam_id, bool magiced)
     8905{
     8906    psMetadata *md = psMetadataAlloc();
     8907    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
     8908        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     8909        psFree(md);
     8910        return false;
     8911    }
     8912    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
     8913        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
     8914        psFree(md);
     8915        return false;
     8916    }
     8917    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, magiced)) {
     8918        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
     8919        psFree(md);
     8920        return false;
     8921    }
     8922
     8923    bool status = psDBInsertOneRow(dbh, WARPINPUTEXP_TABLE_NAME, md);
     8924    psFree(md);
     8925
     8926    return status;
     8927}
     8928
     8929long long warpInputExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     8930{
     8931    long long       deleted = 0;
     8932
     8933    long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
     8934    if (count < 0) {
     8935        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
     8936        return count;
     8937
     8938        deleted += count;
     8939    }
     8940
     8941    return deleted;
     8942}
     8943bool warpInputExpInsertObject(psDB *dbh, warpInputExpRow *object)
     8944{
     8945    return warpInputExpInsert(dbh, object->warp_id, object->cam_id, object->magiced);
     8946}
     8947
     8948bool warpInputExpInsertObjects(psDB *dbh, psArray *objects)
     8949{
     8950    for (long i = 0; i < psArrayLength(objects); i++) {
     8951        if (!warpInputExpInsertObject(dbh, objects->data[i])) {
     8952            return false;
     8953        }
     8954    }
     8955
     8956    return true;
     8957}
     8958
     8959bool warpInputExpInsertFits(psDB *dbh, const psFits *fits)
     8960{
     8961    psArray         *rowSet;
     8962
     8963    // move to (the first?) extension named  WARPINPUTEXP_TABLE_NAME
     8964    if (!psFitsMoveExtName(fits, WARPINPUTEXP_TABLE_NAME)) {
     8965        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPINPUTEXP_TABLE_NAME);
     8966        return false;
     8967    }
     8968
     8969    // check HDU type
     8970    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     8971        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     8972        return false;
     8973    }
     8974
     8975    // read fits table
     8976    rowSet = psFitsReadTable(fits);
     8977    if (!rowSet) {
     8978        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     8979        psFree(rowSet);
     8980        return false;
     8981    }
     8982
     8983    if (!psDBInsertRows(dbh, WARPINPUTEXP_TABLE_NAME, rowSet)) {
     8984        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     8985        psFree(rowSet);
     8986        return false;
     8987    }
     8988
     8989    psFree(rowSet);
     8990
     8991    return true;
     8992}
     8993
     8994bool warpInputExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     8995{
     8996    psArray         *rowSet;
     8997
     8998    rowSet = psDBSelectRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
     8999    if (!rowSet) {
     9000        return false;
     9001    }
     9002
     9003    // output to fits
     9004    if (!psFitsWriteTable(fits, NULL, rowSet, WARPINPUTEXP_TABLE_NAME)) {
     9005        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     9006        psFree(rowSet);
     9007        return false;
     9008    }
     9009
     9010    psFree(rowSet);
     9011
     9012    return true;
     9013}
     9014
     9015psMetadata *warpInputExpMetadataFromObject(const warpInputExpRow *object)
     9016{
     9017    psMetadata *md = psMetadataAlloc();
     9018    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
     9019        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9020        psFree(md);
     9021        return false;
     9022    }
     9023    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
     9024        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
     9025        psFree(md);
     9026        return false;
     9027    }
     9028    if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, object->magiced)) {
     9029        psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
     9030        psFree(md);
     9031        return false;
     9032    }
     9033
     9034
     9035    return md;
     9036}
     9037
     9038warpInputExpRow *warpInputExpObjectFromMetadata(psMetadata *md)
     9039{
     9040
     9041bool status = false;
     9042    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
     9043    if (!status) {
     9044        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
     9045        return false;
     9046    }
     9047    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
     9048    if (!status) {
     9049        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
     9050        return false;
     9051    }
     9052    bool magiced = psMetadataLookupBool(&status, md, "magiced");
     9053    if (!status) {
     9054        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magiced");
     9055        return false;
     9056    }
     9057
     9058    return warpInputExpRowAlloc(warp_id, cam_id, magiced);
     9059}
     9060psArray *warpInputExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     9061{
     9062    psArray         *rowSet;
     9063    psArray         *returnSet;
     9064    psU64           i;
     9065
     9066    rowSet = psDBSelectRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
     9067    if (!rowSet) {
     9068        return NULL;
     9069    }
     9070
     9071    // convert psMetadata rows to row objects
     9072
     9073    returnSet = psArrayAllocEmpty(rowSet->n);
     9074
     9075    for (i = 0; i < rowSet->n; i++) {
     9076        warpInputExpRow *object = warpInputExpObjectFromMetadata(rowSet->data[i]);
     9077        psArrayAdd(returnSet, 0, object);
     9078        psFree(object);
     9079    }
     9080
     9081    psFree(rowSet);
     9082
     9083    return returnSet;
     9084}
     9085bool warpInputExpDeleteObject(psDB *dbh, const warpInputExpRow *object)
     9086{
     9087    psMetadata *where = warpInputExpMetadataFromObject(object);
     9088    long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, 0);
     9089    psFree(where);
     9090    if (count < 0) {
     9091        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
     9092        return false;
     9093    }
     9094    if (count > 1) {
     9095        // XXX should this be a psAbort() instead?  It is possible that
     9096        // having an object match multiple rows was by design.
     9097        psError(PS_ERR_UNKNOWN, true, "warpInputExpRow object matched more then one row.  Check your database schema");
     9098        return false;
     9099    }
     9100
     9101    return true;
     9102}
     9103long long warpInputExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     9104{
     9105    long long       deleted = 0;
     9106
     9107    for (long long i = 0; i < objects->n; i++) {
     9108        warpInputExpRow *object = objects->data[i];
     9109        psMetadata *where = warpInputExpMetadataFromObject(object);
     9110        long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
     9111        psFree(where);
     9112        if (count < 0) {
     9113            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
     9114            return count;
     9115        }
     9116
     9117        deleted += count;
     9118    }
     9119
     9120    return deleted;
     9121}
     9122bool warpInputExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
     9123{
     9124    PS_ASSERT_PTR_NON_NULL(objects, false);
     9125
     9126    psMetadata *output = psMetadataAlloc();
     9127    for (long i = 0; i < psArrayLength(objects); i++) {
     9128        psMetadata *md = warpInputExpMetadataFromObject(objects->data[i]);
     9129        if (!psMetadataAddMetadata(
     9130            output,
     9131            PS_LIST_TAIL,
     9132            WARPINPUTEXP_TABLE_NAME,
     9133            PS_META_DUPLICATE_OK,
     9134            NULL,
     9135            md
     9136        )) {
     9137            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     9138            psFree(md);
     9139            psFree(output);
     9140            return false;
     9141        }
     9142        psFree(md);
     9143    }
     9144
     9145    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     9146        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     9147        psFree(output);
     9148    }
     9149    psFree(output);
     9150
     9151    return true;
     9152}
     9153bool warpInputExpPrintObject(FILE *stream, warpInputExpRow *object, bool mdcf)
     9154{
     9155    PS_ASSERT_PTR_NON_NULL(object, false);
     9156
     9157    psMetadata *md = warpInputExpMetadataFromObject(object);
     9158
     9159    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     9160        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     9161        psFree(md);
     9162    }
     9163
     9164    psFree(md);
     9165
     9166    return true;
     9167}
     9168static void warpSkyCellMapRowFree(warpSkyCellMapRow *object);
     9169
     9170warpSkyCellMapRow *warpSkyCellMapRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
     9171{
     9172    warpSkyCellMapRow *_object;
     9173
     9174    _object = psAlloc(sizeof(warpSkyCellMapRow));
     9175    psMemSetDeallocator(_object, (psFreeFunc)warpSkyCellMapRowFree);
     9176
     9177    _object->warp_id = warp_id;
     9178    _object->skycell_id = psStringCopy(skycell_id);
     9179    _object->tess_id = psStringCopy(tess_id);
     9180    _object->cam_id = cam_id;
     9181    _object->class_id = psStringCopy(class_id);
     9182    _object->fault = fault;
     9183
     9184    return _object;
     9185}
     9186
     9187static void warpSkyCellMapRowFree(warpSkyCellMapRow *object)
     9188{
     9189    psFree(object->skycell_id);
     9190    psFree(object->tess_id);
     9191    psFree(object->class_id);
     9192}
     9193
     9194bool warpSkyCellMapCreateTable(psDB *dbh)
     9195{
     9196    psMetadata *md = psMetadataAlloc();
     9197    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)) {
     9198        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9199        psFree(md);
     9200        return false;
     9201    }
     9202    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
     9203        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9204        psFree(md);
     9205        return false;
     9206    }
     9207    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
     9208        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9209        psFree(md);
     9210        return false;
     9211    }
     9212    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
     9213        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
     9214        psFree(md);
     9215        return false;
     9216    }
     9217    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
     9218        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
     9219        psFree(md);
     9220        return false;
     9221    }
     9222    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
     9223        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
     9224        psFree(md);
     9225        return false;
     9226    }
     9227
     9228    bool status = psDBCreateTable(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
     9229
     9230    psFree(md);
     9231
     9232    return status;
     9233}
     9234
     9235bool warpSkyCellMapDropTable(psDB *dbh)
     9236{
     9237    return psDBDropTable(dbh, WARPSKYCELLMAP_TABLE_NAME);
     9238}
     9239
     9240bool warpSkyCellMapInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
     9241{
     9242    psMetadata *md = psMetadataAlloc();
     9243    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
     9244        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9245        psFree(md);
     9246        return false;
     9247    }
     9248    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
     9249        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9250        psFree(md);
     9251        return false;
     9252    }
     9253    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
     9254        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9255        psFree(md);
     9256        return false;
     9257    }
     9258    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
     9259        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
     9260        psFree(md);
     9261        return false;
     9262    }
     9263    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
     9264        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
     9265        psFree(md);
     9266        return false;
     9267    }
     9268    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
     9269        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
     9270        psFree(md);
     9271        return false;
     9272    }
     9273
     9274    bool status = psDBInsertOneRow(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
     9275    psFree(md);
     9276
     9277    return status;
     9278}
     9279
     9280long long warpSkyCellMapDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     9281{
     9282    long long       deleted = 0;
     9283
     9284    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
     9285    if (count < 0) {
     9286        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
     9287        return count;
     9288
     9289        deleted += count;
     9290    }
     9291
     9292    return deleted;
     9293}
     9294bool warpSkyCellMapInsertObject(psDB *dbh, warpSkyCellMapRow *object)
     9295{
     9296    return warpSkyCellMapInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->cam_id, object->class_id, object->fault);
     9297}
     9298
     9299bool warpSkyCellMapInsertObjects(psDB *dbh, psArray *objects)
     9300{
     9301    for (long i = 0; i < psArrayLength(objects); i++) {
     9302        if (!warpSkyCellMapInsertObject(dbh, objects->data[i])) {
     9303            return false;
     9304        }
     9305    }
     9306
     9307    return true;
     9308}
     9309
     9310bool warpSkyCellMapInsertFits(psDB *dbh, const psFits *fits)
     9311{
     9312    psArray         *rowSet;
     9313
     9314    // move to (the first?) extension named  WARPSKYCELLMAP_TABLE_NAME
     9315    if (!psFitsMoveExtName(fits, WARPSKYCELLMAP_TABLE_NAME)) {
     9316        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYCELLMAP_TABLE_NAME);
     9317        return false;
     9318    }
     9319
     9320    // check HDU type
     9321    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     9322        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     9323        return false;
     9324    }
     9325
     9326    // read fits table
     9327    rowSet = psFitsReadTable(fits);
     9328    if (!rowSet) {
     9329        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     9330        psFree(rowSet);
     9331        return false;
     9332    }
     9333
     9334    if (!psDBInsertRows(dbh, WARPSKYCELLMAP_TABLE_NAME, rowSet)) {
     9335        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     9336        psFree(rowSet);
     9337        return false;
     9338    }
     9339
     9340    psFree(rowSet);
     9341
     9342    return true;
     9343}
     9344
     9345bool warpSkyCellMapSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     9346{
     9347    psArray         *rowSet;
     9348
     9349    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
     9350    if (!rowSet) {
     9351        return false;
     9352    }
     9353
     9354    // output to fits
     9355    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYCELLMAP_TABLE_NAME)) {
     9356        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     9357        psFree(rowSet);
     9358        return false;
     9359    }
     9360
     9361    psFree(rowSet);
     9362
     9363    return true;
     9364}
     9365
     9366psMetadata *warpSkyCellMapMetadataFromObject(const warpSkyCellMapRow *object)
     9367{
     9368    psMetadata *md = psMetadataAlloc();
     9369    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
     9370        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9371        psFree(md);
     9372        return false;
     9373    }
     9374    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
     9375        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9376        psFree(md);
     9377        return false;
     9378    }
     9379    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
     9380        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9381        psFree(md);
     9382        return false;
     9383    }
     9384    if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
     9385        psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
     9386        psFree(md);
     9387        return false;
     9388    }
     9389    if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
     9390        psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
     9391        psFree(md);
     9392        return false;
     9393    }
     9394    if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
     9395        psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
     9396        psFree(md);
     9397        return false;
     9398    }
     9399
     9400
     9401    return md;
     9402}
     9403
     9404warpSkyCellMapRow *warpSkyCellMapObjectFromMetadata(psMetadata *md)
     9405{
     9406
     9407bool status = false;
     9408    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
     9409    if (!status) {
     9410        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
     9411        return false;
     9412    }
     9413    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
     9414    if (!status) {
     9415        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
     9416        return false;
     9417    }
     9418    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
     9419    if (!status) {
     9420        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
     9421        return false;
     9422    }
     9423    psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
     9424    if (!status) {
     9425        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
     9426        return false;
     9427    }
     9428    char* class_id = psMetadataLookupPtr(&status, md, "class_id");
     9429    if (!status) {
     9430        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
     9431        return false;
     9432    }
     9433    psS16 fault = psMetadataLookupS16(&status, md, "fault");
     9434    if (!status) {
     9435        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
     9436        return false;
     9437    }
     9438
     9439    return warpSkyCellMapRowAlloc(warp_id, skycell_id, tess_id, cam_id, class_id, fault);
     9440}
     9441psArray *warpSkyCellMapSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     9442{
     9443    psArray         *rowSet;
     9444    psArray         *returnSet;
     9445    psU64           i;
     9446
     9447    rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
     9448    if (!rowSet) {
     9449        return NULL;
     9450    }
     9451
     9452    // convert psMetadata rows to row objects
     9453
     9454    returnSet = psArrayAllocEmpty(rowSet->n);
     9455
     9456    for (i = 0; i < rowSet->n; i++) {
     9457        warpSkyCellMapRow *object = warpSkyCellMapObjectFromMetadata(rowSet->data[i]);
     9458        psArrayAdd(returnSet, 0, object);
     9459        psFree(object);
     9460    }
     9461
     9462    psFree(rowSet);
     9463
     9464    return returnSet;
     9465}
     9466bool warpSkyCellMapDeleteObject(psDB *dbh, const warpSkyCellMapRow *object)
     9467{
     9468    psMetadata *where = warpSkyCellMapMetadataFromObject(object);
     9469    long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, 0);
     9470    psFree(where);
     9471    if (count < 0) {
     9472        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
     9473        return false;
     9474    }
     9475    if (count > 1) {
     9476        // XXX should this be a psAbort() instead?  It is possible that
     9477        // having an object match multiple rows was by design.
     9478        psError(PS_ERR_UNKNOWN, true, "warpSkyCellMapRow object matched more then one row.  Check your database schema");
     9479        return false;
     9480    }
     9481
     9482    return true;
     9483}
     9484long long warpSkyCellMapDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     9485{
     9486    long long       deleted = 0;
     9487
     9488    for (long long i = 0; i < objects->n; i++) {
     9489        warpSkyCellMapRow *object = objects->data[i];
     9490        psMetadata *where = warpSkyCellMapMetadataFromObject(object);
     9491        long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
     9492        psFree(where);
     9493        if (count < 0) {
     9494            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
     9495            return count;
     9496        }
     9497
     9498        deleted += count;
     9499    }
     9500
     9501    return deleted;
     9502}
     9503bool warpSkyCellMapPrintObjects(FILE *stream, psArray *objects, bool mdcf)
     9504{
     9505    PS_ASSERT_PTR_NON_NULL(objects, false);
     9506
     9507    psMetadata *output = psMetadataAlloc();
     9508    for (long i = 0; i < psArrayLength(objects); i++) {
     9509        psMetadata *md = warpSkyCellMapMetadataFromObject(objects->data[i]);
     9510        if (!psMetadataAddMetadata(
     9511            output,
     9512            PS_LIST_TAIL,
     9513            WARPSKYCELLMAP_TABLE_NAME,
     9514            PS_META_DUPLICATE_OK,
     9515            NULL,
     9516            md
     9517        )) {
     9518            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     9519            psFree(md);
     9520            psFree(output);
     9521            return false;
     9522        }
     9523        psFree(md);
     9524    }
     9525
     9526    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     9527        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     9528        psFree(output);
     9529    }
     9530    psFree(output);
     9531
     9532    return true;
     9533}
     9534bool warpSkyCellMapPrintObject(FILE *stream, warpSkyCellMapRow *object, bool mdcf)
     9535{
     9536    PS_ASSERT_PTR_NON_NULL(object, false);
     9537
     9538    psMetadata *md = warpSkyCellMapMetadataFromObject(object);
     9539
     9540    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     9541        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     9542        psFree(md);
     9543    }
     9544
     9545    psFree(md);
     9546
     9547    return true;
     9548}
     9549static void warpSkyfileRowFree(warpSkyfileRow *object);
     9550
     9551warpSkyfileRow *warpSkyfileRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
     9552{
     9553    warpSkyfileRow  *_object;
     9554
     9555    _object = psAlloc(sizeof(warpSkyfileRow));
     9556    psMemSetDeallocator(_object, (psFreeFunc)warpSkyfileRowFree);
     9557
     9558    _object->warp_id = warp_id;
     9559    _object->skycell_id = psStringCopy(skycell_id);
     9560    _object->tess_id = psStringCopy(tess_id);
     9561    _object->uri = psStringCopy(uri);
     9562    _object->bg = bg;
     9563    _object->bg_stdev = bg_stdev;
     9564
     9565    return _object;
     9566}
     9567
     9568static void warpSkyfileRowFree(warpSkyfileRow *object)
     9569{
     9570    psFree(object->skycell_id);
     9571    psFree(object->tess_id);
     9572    psFree(object->uri);
     9573}
     9574
     9575bool warpSkyfileCreateTable(psDB *dbh)
     9576{
     9577    psMetadata *md = psMetadataAlloc();
     9578    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)) {
     9579        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9580        psFree(md);
     9581        return false;
     9582    }
     9583    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
     9584        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9585        psFree(md);
     9586        return false;
     9587    }
     9588    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
     9589        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9590        psFree(md);
     9591        return false;
     9592    }
     9593    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
     9594        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     9595        psFree(md);
     9596        return false;
     9597    }
     9598    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
     9599        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     9600        psFree(md);
     9601        return false;
     9602    }
     9603    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
     9604        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     9605        psFree(md);
     9606        return false;
     9607    }
     9608
     9609    bool status = psDBCreateTable(dbh, WARPSKYFILE_TABLE_NAME, md);
     9610
     9611    psFree(md);
     9612
     9613    return status;
     9614}
     9615
     9616bool warpSkyfileDropTable(psDB *dbh)
     9617{
     9618    return psDBDropTable(dbh, WARPSKYFILE_TABLE_NAME);
     9619}
     9620
     9621bool warpSkyfileInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
     9622{
     9623    psMetadata *md = psMetadataAlloc();
     9624    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
     9625        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9626        psFree(md);
     9627        return false;
     9628    }
     9629    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
     9630        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9631        psFree(md);
     9632        return false;
     9633    }
     9634    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
     9635        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9636        psFree(md);
     9637        return false;
     9638    }
     9639    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
     9640        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     9641        psFree(md);
     9642        return false;
     9643    }
     9644    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
     9645        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     9646        psFree(md);
     9647        return false;
     9648    }
     9649    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
     9650        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     9651        psFree(md);
     9652        return false;
     9653    }
     9654
     9655    bool status = psDBInsertOneRow(dbh, WARPSKYFILE_TABLE_NAME, md);
     9656    psFree(md);
     9657
     9658    return status;
     9659}
     9660
     9661long long warpSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     9662{
     9663    long long       deleted = 0;
     9664
     9665    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
     9666    if (count < 0) {
     9667        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
     9668        return count;
     9669
     9670        deleted += count;
     9671    }
     9672
     9673    return deleted;
     9674}
     9675bool warpSkyfileInsertObject(psDB *dbh, warpSkyfileRow *object)
     9676{
     9677    return warpSkyfileInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->uri, object->bg, object->bg_stdev);
     9678}
     9679
     9680bool warpSkyfileInsertObjects(psDB *dbh, psArray *objects)
     9681{
     9682    for (long i = 0; i < psArrayLength(objects); i++) {
     9683        if (!warpSkyfileInsertObject(dbh, objects->data[i])) {
     9684            return false;
     9685        }
     9686    }
     9687
     9688    return true;
     9689}
     9690
     9691bool warpSkyfileInsertFits(psDB *dbh, const psFits *fits)
     9692{
     9693    psArray         *rowSet;
     9694
     9695    // move to (the first?) extension named  WARPSKYFILE_TABLE_NAME
     9696    if (!psFitsMoveExtName(fits, WARPSKYFILE_TABLE_NAME)) {
     9697        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYFILE_TABLE_NAME);
     9698        return false;
     9699    }
     9700
     9701    // check HDU type
     9702    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     9703        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     9704        return false;
     9705    }
     9706
     9707    // read fits table
     9708    rowSet = psFitsReadTable(fits);
     9709    if (!rowSet) {
     9710        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     9711        psFree(rowSet);
     9712        return false;
     9713    }
     9714
     9715    if (!psDBInsertRows(dbh, WARPSKYFILE_TABLE_NAME, rowSet)) {
     9716        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     9717        psFree(rowSet);
     9718        return false;
     9719    }
     9720
     9721    psFree(rowSet);
     9722
     9723    return true;
     9724}
     9725
     9726bool warpSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     9727{
     9728    psArray         *rowSet;
     9729
     9730    rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
     9731    if (!rowSet) {
     9732        return false;
     9733    }
     9734
     9735    // output to fits
     9736    if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYFILE_TABLE_NAME)) {
     9737        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     9738        psFree(rowSet);
     9739        return false;
     9740    }
     9741
     9742    psFree(rowSet);
     9743
     9744    return true;
     9745}
     9746
     9747psMetadata *warpSkyfileMetadataFromObject(const warpSkyfileRow *object)
     9748{
     9749    psMetadata *md = psMetadataAlloc();
     9750    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
     9751        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     9752        psFree(md);
     9753        return false;
     9754    }
     9755    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
     9756        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9757        psFree(md);
     9758        return false;
     9759    }
     9760    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
     9761        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9762        psFree(md);
     9763        return false;
     9764    }
     9765    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
     9766        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     9767        psFree(md);
     9768        return false;
     9769    }
     9770    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
     9771        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     9772        psFree(md);
     9773        return false;
     9774    }
     9775    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
     9776        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     9777        psFree(md);
     9778        return false;
     9779    }
     9780
     9781
     9782    return md;
     9783}
     9784
     9785warpSkyfileRow *warpSkyfileObjectFromMetadata(psMetadata *md)
     9786{
     9787
     9788bool status = false;
     9789    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
     9790    if (!status) {
     9791        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
     9792        return false;
     9793    }
     9794    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
     9795    if (!status) {
     9796        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
     9797        return false;
     9798    }
     9799    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
     9800    if (!status) {
     9801        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
     9802        return false;
     9803    }
     9804    char* uri = psMetadataLookupPtr(&status, md, "uri");
     9805    if (!status) {
     9806        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
     9807        return false;
     9808    }
     9809    psF64 bg = psMetadataLookupF64(&status, md, "bg");
     9810    if (!status) {
     9811        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
     9812        return false;
     9813    }
     9814    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
     9815    if (!status) {
     9816        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
     9817        return false;
     9818    }
     9819
     9820    return warpSkyfileRowAlloc(warp_id, skycell_id, tess_id, uri, bg, bg_stdev);
     9821}
     9822psArray *warpSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     9823{
     9824    psArray         *rowSet;
     9825    psArray         *returnSet;
     9826    psU64           i;
     9827
     9828    rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
     9829    if (!rowSet) {
     9830        return NULL;
     9831    }
     9832
     9833    // convert psMetadata rows to row objects
     9834
     9835    returnSet = psArrayAllocEmpty(rowSet->n);
     9836
     9837    for (i = 0; i < rowSet->n; i++) {
     9838        warpSkyfileRow *object = warpSkyfileObjectFromMetadata(rowSet->data[i]);
     9839        psArrayAdd(returnSet, 0, object);
     9840        psFree(object);
     9841    }
     9842
     9843    psFree(rowSet);
     9844
     9845    return returnSet;
     9846}
     9847bool warpSkyfileDeleteObject(psDB *dbh, const warpSkyfileRow *object)
     9848{
     9849    psMetadata *where = warpSkyfileMetadataFromObject(object);
     9850    long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, 0);
     9851    psFree(where);
     9852    if (count < 0) {
     9853        psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
     9854        return false;
     9855    }
     9856    if (count > 1) {
     9857        // XXX should this be a psAbort() instead?  It is possible that
     9858        // having an object match multiple rows was by design.
     9859        psError(PS_ERR_UNKNOWN, true, "warpSkyfileRow object matched more then one row.  Check your database schema");
     9860        return false;
     9861    }
     9862
     9863    return true;
     9864}
     9865long long warpSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     9866{
     9867    long long       deleted = 0;
     9868
     9869    for (long long i = 0; i < objects->n; i++) {
     9870        warpSkyfileRow *object = objects->data[i];
     9871        psMetadata *where = warpSkyfileMetadataFromObject(object);
     9872        long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
     9873        psFree(where);
     9874        if (count < 0) {
     9875            psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
     9876            return count;
     9877        }
     9878
     9879        deleted += count;
     9880    }
     9881
     9882    return deleted;
     9883}
     9884bool warpSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
     9885{
     9886    PS_ASSERT_PTR_NON_NULL(objects, false);
     9887
     9888    psMetadata *output = psMetadataAlloc();
     9889    for (long i = 0; i < psArrayLength(objects); i++) {
     9890        psMetadata *md = warpSkyfileMetadataFromObject(objects->data[i]);
     9891        if (!psMetadataAddMetadata(
     9892            output,
     9893            PS_LIST_TAIL,
     9894            WARPSKYFILE_TABLE_NAME,
     9895            PS_META_DUPLICATE_OK,
     9896            NULL,
     9897            md
     9898        )) {
     9899            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     9900            psFree(md);
     9901            psFree(output);
     9902            return false;
     9903        }
     9904        psFree(md);
     9905    }
     9906
     9907    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     9908        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     9909        psFree(output);
     9910    }
     9911    psFree(output);
     9912
     9913    return true;
     9914}
     9915bool warpSkyfilePrintObject(FILE *stream, warpSkyfileRow *object, bool mdcf)
     9916{
     9917    PS_ASSERT_PTR_NON_NULL(object, false);
     9918
     9919    psMetadata *md = warpSkyfileMetadataFromObject(object);
     9920
     9921    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     9922        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     9923        psFree(md);
     9924    }
     9925
     9926    psFree(md);
     9927
     9928    return true;
     9929}
     9930static void diffRunRowFree(diffRunRow *object);
     9931
     9932diffRunRow *diffRunRowAlloc(psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
     9933{
     9934    diffRunRow      *_object;
     9935
     9936    _object = psAlloc(sizeof(diffRunRow));
     9937    psMemSetDeallocator(_object, (psFreeFunc)diffRunRowFree);
     9938
     9939    _object->diff_id = diff_id;
     9940    _object->state = psStringCopy(state);
     9941    _object->workdir = psStringCopy(workdir);
     9942    _object->dvodb = psStringCopy(dvodb);
     9943    _object->registered = psTimeCopy(registered);
     9944    _object->skycell_id = psStringCopy(skycell_id);
     9945    _object->tess_id = psStringCopy(tess_id);
     9946
     9947    return _object;
     9948}
     9949
     9950static void diffRunRowFree(diffRunRow *object)
     9951{
     9952    psFree(object->state);
     9953    psFree(object->workdir);
     9954    psFree(object->dvodb);
     9955    psFree(object->registered);
     9956    psFree(object->skycell_id);
     9957    psFree(object->tess_id);
     9958}
     9959
     9960bool diffRunCreateTable(psDB *dbh)
     9961{
     9962    psMetadata *md = psMetadataAlloc();
     9963    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
     9964        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     9965        psFree(md);
     9966        return false;
     9967    }
     9968    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
     9969        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     9970        psFree(md);
     9971        return false;
     9972    }
     9973    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
     9974        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     9975        psFree(md);
     9976        return false;
     9977    }
     9978    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
     9979        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     9980        psFree(md);
     9981        return false;
     9982    }
     9983    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
     9984        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     9985        psFree(md);
     9986        return false;
     9987    }
     9988    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
     9989        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     9990        psFree(md);
     9991        return false;
     9992    }
     9993    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
     9994        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     9995        psFree(md);
     9996        return false;
     9997    }
     9998
     9999    bool status = psDBCreateTable(dbh, DIFFRUN_TABLE_NAME, md);
     10000
     10001    psFree(md);
     10002
     10003    return status;
     10004}
     10005
     10006bool diffRunDropTable(psDB *dbh)
     10007{
     10008    return psDBDropTable(dbh, DIFFRUN_TABLE_NAME);
     10009}
     10010
     10011bool 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)
     10012{
     10013    psMetadata *md = psMetadataAlloc();
     10014    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
     10015        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10016        psFree(md);
     10017        return false;
     10018    }
     10019    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
     10020        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     10021        psFree(md);
     10022        return false;
     10023    }
     10024    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
     10025        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     10026        psFree(md);
     10027        return false;
     10028    }
     10029    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
     10030        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     10031        psFree(md);
     10032        return false;
     10033    }
     10034    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
     10035        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     10036        psFree(md);
     10037        return false;
     10038    }
     10039    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
     10040        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     10041        psFree(md);
     10042        return false;
     10043    }
     10044    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
     10045        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     10046        psFree(md);
     10047        return false;
     10048    }
     10049
     10050    bool status = psDBInsertOneRow(dbh, DIFFRUN_TABLE_NAME, md);
     10051    psFree(md);
     10052
     10053    return status;
     10054}
     10055
     10056long long diffRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     10057{
     10058    long long       deleted = 0;
     10059
     10060    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
     10061    if (count < 0) {
     10062        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
     10063        return count;
     10064
     10065        deleted += count;
     10066    }
     10067
     10068    return deleted;
     10069}
     10070bool diffRunInsertObject(psDB *dbh, diffRunRow *object)
     10071{
     10072    return diffRunInsert(dbh, object->diff_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
     10073}
     10074
     10075bool diffRunInsertObjects(psDB *dbh, psArray *objects)
     10076{
     10077    for (long i = 0; i < psArrayLength(objects); i++) {
     10078        if (!diffRunInsertObject(dbh, objects->data[i])) {
     10079            return false;
     10080        }
     10081    }
     10082
     10083    return true;
     10084}
     10085
     10086bool diffRunInsertFits(psDB *dbh, const psFits *fits)
     10087{
     10088    psArray         *rowSet;
     10089
     10090    // move to (the first?) extension named  DIFFRUN_TABLE_NAME
     10091    if (!psFitsMoveExtName(fits, DIFFRUN_TABLE_NAME)) {
     10092        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFRUN_TABLE_NAME);
     10093        return false;
     10094    }
     10095
     10096    // check HDU type
     10097    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     10098        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     10099        return false;
     10100    }
     10101
     10102    // read fits table
     10103    rowSet = psFitsReadTable(fits);
     10104    if (!rowSet) {
     10105        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     10106        psFree(rowSet);
     10107        return false;
     10108    }
     10109
     10110    if (!psDBInsertRows(dbh, DIFFRUN_TABLE_NAME, rowSet)) {
     10111        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     10112        psFree(rowSet);
     10113        return false;
     10114    }
     10115
     10116    psFree(rowSet);
     10117
     10118    return true;
     10119}
     10120
     10121bool diffRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     10122{
     10123    psArray         *rowSet;
     10124
     10125    rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
     10126    if (!rowSet) {
     10127        return false;
     10128    }
     10129
     10130    // output to fits
     10131    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFRUN_TABLE_NAME)) {
     10132        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     10133        psFree(rowSet);
     10134        return false;
     10135    }
     10136
     10137    psFree(rowSet);
     10138
     10139    return true;
     10140}
     10141
     10142psMetadata *diffRunMetadataFromObject(const diffRunRow *object)
     10143{
     10144    psMetadata *md = psMetadataAlloc();
     10145    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
     10146        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10147        psFree(md);
     10148        return false;
     10149    }
     10150    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
     10151        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     10152        psFree(md);
     10153        return false;
     10154    }
     10155    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
     10156        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     10157        psFree(md);
     10158        return false;
     10159    }
     10160    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
     10161        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     10162        psFree(md);
     10163        return false;
     10164    }
     10165    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
     10166        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     10167        psFree(md);
     10168        return false;
     10169    }
     10170    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
     10171        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     10172        psFree(md);
     10173        return false;
     10174    }
     10175    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
     10176        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     10177        psFree(md);
     10178        return false;
     10179    }
     10180
     10181
     10182    return md;
     10183}
     10184
     10185diffRunRow *diffRunObjectFromMetadata(psMetadata *md)
     10186{
     10187
     10188bool status = false;
     10189    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
     10190    if (!status) {
     10191        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
     10192        return false;
     10193    }
     10194    char* state = psMetadataLookupPtr(&status, md, "state");
     10195    if (!status) {
     10196        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
     10197        return false;
     10198    }
     10199    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
     10200    if (!status) {
     10201        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
     10202        return false;
     10203    }
     10204    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
     10205    if (!status) {
     10206        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
     10207        return false;
     10208    }
     10209    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
     10210    if (!status) {
     10211        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
     10212        return false;
     10213    }
     10214    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
     10215    if (!status) {
     10216        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
     10217        return false;
     10218    }
     10219    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
     10220    if (!status) {
     10221        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
     10222        return false;
     10223    }
     10224
     10225    return diffRunRowAlloc(diff_id, state, workdir, dvodb, registered, skycell_id, tess_id);
     10226}
     10227psArray *diffRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     10228{
     10229    psArray         *rowSet;
     10230    psArray         *returnSet;
     10231    psU64           i;
     10232
     10233    rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
     10234    if (!rowSet) {
     10235        return NULL;
     10236    }
     10237
     10238    // convert psMetadata rows to row objects
     10239
     10240    returnSet = psArrayAllocEmpty(rowSet->n);
     10241
     10242    for (i = 0; i < rowSet->n; i++) {
     10243        diffRunRow *object = diffRunObjectFromMetadata(rowSet->data[i]);
     10244        psArrayAdd(returnSet, 0, object);
     10245        psFree(object);
     10246    }
     10247
     10248    psFree(rowSet);
     10249
     10250    return returnSet;
     10251}
     10252bool diffRunDeleteObject(psDB *dbh, const diffRunRow *object)
     10253{
     10254    psMetadata *where = diffRunMetadataFromObject(object);
     10255    long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, 0);
     10256    psFree(where);
     10257    if (count < 0) {
     10258        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
     10259        return false;
     10260    }
     10261    if (count > 1) {
     10262        // XXX should this be a psAbort() instead?  It is possible that
     10263        // having an object match multiple rows was by design.
     10264        psError(PS_ERR_UNKNOWN, true, "diffRunRow object matched more then one row.  Check your database schema");
     10265        return false;
     10266    }
     10267
     10268    return true;
     10269}
     10270long long diffRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     10271{
     10272    long long       deleted = 0;
     10273
     10274    for (long long i = 0; i < objects->n; i++) {
     10275        diffRunRow *object = objects->data[i];
     10276        psMetadata *where = diffRunMetadataFromObject(object);
     10277        long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
     10278        psFree(where);
     10279        if (count < 0) {
     10280            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
     10281            return count;
     10282        }
     10283
     10284        deleted += count;
     10285    }
     10286
     10287    return deleted;
     10288}
     10289bool diffRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
     10290{
     10291    PS_ASSERT_PTR_NON_NULL(objects, false);
     10292
     10293    psMetadata *output = psMetadataAlloc();
     10294    for (long i = 0; i < psArrayLength(objects); i++) {
     10295        psMetadata *md = diffRunMetadataFromObject(objects->data[i]);
     10296        if (!psMetadataAddMetadata(
     10297            output,
     10298            PS_LIST_TAIL,
     10299            DIFFRUN_TABLE_NAME,
     10300            PS_META_DUPLICATE_OK,
     10301            NULL,
     10302            md
     10303        )) {
     10304            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     10305            psFree(md);
     10306            psFree(output);
     10307            return false;
     10308        }
     10309        psFree(md);
     10310    }
     10311
     10312    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     10313        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     10314        psFree(output);
     10315    }
     10316    psFree(output);
     10317
     10318    return true;
     10319}
     10320bool diffRunPrintObject(FILE *stream, diffRunRow *object, bool mdcf)
     10321{
     10322    PS_ASSERT_PTR_NON_NULL(object, false);
     10323
     10324    psMetadata *md = diffRunMetadataFromObject(object);
     10325
     10326    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     10327        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     10328        psFree(md);
     10329    }
     10330
     10331    psFree(md);
     10332
     10333    return true;
     10334}
     10335static void diffInputSkyfileRowFree(diffInputSkyfileRow *object);
     10336
     10337diffInputSkyfileRow *diffInputSkyfileRowAlloc(psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
     10338{
     10339    diffInputSkyfileRow *_object;
     10340
     10341    _object = psAlloc(sizeof(diffInputSkyfileRow));
     10342    psMemSetDeallocator(_object, (psFreeFunc)diffInputSkyfileRowFree);
     10343
     10344    _object->diff_id = diff_id;
     10345    _object->warp_id = warp_id;
     10346    _object->skycell_id = psStringCopy(skycell_id);
     10347    _object->tess_id = psStringCopy(tess_id);
     10348    _object->kind = psStringCopy(kind);
     10349    _object->template = template;
     10350
     10351    return _object;
     10352}
     10353
     10354static void diffInputSkyfileRowFree(diffInputSkyfileRow *object)
     10355{
     10356    psFree(object->skycell_id);
     10357    psFree(object->tess_id);
     10358    psFree(object->kind);
     10359}
     10360
     10361bool diffInputSkyfileCreateTable(psDB *dbh)
     10362{
     10363    psMetadata *md = psMetadataAlloc();
     10364    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
     10365        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10366        psFree(md);
     10367        return false;
     10368    }
     10369    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)) {
     10370        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     10371        psFree(md);
     10372        return false;
     10373    }
     10374    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
     10375        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     10376        psFree(md);
     10377        return false;
     10378    }
     10379    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
     10380        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     10381        psFree(md);
     10382        return false;
     10383    }
     10384    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, "Key", "64")) {
     10385        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
     10386        psFree(md);
     10387        return false;
     10388    }
     10389    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, 0)) {
     10390        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
     10391        psFree(md);
     10392        return false;
     10393    }
     10394
     10395    bool status = psDBCreateTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
     10396
     10397    psFree(md);
     10398
     10399    return status;
     10400}
     10401
     10402bool diffInputSkyfileDropTable(psDB *dbh)
     10403{
     10404    return psDBDropTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME);
     10405}
     10406
     10407bool diffInputSkyfileInsert(psDB * dbh, psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
     10408{
     10409    psMetadata *md = psMetadataAlloc();
     10410    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
     10411        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10412        psFree(md);
     10413        return false;
     10414    }
     10415    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
     10416        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     10417        psFree(md);
     10418        return false;
     10419    }
     10420    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
     10421        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     10422        psFree(md);
     10423        return false;
     10424    }
     10425    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
     10426        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     10427        psFree(md);
     10428        return false;
     10429    }
     10430    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, kind)) {
     10431        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
     10432        psFree(md);
     10433        return false;
     10434    }
     10435    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, template)) {
     10436        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
     10437        psFree(md);
     10438        return false;
     10439    }
     10440
     10441    bool status = psDBInsertOneRow(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
     10442    psFree(md);
     10443
     10444    return status;
     10445}
     10446
     10447long long diffInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     10448{
     10449    long long       deleted = 0;
     10450
     10451    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
     10452    if (count < 0) {
     10453        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
     10454        return count;
     10455
     10456        deleted += count;
     10457    }
     10458
     10459    return deleted;
     10460}
     10461bool diffInputSkyfileInsertObject(psDB *dbh, diffInputSkyfileRow *object)
     10462{
     10463    return diffInputSkyfileInsert(dbh, object->diff_id, object->warp_id, object->skycell_id, object->tess_id, object->kind, object->template);
     10464}
     10465
     10466bool diffInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
     10467{
     10468    for (long i = 0; i < psArrayLength(objects); i++) {
     10469        if (!diffInputSkyfileInsertObject(dbh, objects->data[i])) {
     10470            return false;
     10471        }
     10472    }
     10473
     10474    return true;
     10475}
     10476
     10477bool diffInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
     10478{
     10479    psArray         *rowSet;
     10480
     10481    // move to (the first?) extension named  DIFFINPUTSKYFILE_TABLE_NAME
     10482    if (!psFitsMoveExtName(fits, DIFFINPUTSKYFILE_TABLE_NAME)) {
     10483        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFINPUTSKYFILE_TABLE_NAME);
     10484        return false;
     10485    }
     10486
     10487    // check HDU type
     10488    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     10489        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     10490        return false;
     10491    }
     10492
     10493    // read fits table
     10494    rowSet = psFitsReadTable(fits);
     10495    if (!rowSet) {
     10496        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     10497        psFree(rowSet);
     10498        return false;
     10499    }
     10500
     10501    if (!psDBInsertRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, rowSet)) {
     10502        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     10503        psFree(rowSet);
     10504        return false;
     10505    }
     10506
     10507    psFree(rowSet);
     10508
     10509    return true;
     10510}
     10511
     10512bool diffInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     10513{
     10514    psArray         *rowSet;
     10515
     10516    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
     10517    if (!rowSet) {
     10518        return false;
     10519    }
     10520
     10521    // output to fits
     10522    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFINPUTSKYFILE_TABLE_NAME)) {
     10523        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     10524        psFree(rowSet);
     10525        return false;
     10526    }
     10527
     10528    psFree(rowSet);
     10529
     10530    return true;
     10531}
     10532
     10533psMetadata *diffInputSkyfileMetadataFromObject(const diffInputSkyfileRow *object)
     10534{
     10535    psMetadata *md = psMetadataAlloc();
     10536    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
     10537        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10538        psFree(md);
     10539        return false;
     10540    }
     10541    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
     10542        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     10543        psFree(md);
     10544        return false;
     10545    }
     10546    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
     10547        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     10548        psFree(md);
     10549        return false;
     10550    }
     10551    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
     10552        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     10553        psFree(md);
     10554        return false;
     10555    }
     10556    if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, object->kind)) {
     10557        psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
     10558        psFree(md);
     10559        return false;
     10560    }
     10561    if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, object->template)) {
     10562        psError(PS_ERR_UNKNOWN, false, "failed to add item template");
     10563        psFree(md);
     10564        return false;
     10565    }
     10566
     10567
     10568    return md;
     10569}
     10570
     10571diffInputSkyfileRow *diffInputSkyfileObjectFromMetadata(psMetadata *md)
     10572{
     10573
     10574bool status = false;
     10575    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
     10576    if (!status) {
     10577        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
     10578        return false;
     10579    }
     10580    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
     10581    if (!status) {
     10582        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
     10583        return false;
     10584    }
     10585    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
     10586    if (!status) {
     10587        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
     10588        return false;
     10589    }
     10590    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
     10591    if (!status) {
     10592        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
     10593        return false;
     10594    }
     10595    char* kind = psMetadataLookupPtr(&status, md, "kind");
     10596    if (!status) {
     10597        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item kind");
     10598        return false;
     10599    }
     10600    bool template = psMetadataLookupBool(&status, md, "template");
     10601    if (!status) {
     10602        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item template");
     10603        return false;
     10604    }
     10605
     10606    return diffInputSkyfileRowAlloc(diff_id, warp_id, skycell_id, tess_id, kind, template);
     10607}
     10608psArray *diffInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     10609{
     10610    psArray         *rowSet;
     10611    psArray         *returnSet;
     10612    psU64           i;
     10613
     10614    rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
     10615    if (!rowSet) {
     10616        return NULL;
     10617    }
     10618
     10619    // convert psMetadata rows to row objects
     10620
     10621    returnSet = psArrayAllocEmpty(rowSet->n);
     10622
     10623    for (i = 0; i < rowSet->n; i++) {
     10624        diffInputSkyfileRow *object = diffInputSkyfileObjectFromMetadata(rowSet->data[i]);
     10625        psArrayAdd(returnSet, 0, object);
     10626        psFree(object);
     10627    }
     10628
     10629    psFree(rowSet);
     10630
     10631    return returnSet;
     10632}
     10633bool diffInputSkyfileDeleteObject(psDB *dbh, const diffInputSkyfileRow *object)
     10634{
     10635    psMetadata *where = diffInputSkyfileMetadataFromObject(object);
     10636    long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, 0);
     10637    psFree(where);
     10638    if (count < 0) {
     10639        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
     10640        return false;
     10641    }
     10642    if (count > 1) {
     10643        // XXX should this be a psAbort() instead?  It is possible that
     10644        // having an object match multiple rows was by design.
     10645        psError(PS_ERR_UNKNOWN, true, "diffInputSkyfileRow object matched more then one row.  Check your database schema");
     10646        return false;
     10647    }
     10648
     10649    return true;
     10650}
     10651long long diffInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     10652{
     10653    long long       deleted = 0;
     10654
     10655    for (long long i = 0; i < objects->n; i++) {
     10656        diffInputSkyfileRow *object = objects->data[i];
     10657        psMetadata *where = diffInputSkyfileMetadataFromObject(object);
     10658        long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
     10659        psFree(where);
     10660        if (count < 0) {
     10661            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
     10662            return count;
     10663        }
     10664
     10665        deleted += count;
     10666    }
     10667
     10668    return deleted;
     10669}
     10670bool diffInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
     10671{
     10672    PS_ASSERT_PTR_NON_NULL(objects, false);
     10673
     10674    psMetadata *output = psMetadataAlloc();
     10675    for (long i = 0; i < psArrayLength(objects); i++) {
     10676        psMetadata *md = diffInputSkyfileMetadataFromObject(objects->data[i]);
     10677        if (!psMetadataAddMetadata(
     10678            output,
     10679            PS_LIST_TAIL,
     10680            DIFFINPUTSKYFILE_TABLE_NAME,
     10681            PS_META_DUPLICATE_OK,
     10682            NULL,
     10683            md
     10684        )) {
     10685            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     10686            psFree(md);
     10687            psFree(output);
     10688            return false;
     10689        }
     10690        psFree(md);
     10691    }
     10692
     10693    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     10694        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     10695        psFree(output);
     10696    }
     10697    psFree(output);
     10698
     10699    return true;
     10700}
     10701bool diffInputSkyfilePrintObject(FILE *stream, diffInputSkyfileRow *object, bool mdcf)
     10702{
     10703    PS_ASSERT_PTR_NON_NULL(object, false);
     10704
     10705    psMetadata *md = diffInputSkyfileMetadataFromObject(object);
     10706
     10707    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     10708        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     10709        psFree(md);
     10710    }
     10711
     10712    psFree(md);
     10713
     10714    return true;
     10715}
     10716static void diffSkyfileRowFree(diffSkyfileRow *object);
     10717
     10718diffSkyfileRow *diffSkyfileRowAlloc(psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
     10719{
     10720    diffSkyfileRow  *_object;
     10721
     10722    _object = psAlloc(sizeof(diffSkyfileRow));
     10723    psMemSetDeallocator(_object, (psFreeFunc)diffSkyfileRowFree);
     10724
     10725    _object->diff_id = diff_id;
     10726    _object->uri = psStringCopy(uri);
     10727    _object->bg = bg;
     10728    _object->bg_stdev = bg_stdev;
     10729
     10730    return _object;
     10731}
     10732
     10733static void diffSkyfileRowFree(diffSkyfileRow *object)
     10734{
     10735    psFree(object->uri);
     10736}
     10737
     10738bool diffSkyfileCreateTable(psDB *dbh)
     10739{
     10740    psMetadata *md = psMetadataAlloc();
     10741    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key fkey(diff_id) ref diffRun(diff_id)", 0)) {
     10742        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10743        psFree(md);
     10744        return false;
     10745    }
     10746    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
     10747        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     10748        psFree(md);
     10749        return false;
     10750    }
     10751    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
     10752        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     10753        psFree(md);
     10754        return false;
     10755    }
     10756    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
     10757        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     10758        psFree(md);
     10759        return false;
     10760    }
     10761
     10762    bool status = psDBCreateTable(dbh, DIFFSKYFILE_TABLE_NAME, md);
     10763
     10764    psFree(md);
     10765
     10766    return status;
     10767}
     10768
     10769bool diffSkyfileDropTable(psDB *dbh)
     10770{
     10771    return psDBDropTable(dbh, DIFFSKYFILE_TABLE_NAME);
     10772}
     10773
     10774bool diffSkyfileInsert(psDB * dbh, psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
     10775{
     10776    psMetadata *md = psMetadataAlloc();
     10777    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
     10778        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10779        psFree(md);
     10780        return false;
     10781    }
     10782    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
     10783        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     10784        psFree(md);
     10785        return false;
     10786    }
     10787    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
     10788        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     10789        psFree(md);
     10790        return false;
     10791    }
     10792    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
     10793        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     10794        psFree(md);
     10795        return false;
     10796    }
     10797
     10798    bool status = psDBInsertOneRow(dbh, DIFFSKYFILE_TABLE_NAME, md);
     10799    psFree(md);
     10800
     10801    return status;
     10802}
     10803
     10804long long diffSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     10805{
     10806    long long       deleted = 0;
     10807
     10808    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
     10809    if (count < 0) {
     10810        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
     10811        return count;
     10812
     10813        deleted += count;
     10814    }
     10815
     10816    return deleted;
     10817}
     10818bool diffSkyfileInsertObject(psDB *dbh, diffSkyfileRow *object)
     10819{
     10820    return diffSkyfileInsert(dbh, object->diff_id, object->uri, object->bg, object->bg_stdev);
     10821}
     10822
     10823bool diffSkyfileInsertObjects(psDB *dbh, psArray *objects)
     10824{
     10825    for (long i = 0; i < psArrayLength(objects); i++) {
     10826        if (!diffSkyfileInsertObject(dbh, objects->data[i])) {
     10827            return false;
     10828        }
     10829    }
     10830
     10831    return true;
     10832}
     10833
     10834bool diffSkyfileInsertFits(psDB *dbh, const psFits *fits)
     10835{
     10836    psArray         *rowSet;
     10837
     10838    // move to (the first?) extension named  DIFFSKYFILE_TABLE_NAME
     10839    if (!psFitsMoveExtName(fits, DIFFSKYFILE_TABLE_NAME)) {
     10840        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFSKYFILE_TABLE_NAME);
     10841        return false;
     10842    }
     10843
     10844    // check HDU type
     10845    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     10846        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     10847        return false;
     10848    }
     10849
     10850    // read fits table
     10851    rowSet = psFitsReadTable(fits);
     10852    if (!rowSet) {
     10853        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     10854        psFree(rowSet);
     10855        return false;
     10856    }
     10857
     10858    if (!psDBInsertRows(dbh, DIFFSKYFILE_TABLE_NAME, rowSet)) {
     10859        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     10860        psFree(rowSet);
     10861        return false;
     10862    }
     10863
     10864    psFree(rowSet);
     10865
     10866    return true;
     10867}
     10868
     10869bool diffSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     10870{
     10871    psArray         *rowSet;
     10872
     10873    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
     10874    if (!rowSet) {
     10875        return false;
     10876    }
     10877
     10878    // output to fits
     10879    if (!psFitsWriteTable(fits, NULL, rowSet, DIFFSKYFILE_TABLE_NAME)) {
     10880        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     10881        psFree(rowSet);
     10882        return false;
     10883    }
     10884
     10885    psFree(rowSet);
     10886
     10887    return true;
     10888}
     10889
     10890psMetadata *diffSkyfileMetadataFromObject(const diffSkyfileRow *object)
     10891{
     10892    psMetadata *md = psMetadataAlloc();
     10893    if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
     10894        psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
     10895        psFree(md);
     10896        return false;
     10897    }
     10898    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
     10899        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     10900        psFree(md);
     10901        return false;
     10902    }
     10903    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
     10904        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     10905        psFree(md);
     10906        return false;
     10907    }
     10908    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
     10909        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     10910        psFree(md);
     10911        return false;
     10912    }
     10913
     10914
     10915    return md;
     10916}
     10917
     10918diffSkyfileRow *diffSkyfileObjectFromMetadata(psMetadata *md)
     10919{
     10920
     10921bool status = false;
     10922    psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
     10923    if (!status) {
     10924        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
     10925        return false;
     10926    }
     10927    char* uri = psMetadataLookupPtr(&status, md, "uri");
     10928    if (!status) {
     10929        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
     10930        return false;
     10931    }
     10932    psF64 bg = psMetadataLookupF64(&status, md, "bg");
     10933    if (!status) {
     10934        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
     10935        return false;
     10936    }
     10937    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
     10938    if (!status) {
     10939        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
     10940        return false;
     10941    }
     10942
     10943    return diffSkyfileRowAlloc(diff_id, uri, bg, bg_stdev);
     10944}
     10945psArray *diffSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     10946{
     10947    psArray         *rowSet;
     10948    psArray         *returnSet;
     10949    psU64           i;
     10950
     10951    rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
     10952    if (!rowSet) {
     10953        return NULL;
     10954    }
     10955
     10956    // convert psMetadata rows to row objects
     10957
     10958    returnSet = psArrayAllocEmpty(rowSet->n);
     10959
     10960    for (i = 0; i < rowSet->n; i++) {
     10961        diffSkyfileRow *object = diffSkyfileObjectFromMetadata(rowSet->data[i]);
     10962        psArrayAdd(returnSet, 0, object);
     10963        psFree(object);
     10964    }
     10965
     10966    psFree(rowSet);
     10967
     10968    return returnSet;
     10969}
     10970bool diffSkyfileDeleteObject(psDB *dbh, const diffSkyfileRow *object)
     10971{
     10972    psMetadata *where = diffSkyfileMetadataFromObject(object);
     10973    long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, 0);
     10974    psFree(where);
     10975    if (count < 0) {
     10976        psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
     10977        return false;
     10978    }
     10979    if (count > 1) {
     10980        // XXX should this be a psAbort() instead?  It is possible that
     10981        // having an object match multiple rows was by design.
     10982        psError(PS_ERR_UNKNOWN, true, "diffSkyfileRow object matched more then one row.  Check your database schema");
     10983        return false;
     10984    }
     10985
     10986    return true;
     10987}
     10988long long diffSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     10989{
     10990    long long       deleted = 0;
     10991
     10992    for (long long i = 0; i < objects->n; i++) {
     10993        diffSkyfileRow *object = objects->data[i];
     10994        psMetadata *where = diffSkyfileMetadataFromObject(object);
     10995        long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
     10996        psFree(where);
     10997        if (count < 0) {
     10998            psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
     10999            return count;
     11000        }
     11001
     11002        deleted += count;
     11003    }
     11004
     11005    return deleted;
     11006}
     11007bool diffSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
     11008{
     11009    PS_ASSERT_PTR_NON_NULL(objects, false);
     11010
     11011    psMetadata *output = psMetadataAlloc();
     11012    for (long i = 0; i < psArrayLength(objects); i++) {
     11013        psMetadata *md = diffSkyfileMetadataFromObject(objects->data[i]);
     11014        if (!psMetadataAddMetadata(
     11015            output,
     11016            PS_LIST_TAIL,
     11017            DIFFSKYFILE_TABLE_NAME,
     11018            PS_META_DUPLICATE_OK,
     11019            NULL,
     11020            md
     11021        )) {
     11022            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     11023            psFree(md);
     11024            psFree(output);
     11025            return false;
     11026        }
     11027        psFree(md);
     11028    }
     11029
     11030    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     11031        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     11032        psFree(output);
     11033    }
     11034    psFree(output);
     11035
     11036    return true;
     11037}
     11038bool diffSkyfilePrintObject(FILE *stream, diffSkyfileRow *object, bool mdcf)
     11039{
     11040    PS_ASSERT_PTR_NON_NULL(object, false);
     11041
     11042    psMetadata *md = diffSkyfileMetadataFromObject(object);
     11043
     11044    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     11045        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     11046        psFree(md);
     11047    }
     11048
     11049    psFree(md);
     11050
     11051    return true;
     11052}
     11053static void stackRunRowFree(stackRunRow *object);
     11054
     11055stackRunRow *stackRunRowAlloc(psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
     11056{
     11057    stackRunRow     *_object;
     11058
     11059    _object = psAlloc(sizeof(stackRunRow));
     11060    psMemSetDeallocator(_object, (psFreeFunc)stackRunRowFree);
     11061
     11062    _object->stack_id = stack_id;
     11063    _object->state = psStringCopy(state);
     11064    _object->workdir = psStringCopy(workdir);
     11065    _object->dvodb = psStringCopy(dvodb);
     11066    _object->registered = psTimeCopy(registered);
     11067    _object->skycell_id = psStringCopy(skycell_id);
     11068    _object->tess_id = psStringCopy(tess_id);
     11069
     11070    return _object;
     11071}
     11072
     11073static void stackRunRowFree(stackRunRow *object)
     11074{
     11075    psFree(object->state);
     11076    psFree(object->workdir);
     11077    psFree(object->dvodb);
     11078    psFree(object->registered);
     11079    psFree(object->skycell_id);
     11080    psFree(object->tess_id);
     11081}
     11082
     11083bool stackRunCreateTable(psDB *dbh)
     11084{
     11085    psMetadata *md = psMetadataAlloc();
     11086    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
     11087        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11088        psFree(md);
     11089        return false;
     11090    }
     11091    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
     11092        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     11093        psFree(md);
     11094        return false;
     11095    }
     11096    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
     11097        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     11098        psFree(md);
     11099        return false;
     11100    }
     11101    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
     11102        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     11103        psFree(md);
     11104        return false;
     11105    }
     11106    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
     11107        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     11108        psFree(md);
     11109        return false;
     11110    }
     11111    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
     11112        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     11113        psFree(md);
     11114        return false;
     11115    }
     11116    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
     11117        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     11118        psFree(md);
     11119        return false;
     11120    }
     11121
     11122    bool status = psDBCreateTable(dbh, STACKRUN_TABLE_NAME, md);
     11123
     11124    psFree(md);
     11125
     11126    return status;
     11127}
     11128
     11129bool stackRunDropTable(psDB *dbh)
     11130{
     11131    return psDBDropTable(dbh, STACKRUN_TABLE_NAME);
     11132}
     11133
     11134bool 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)
     11135{
     11136    psMetadata *md = psMetadataAlloc();
     11137    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
     11138        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11139        psFree(md);
     11140        return false;
     11141    }
     11142    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
     11143        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     11144        psFree(md);
     11145        return false;
     11146    }
     11147    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
     11148        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     11149        psFree(md);
     11150        return false;
     11151    }
     11152    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
     11153        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     11154        psFree(md);
     11155        return false;
     11156    }
     11157    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
     11158        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     11159        psFree(md);
     11160        return false;
     11161    }
     11162    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
     11163        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     11164        psFree(md);
     11165        return false;
     11166    }
     11167    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
     11168        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     11169        psFree(md);
     11170        return false;
     11171    }
     11172
     11173    bool status = psDBInsertOneRow(dbh, STACKRUN_TABLE_NAME, md);
     11174    psFree(md);
     11175
     11176    return status;
     11177}
     11178
     11179long long stackRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     11180{
     11181    long long       deleted = 0;
     11182
     11183    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
     11184    if (count < 0) {
     11185        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
     11186        return count;
     11187
     11188        deleted += count;
     11189    }
     11190
     11191    return deleted;
     11192}
     11193bool stackRunInsertObject(psDB *dbh, stackRunRow *object)
     11194{
     11195    return stackRunInsert(dbh, object->stack_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
     11196}
     11197
     11198bool stackRunInsertObjects(psDB *dbh, psArray *objects)
     11199{
     11200    for (long i = 0; i < psArrayLength(objects); i++) {
     11201        if (!stackRunInsertObject(dbh, objects->data[i])) {
     11202            return false;
     11203        }
     11204    }
     11205
     11206    return true;
     11207}
     11208
     11209bool stackRunInsertFits(psDB *dbh, const psFits *fits)
     11210{
     11211    psArray         *rowSet;
     11212
     11213    // move to (the first?) extension named  STACKRUN_TABLE_NAME
     11214    if (!psFitsMoveExtName(fits, STACKRUN_TABLE_NAME)) {
     11215        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKRUN_TABLE_NAME);
     11216        return false;
     11217    }
     11218
     11219    // check HDU type
     11220    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     11221        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     11222        return false;
     11223    }
     11224
     11225    // read fits table
     11226    rowSet = psFitsReadTable(fits);
     11227    if (!rowSet) {
     11228        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     11229        psFree(rowSet);
     11230        return false;
     11231    }
     11232
     11233    if (!psDBInsertRows(dbh, STACKRUN_TABLE_NAME, rowSet)) {
     11234        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     11235        psFree(rowSet);
     11236        return false;
     11237    }
     11238
     11239    psFree(rowSet);
     11240
     11241    return true;
     11242}
     11243
     11244bool stackRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     11245{
     11246    psArray         *rowSet;
     11247
     11248    rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
     11249    if (!rowSet) {
     11250        return false;
     11251    }
     11252
     11253    // output to fits
     11254    if (!psFitsWriteTable(fits, NULL, rowSet, STACKRUN_TABLE_NAME)) {
     11255        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     11256        psFree(rowSet);
     11257        return false;
     11258    }
     11259
     11260    psFree(rowSet);
     11261
     11262    return true;
     11263}
     11264
     11265psMetadata *stackRunMetadataFromObject(const stackRunRow *object)
     11266{
     11267    psMetadata *md = psMetadataAlloc();
     11268    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
     11269        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11270        psFree(md);
     11271        return false;
     11272    }
     11273    if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
     11274        psError(PS_ERR_UNKNOWN, false, "failed to add item state");
     11275        psFree(md);
     11276        return false;
     11277    }
     11278    if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
     11279        psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
     11280        psFree(md);
     11281        return false;
     11282    }
     11283    if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
     11284        psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
     11285        psFree(md);
     11286        return false;
     11287    }
     11288    if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
     11289        psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
     11290        psFree(md);
     11291        return false;
     11292    }
     11293    if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
     11294        psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
     11295        psFree(md);
     11296        return false;
     11297    }
     11298    if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
     11299        psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
     11300        psFree(md);
     11301        return false;
     11302    }
     11303
     11304
     11305    return md;
     11306}
     11307
     11308stackRunRow *stackRunObjectFromMetadata(psMetadata *md)
     11309{
     11310
     11311bool status = false;
     11312    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
     11313    if (!status) {
     11314        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
     11315        return false;
     11316    }
     11317    char* state = psMetadataLookupPtr(&status, md, "state");
     11318    if (!status) {
     11319        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
     11320        return false;
     11321    }
     11322    char* workdir = psMetadataLookupPtr(&status, md, "workdir");
     11323    if (!status) {
     11324        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
     11325        return false;
     11326    }
     11327    char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
     11328    if (!status) {
     11329        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
     11330        return false;
     11331    }
     11332    psTime* registered = psMetadataLookupPtr(&status, md, "registered");
     11333    if (!status) {
     11334        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
     11335        return false;
     11336    }
     11337    char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
     11338    if (!status) {
     11339        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
     11340        return false;
     11341    }
     11342    char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
     11343    if (!status) {
     11344        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
     11345        return false;
     11346    }
     11347
     11348    return stackRunRowAlloc(stack_id, state, workdir, dvodb, registered, skycell_id, tess_id);
     11349}
     11350psArray *stackRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     11351{
     11352    psArray         *rowSet;
     11353    psArray         *returnSet;
     11354    psU64           i;
     11355
     11356    rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
     11357    if (!rowSet) {
     11358        return NULL;
     11359    }
     11360
     11361    // convert psMetadata rows to row objects
     11362
     11363    returnSet = psArrayAllocEmpty(rowSet->n);
     11364
     11365    for (i = 0; i < rowSet->n; i++) {
     11366        stackRunRow *object = stackRunObjectFromMetadata(rowSet->data[i]);
     11367        psArrayAdd(returnSet, 0, object);
     11368        psFree(object);
     11369    }
     11370
     11371    psFree(rowSet);
     11372
     11373    return returnSet;
     11374}
     11375bool stackRunDeleteObject(psDB *dbh, const stackRunRow *object)
     11376{
     11377    psMetadata *where = stackRunMetadataFromObject(object);
     11378    long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, 0);
     11379    psFree(where);
     11380    if (count < 0) {
     11381        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
     11382        return false;
     11383    }
     11384    if (count > 1) {
     11385        // XXX should this be a psAbort() instead?  It is possible that
     11386        // having an object match multiple rows was by design.
     11387        psError(PS_ERR_UNKNOWN, true, "stackRunRow object matched more then one row.  Check your database schema");
     11388        return false;
     11389    }
     11390
     11391    return true;
     11392}
     11393long long stackRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     11394{
     11395    long long       deleted = 0;
     11396
     11397    for (long long i = 0; i < objects->n; i++) {
     11398        stackRunRow *object = objects->data[i];
     11399        psMetadata *where = stackRunMetadataFromObject(object);
     11400        long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
     11401        psFree(where);
     11402        if (count < 0) {
     11403            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
     11404            return count;
     11405        }
     11406
     11407        deleted += count;
     11408    }
     11409
     11410    return deleted;
     11411}
     11412bool stackRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
     11413{
     11414    PS_ASSERT_PTR_NON_NULL(objects, false);
     11415
     11416    psMetadata *output = psMetadataAlloc();
     11417    for (long i = 0; i < psArrayLength(objects); i++) {
     11418        psMetadata *md = stackRunMetadataFromObject(objects->data[i]);
     11419        if (!psMetadataAddMetadata(
     11420            output,
     11421            PS_LIST_TAIL,
     11422            STACKRUN_TABLE_NAME,
     11423            PS_META_DUPLICATE_OK,
     11424            NULL,
     11425            md
     11426        )) {
     11427            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     11428            psFree(md);
     11429            psFree(output);
     11430            return false;
     11431        }
     11432        psFree(md);
     11433    }
     11434
     11435    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     11436        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     11437        psFree(output);
     11438    }
     11439    psFree(output);
     11440
     11441    return true;
     11442}
     11443bool stackRunPrintObject(FILE *stream, stackRunRow *object, bool mdcf)
     11444{
     11445    PS_ASSERT_PTR_NON_NULL(object, false);
     11446
     11447    psMetadata *md = stackRunMetadataFromObject(object);
     11448
     11449    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     11450        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     11451        psFree(md);
     11452    }
     11453
     11454    psFree(md);
     11455
     11456    return true;
     11457}
     11458static void stackInputSkyfileRowFree(stackInputSkyfileRow *object);
     11459
     11460stackInputSkyfileRow *stackInputSkyfileRowAlloc(psS64 stack_id, psS64 warp_id)
     11461{
     11462    stackInputSkyfileRow *_object;
     11463
     11464    _object = psAlloc(sizeof(stackInputSkyfileRow));
     11465    psMemSetDeallocator(_object, (psFreeFunc)stackInputSkyfileRowFree);
     11466
     11467    _object->stack_id = stack_id;
     11468    _object->warp_id = warp_id;
     11469
     11470    return _object;
     11471}
     11472
     11473static void stackInputSkyfileRowFree(stackInputSkyfileRow *object)
     11474{
     11475}
     11476
     11477bool stackInputSkyfileCreateTable(psDB *dbh)
     11478{
     11479    psMetadata *md = psMetadataAlloc();
     11480    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key fkey(stack_id) ref stackRun(stack_id)", 0)) {
     11481        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11482        psFree(md);
     11483        return false;
     11484    }
     11485    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key fkey(warp_id) ref warpSkyfile(warp_id)", 0)) {
     11486        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     11487        psFree(md);
     11488        return false;
     11489    }
     11490
     11491    bool status = psDBCreateTable(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
     11492
     11493    psFree(md);
     11494
     11495    return status;
     11496}
     11497
     11498bool stackInputSkyfileDropTable(psDB *dbh)
     11499{
     11500    return psDBDropTable(dbh, STACKINPUTSKYFILE_TABLE_NAME);
     11501}
     11502
     11503bool stackInputSkyfileInsert(psDB * dbh, psS64 stack_id, psS64 warp_id)
     11504{
     11505    psMetadata *md = psMetadataAlloc();
     11506    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
     11507        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11508        psFree(md);
     11509        return false;
     11510    }
     11511    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
     11512        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     11513        psFree(md);
     11514        return false;
     11515    }
     11516
     11517    bool status = psDBInsertOneRow(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
     11518    psFree(md);
     11519
     11520    return status;
     11521}
     11522
     11523long long stackInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     11524{
     11525    long long       deleted = 0;
     11526
     11527    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
     11528    if (count < 0) {
     11529        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
     11530        return count;
     11531
     11532        deleted += count;
     11533    }
     11534
     11535    return deleted;
     11536}
     11537bool stackInputSkyfileInsertObject(psDB *dbh, stackInputSkyfileRow *object)
     11538{
     11539    return stackInputSkyfileInsert(dbh, object->stack_id, object->warp_id);
     11540}
     11541
     11542bool stackInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
     11543{
     11544    for (long i = 0; i < psArrayLength(objects); i++) {
     11545        if (!stackInputSkyfileInsertObject(dbh, objects->data[i])) {
     11546            return false;
     11547        }
     11548    }
     11549
     11550    return true;
     11551}
     11552
     11553bool stackInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
     11554{
     11555    psArray         *rowSet;
     11556
     11557    // move to (the first?) extension named  STACKINPUTSKYFILE_TABLE_NAME
     11558    if (!psFitsMoveExtName(fits, STACKINPUTSKYFILE_TABLE_NAME)) {
     11559        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKINPUTSKYFILE_TABLE_NAME);
     11560        return false;
     11561    }
     11562
     11563    // check HDU type
     11564    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     11565        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     11566        return false;
     11567    }
     11568
     11569    // read fits table
     11570    rowSet = psFitsReadTable(fits);
     11571    if (!rowSet) {
     11572        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     11573        psFree(rowSet);
     11574        return false;
     11575    }
     11576
     11577    if (!psDBInsertRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, rowSet)) {
     11578        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     11579        psFree(rowSet);
     11580        return false;
     11581    }
     11582
     11583    psFree(rowSet);
     11584
     11585    return true;
     11586}
     11587
     11588bool stackInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     11589{
     11590    psArray         *rowSet;
     11591
     11592    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
     11593    if (!rowSet) {
     11594        return false;
     11595    }
     11596
     11597    // output to fits
     11598    if (!psFitsWriteTable(fits, NULL, rowSet, STACKINPUTSKYFILE_TABLE_NAME)) {
     11599        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     11600        psFree(rowSet);
     11601        return false;
     11602    }
     11603
     11604    psFree(rowSet);
     11605
     11606    return true;
     11607}
     11608
     11609psMetadata *stackInputSkyfileMetadataFromObject(const stackInputSkyfileRow *object)
     11610{
     11611    psMetadata *md = psMetadataAlloc();
     11612    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
     11613        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11614        psFree(md);
     11615        return false;
     11616    }
     11617    if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
     11618        psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
     11619        psFree(md);
     11620        return false;
     11621    }
     11622
     11623
     11624    return md;
     11625}
     11626
     11627stackInputSkyfileRow *stackInputSkyfileObjectFromMetadata(psMetadata *md)
     11628{
     11629
     11630bool status = false;
     11631    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
     11632    if (!status) {
     11633        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
     11634        return false;
     11635    }
     11636    psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
     11637    if (!status) {
     11638        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
     11639        return false;
     11640    }
     11641
     11642    return stackInputSkyfileRowAlloc(stack_id, warp_id);
     11643}
     11644psArray *stackInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     11645{
     11646    psArray         *rowSet;
     11647    psArray         *returnSet;
     11648    psU64           i;
     11649
     11650    rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
     11651    if (!rowSet) {
     11652        return NULL;
     11653    }
     11654
     11655    // convert psMetadata rows to row objects
     11656
     11657    returnSet = psArrayAllocEmpty(rowSet->n);
     11658
     11659    for (i = 0; i < rowSet->n; i++) {
     11660        stackInputSkyfileRow *object = stackInputSkyfileObjectFromMetadata(rowSet->data[i]);
     11661        psArrayAdd(returnSet, 0, object);
     11662        psFree(object);
     11663    }
     11664
     11665    psFree(rowSet);
     11666
     11667    return returnSet;
     11668}
     11669bool stackInputSkyfileDeleteObject(psDB *dbh, const stackInputSkyfileRow *object)
     11670{
     11671    psMetadata *where = stackInputSkyfileMetadataFromObject(object);
     11672    long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, 0);
     11673    psFree(where);
     11674    if (count < 0) {
     11675        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
     11676        return false;
     11677    }
     11678    if (count > 1) {
     11679        // XXX should this be a psAbort() instead?  It is possible that
     11680        // having an object match multiple rows was by design.
     11681        psError(PS_ERR_UNKNOWN, true, "stackInputSkyfileRow object matched more then one row.  Check your database schema");
     11682        return false;
     11683    }
     11684
     11685    return true;
     11686}
     11687long long stackInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     11688{
     11689    long long       deleted = 0;
     11690
     11691    for (long long i = 0; i < objects->n; i++) {
     11692        stackInputSkyfileRow *object = objects->data[i];
     11693        psMetadata *where = stackInputSkyfileMetadataFromObject(object);
     11694        long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
     11695        psFree(where);
     11696        if (count < 0) {
     11697            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
     11698            return count;
     11699        }
     11700
     11701        deleted += count;
     11702    }
     11703
     11704    return deleted;
     11705}
     11706bool stackInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
     11707{
     11708    PS_ASSERT_PTR_NON_NULL(objects, false);
     11709
     11710    psMetadata *output = psMetadataAlloc();
     11711    for (long i = 0; i < psArrayLength(objects); i++) {
     11712        psMetadata *md = stackInputSkyfileMetadataFromObject(objects->data[i]);
     11713        if (!psMetadataAddMetadata(
     11714            output,
     11715            PS_LIST_TAIL,
     11716            STACKINPUTSKYFILE_TABLE_NAME,
     11717            PS_META_DUPLICATE_OK,
     11718            NULL,
     11719            md
     11720        )) {
     11721            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     11722            psFree(md);
     11723            psFree(output);
     11724            return false;
     11725        }
     11726        psFree(md);
     11727    }
     11728
     11729    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     11730        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     11731        psFree(output);
     11732    }
     11733    psFree(output);
     11734
     11735    return true;
     11736}
     11737bool stackInputSkyfilePrintObject(FILE *stream, stackInputSkyfileRow *object, bool mdcf)
     11738{
     11739    PS_ASSERT_PTR_NON_NULL(object, false);
     11740
     11741    psMetadata *md = stackInputSkyfileMetadataFromObject(object);
     11742
     11743    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     11744        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     11745        psFree(md);
     11746    }
     11747
     11748    psFree(md);
     11749
     11750    return true;
     11751}
     11752static void stackSumSkyfileRowFree(stackSumSkyfileRow *object);
     11753
     11754stackSumSkyfileRow *stackSumSkyfileRowAlloc(psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
     11755{
     11756    stackSumSkyfileRow *_object;
     11757
     11758    _object = psAlloc(sizeof(stackSumSkyfileRow));
     11759    psMemSetDeallocator(_object, (psFreeFunc)stackSumSkyfileRowFree);
     11760
     11761    _object->stack_id = stack_id;
     11762    _object->uri = psStringCopy(uri);
     11763    _object->bg = bg;
     11764    _object->bg_stdev = bg_stdev;
     11765
     11766    return _object;
     11767}
     11768
     11769static void stackSumSkyfileRowFree(stackSumSkyfileRow *object)
     11770{
     11771    psFree(object->uri);
     11772}
     11773
     11774bool stackSumSkyfileCreateTable(psDB *dbh)
     11775{
     11776    psMetadata *md = psMetadataAlloc();
     11777    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key fkey(stack_id) ref stackRun(stack_id)", 0)) {
     11778        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11779        psFree(md);
     11780        return false;
     11781    }
     11782    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
     11783        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     11784        psFree(md);
     11785        return false;
     11786    }
     11787    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
     11788        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     11789        psFree(md);
     11790        return false;
     11791    }
     11792    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
     11793        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     11794        psFree(md);
     11795        return false;
     11796    }
     11797
     11798    bool status = psDBCreateTable(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
     11799
     11800    psFree(md);
     11801
     11802    return status;
     11803}
     11804
     11805bool stackSumSkyfileDropTable(psDB *dbh)
     11806{
     11807    return psDBDropTable(dbh, STACKSUMSKYFILE_TABLE_NAME);
     11808}
     11809
     11810bool stackSumSkyfileInsert(psDB * dbh, psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
     11811{
     11812    psMetadata *md = psMetadataAlloc();
     11813    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
     11814        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11815        psFree(md);
     11816        return false;
     11817    }
     11818    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
     11819        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     11820        psFree(md);
     11821        return false;
     11822    }
     11823    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
     11824        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     11825        psFree(md);
     11826        return false;
     11827    }
     11828    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
     11829        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     11830        psFree(md);
     11831        return false;
     11832    }
     11833
     11834    bool status = psDBInsertOneRow(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
     11835    psFree(md);
     11836
     11837    return status;
     11838}
     11839
     11840long long stackSumSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
     11841{
     11842    long long       deleted = 0;
     11843
     11844    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
     11845    if (count < 0) {
     11846        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
     11847        return count;
     11848
     11849        deleted += count;
     11850    }
     11851
     11852    return deleted;
     11853}
     11854bool stackSumSkyfileInsertObject(psDB *dbh, stackSumSkyfileRow *object)
     11855{
     11856    return stackSumSkyfileInsert(dbh, object->stack_id, object->uri, object->bg, object->bg_stdev);
     11857}
     11858
     11859bool stackSumSkyfileInsertObjects(psDB *dbh, psArray *objects)
     11860{
     11861    for (long i = 0; i < psArrayLength(objects); i++) {
     11862        if (!stackSumSkyfileInsertObject(dbh, objects->data[i])) {
     11863            return false;
     11864        }
     11865    }
     11866
     11867    return true;
     11868}
     11869
     11870bool stackSumSkyfileInsertFits(psDB *dbh, const psFits *fits)
     11871{
     11872    psArray         *rowSet;
     11873
     11874    // move to (the first?) extension named  STACKSUMSKYFILE_TABLE_NAME
     11875    if (!psFitsMoveExtName(fits, STACKSUMSKYFILE_TABLE_NAME)) {
     11876        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKSUMSKYFILE_TABLE_NAME);
     11877        return false;
     11878    }
     11879
     11880    // check HDU type
     11881    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
     11882        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
     11883        return false;
     11884    }
     11885
     11886    // read fits table
     11887    rowSet = psFitsReadTable(fits);
     11888    if (!rowSet) {
     11889        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
     11890        psFree(rowSet);
     11891        return false;
     11892    }
     11893
     11894    if (!psDBInsertRows(dbh, STACKSUMSKYFILE_TABLE_NAME, rowSet)) {
     11895        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
     11896        psFree(rowSet);
     11897        return false;
     11898    }
     11899
     11900    psFree(rowSet);
     11901
     11902    return true;
     11903}
     11904
     11905bool stackSumSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
     11906{
     11907    psArray         *rowSet;
     11908
     11909    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
     11910    if (!rowSet) {
     11911        return false;
     11912    }
     11913
     11914    // output to fits
     11915    if (!psFitsWriteTable(fits, NULL, rowSet, STACKSUMSKYFILE_TABLE_NAME)) {
     11916        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
     11917        psFree(rowSet);
     11918        return false;
     11919    }
     11920
     11921    psFree(rowSet);
     11922
     11923    return true;
     11924}
     11925
     11926psMetadata *stackSumSkyfileMetadataFromObject(const stackSumSkyfileRow *object)
     11927{
     11928    psMetadata *md = psMetadataAlloc();
     11929    if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
     11930        psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
     11931        psFree(md);
     11932        return false;
     11933    }
     11934    if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
     11935        psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
     11936        psFree(md);
     11937        return false;
     11938    }
     11939    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
     11940        psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
     11941        psFree(md);
     11942        return false;
     11943    }
     11944    if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
     11945        psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
     11946        psFree(md);
     11947        return false;
     11948    }
     11949
     11950
     11951    return md;
     11952}
     11953
     11954stackSumSkyfileRow *stackSumSkyfileObjectFromMetadata(psMetadata *md)
     11955{
     11956
     11957bool status = false;
     11958    psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
     11959    if (!status) {
     11960        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
     11961        return false;
     11962    }
     11963    char* uri = psMetadataLookupPtr(&status, md, "uri");
     11964    if (!status) {
     11965        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
     11966        return false;
     11967    }
     11968    psF64 bg = psMetadataLookupF64(&status, md, "bg");
     11969    if (!status) {
     11970        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
     11971        return false;
     11972    }
     11973    psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
     11974    if (!status) {
     11975        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
     11976        return false;
     11977    }
     11978
     11979    return stackSumSkyfileRowAlloc(stack_id, uri, bg, bg_stdev);
     11980}
     11981psArray *stackSumSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
     11982{
     11983    psArray         *rowSet;
     11984    psArray         *returnSet;
     11985    psU64           i;
     11986
     11987    rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
     11988    if (!rowSet) {
     11989        return NULL;
     11990    }
     11991
     11992    // convert psMetadata rows to row objects
     11993
     11994    returnSet = psArrayAllocEmpty(rowSet->n);
     11995
     11996    for (i = 0; i < rowSet->n; i++) {
     11997        stackSumSkyfileRow *object = stackSumSkyfileObjectFromMetadata(rowSet->data[i]);
     11998        psArrayAdd(returnSet, 0, object);
     11999        psFree(object);
     12000    }
     12001
     12002    psFree(rowSet);
     12003
     12004    return returnSet;
     12005}
     12006bool stackSumSkyfileDeleteObject(psDB *dbh, const stackSumSkyfileRow *object)
     12007{
     12008    psMetadata *where = stackSumSkyfileMetadataFromObject(object);
     12009    long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, 0);
     12010    psFree(where);
     12011    if (count < 0) {
     12012        psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
     12013        return false;
     12014    }
     12015    if (count > 1) {
     12016        // XXX should this be a psAbort() instead?  It is possible that
     12017        // having an object match multiple rows was by design.
     12018        psError(PS_ERR_UNKNOWN, true, "stackSumSkyfileRow object matched more then one row.  Check your database schema");
     12019        return false;
     12020    }
     12021
     12022    return true;
     12023}
     12024long long stackSumSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
     12025{
     12026    long long       deleted = 0;
     12027
     12028    for (long long i = 0; i < objects->n; i++) {
     12029        stackSumSkyfileRow *object = objects->data[i];
     12030        psMetadata *where = stackSumSkyfileMetadataFromObject(object);
     12031        long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
     12032        psFree(where);
     12033        if (count < 0) {
     12034            psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
     12035            return count;
     12036        }
     12037
     12038        deleted += count;
     12039    }
     12040
     12041    return deleted;
     12042}
     12043bool stackSumSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
     12044{
     12045    PS_ASSERT_PTR_NON_NULL(objects, false);
     12046
     12047    psMetadata *output = psMetadataAlloc();
     12048    for (long i = 0; i < psArrayLength(objects); i++) {
     12049        psMetadata *md = stackSumSkyfileMetadataFromObject(objects->data[i]);
     12050        if (!psMetadataAddMetadata(
     12051            output,
     12052            PS_LIST_TAIL,
     12053            STACKSUMSKYFILE_TABLE_NAME,
     12054            PS_META_DUPLICATE_OK,
     12055            NULL,
     12056            md
     12057        )) {
     12058            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
     12059            psFree(md);
     12060            psFree(output);
     12061            return false;
     12062        }
     12063        psFree(md);
     12064    }
     12065
     12066    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
     12067        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
     12068        psFree(output);
     12069    }
     12070    psFree(output);
     12071
     12072    return true;
     12073}
     12074bool stackSumSkyfilePrintObject(FILE *stream, stackSumSkyfileRow *object, bool mdcf)
     12075{
     12076    PS_ASSERT_PTR_NON_NULL(object, false);
     12077
     12078    psMetadata *md = stackSumSkyfileMetadataFromObject(object);
    846012079
    846112080    if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
     
    1357017189    return true;
    1357117190}
    13572 static void warpRunRowFree(warpRunRow *object);
    13573 
    13574 warpRunRow *warpRunRowAlloc(psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
    13575 {
    13576     warpRunRow      *_object;
    13577 
    13578     _object = psAlloc(sizeof(warpRunRow));
    13579     psMemSetDeallocator(_object, (psFreeFunc)warpRunRowFree);
    13580 
    13581     _object->warp_id = warp_id;
    13582     _object->mode = psStringCopy(mode);
    13583     _object->state = psStringCopy(state);
    13584     _object->workdir = psStringCopy(workdir);
    13585     _object->dvodb = psStringCopy(dvodb);
    13586     _object->registered = psTimeCopy(registered);
    13587 
    13588     return _object;
    13589 }
    13590 
    13591 static void warpRunRowFree(warpRunRow *object)
    13592 {
    13593     psFree(object->mode);
    13594     psFree(object->state);
    13595     psFree(object->workdir);
    13596     psFree(object->dvodb);
    13597     psFree(object->registered);
    13598 }
    13599 
    13600 bool warpRunCreateTable(psDB *dbh)
    13601 {
    13602     psMetadata *md = psMetadataAlloc();
    13603     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
    13604         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    13605         psFree(md);
    13606         return false;
    13607     }
    13608     if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, "Key", "64")) {
    13609         psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
    13610         psFree(md);
    13611         return false;
    13612     }
    13613     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
    13614         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    13615         psFree(md);
    13616         return false;
    13617     }
    13618     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
    13619         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    13620         psFree(md);
    13621         return false;
    13622     }
    13623     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
    13624         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    13625         psFree(md);
    13626         return false;
    13627     }
    13628     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
    13629         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    13630         psFree(md);
    13631         return false;
    13632     }
    13633 
    13634     bool status = psDBCreateTable(dbh, WARPRUN_TABLE_NAME, md);
    13635 
    13636     psFree(md);
    13637 
    13638     return status;
    13639 }
    13640 
    13641 bool warpRunDropTable(psDB *dbh)
    13642 {
    13643     return psDBDropTable(dbh, WARPRUN_TABLE_NAME);
    13644 }
    13645 
    13646 bool warpRunInsert(psDB * dbh, psS64 warp_id, const char *mode, const char *state, const char *workdir, const char *dvodb, psTime* registered)
    13647 {
    13648     psMetadata *md = psMetadataAlloc();
    13649     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
    13650         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    13651         psFree(md);
    13652         return false;
    13653     }
    13654     if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, mode)) {
    13655         psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
    13656         psFree(md);
    13657         return false;
    13658     }
    13659     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
    13660         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    13661         psFree(md);
    13662         return false;
    13663     }
    13664     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
    13665         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    13666         psFree(md);
    13667         return false;
    13668     }
    13669     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
    13670         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    13671         psFree(md);
    13672         return false;
    13673     }
    13674     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
    13675         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    13676         psFree(md);
    13677         return false;
    13678     }
    13679 
    13680     bool status = psDBInsertOneRow(dbh, WARPRUN_TABLE_NAME, md);
    13681     psFree(md);
    13682 
    13683     return status;
    13684 }
    13685 
    13686 long long warpRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    13687 {
    13688     long long       deleted = 0;
    13689 
    13690     long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
    13691     if (count < 0) {
    13692         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
    13693         return count;
    13694 
    13695         deleted += count;
    13696     }
    13697 
    13698     return deleted;
    13699 }
    13700 bool warpRunInsertObject(psDB *dbh, warpRunRow *object)
    13701 {
    13702     return warpRunInsert(dbh, object->warp_id, object->mode, object->state, object->workdir, object->dvodb, object->registered);
    13703 }
    13704 
    13705 bool warpRunInsertObjects(psDB *dbh, psArray *objects)
    13706 {
    13707     for (long i = 0; i < psArrayLength(objects); i++) {
    13708         if (!warpRunInsertObject(dbh, objects->data[i])) {
    13709             return false;
    13710         }
    13711     }
    13712 
    13713     return true;
    13714 }
    13715 
    13716 bool warpRunInsertFits(psDB *dbh, const psFits *fits)
    13717 {
    13718     psArray         *rowSet;
    13719 
    13720     // move to (the first?) extension named  WARPRUN_TABLE_NAME
    13721     if (!psFitsMoveExtName(fits, WARPRUN_TABLE_NAME)) {
    13722         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPRUN_TABLE_NAME);
    13723         return false;
    13724     }
    13725 
    13726     // check HDU type
    13727     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    13728         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    13729         return false;
    13730     }
    13731 
    13732     // read fits table
    13733     rowSet = psFitsReadTable(fits);
    13734     if (!rowSet) {
    13735         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    13736         psFree(rowSet);
    13737         return false;
    13738     }
    13739 
    13740     if (!psDBInsertRows(dbh, WARPRUN_TABLE_NAME, rowSet)) {
    13741         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    13742         psFree(rowSet);
    13743         return false;
    13744     }
    13745 
    13746     psFree(rowSet);
    13747 
    13748     return true;
    13749 }
    13750 
    13751 bool warpRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    13752 {
    13753     psArray         *rowSet;
    13754 
    13755     rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
    13756     if (!rowSet) {
    13757         return false;
    13758     }
    13759 
    13760     // output to fits
    13761     if (!psFitsWriteTable(fits, NULL, rowSet, WARPRUN_TABLE_NAME)) {
    13762         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    13763         psFree(rowSet);
    13764         return false;
    13765     }
    13766 
    13767     psFree(rowSet);
    13768 
    13769     return true;
    13770 }
    13771 
    13772 psMetadata *warpRunMetadataFromObject(const warpRunRow *object)
    13773 {
    13774     psMetadata *md = psMetadataAlloc();
    13775     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
    13776         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    13777         psFree(md);
    13778         return false;
    13779     }
    13780     if (!psMetadataAdd(md, PS_LIST_TAIL, "mode", PS_DATA_STRING, NULL, object->mode)) {
    13781         psError(PS_ERR_UNKNOWN, false, "failed to add item mode");
    13782         psFree(md);
    13783         return false;
    13784     }
    13785     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
    13786         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    13787         psFree(md);
    13788         return false;
    13789     }
    13790     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
    13791         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    13792         psFree(md);
    13793         return false;
    13794     }
    13795     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
    13796         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    13797         psFree(md);
    13798         return false;
    13799     }
    13800     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
    13801         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    13802         psFree(md);
    13803         return false;
    13804     }
    13805 
    13806 
    13807     return md;
    13808 }
    13809 
    13810 warpRunRow *warpRunObjectFromMetadata(psMetadata *md)
    13811 {
    13812 
    13813 bool status = false;
    13814     psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
    13815     if (!status) {
    13816         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
    13817         return false;
    13818     }
    13819     char* mode = psMetadataLookupPtr(&status, md, "mode");
    13820     if (!status) {
    13821         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item mode");
    13822         return false;
    13823     }
    13824     char* state = psMetadataLookupPtr(&status, md, "state");
    13825     if (!status) {
    13826         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
    13827         return false;
    13828     }
    13829     char* workdir = psMetadataLookupPtr(&status, md, "workdir");
    13830     if (!status) {
    13831         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
    13832         return false;
    13833     }
    13834     char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
    13835     if (!status) {
    13836         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
    13837         return false;
    13838     }
    13839     psTime* registered = psMetadataLookupPtr(&status, md, "registered");
    13840     if (!status) {
    13841         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
    13842         return false;
    13843     }
    13844 
    13845     return warpRunRowAlloc(warp_id, mode, state, workdir, dvodb, registered);
    13846 }
    13847 psArray *warpRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    13848 {
    13849     psArray         *rowSet;
    13850     psArray         *returnSet;
    13851     psU64           i;
    13852 
    13853     rowSet = psDBSelectRows(dbh, WARPRUN_TABLE_NAME, where, limit);
    13854     if (!rowSet) {
    13855         return NULL;
    13856     }
    13857 
    13858     // convert psMetadata rows to row objects
    13859 
    13860     returnSet = psArrayAllocEmpty(rowSet->n);
    13861 
    13862     for (i = 0; i < rowSet->n; i++) {
    13863         warpRunRow *object = warpRunObjectFromMetadata(rowSet->data[i]);
    13864         psArrayAdd(returnSet, 0, object);
    13865         psFree(object);
    13866     }
    13867 
    13868     psFree(rowSet);
    13869 
    13870     return returnSet;
    13871 }
    13872 bool warpRunDeleteObject(psDB *dbh, const warpRunRow *object)
    13873 {
    13874     psMetadata *where = warpRunMetadataFromObject(object);
    13875     long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, 0);
    13876     psFree(where);
    13877     if (count < 0) {
    13878         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
    13879         return false;
    13880     }
    13881     if (count > 1) {
    13882         // XXX should this be a psAbort() instead?  It is possible that
    13883         // having an object match multiple rows was by design.
    13884         psError(PS_ERR_UNKNOWN, true, "warpRunRow object matched more then one row.  Check your database schema");
    13885         return false;
    13886     }
    13887 
    13888     return true;
    13889 }
    13890 long long warpRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    13891 {
    13892     long long       deleted = 0;
    13893 
    13894     for (long long i = 0; i < objects->n; i++) {
    13895         warpRunRow *object = objects->data[i];
    13896         psMetadata *where = warpRunMetadataFromObject(object);
    13897         long long count = psDBDeleteRows(dbh, WARPRUN_TABLE_NAME, where, limit);
    13898         psFree(where);
    13899         if (count < 0) {
    13900             psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpRun");
    13901             return count;
    13902         }
    13903 
    13904         deleted += count;
    13905     }
    13906 
    13907     return deleted;
    13908 }
    13909 bool warpRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
    13910 {
    13911     PS_ASSERT_PTR_NON_NULL(objects, false);
    13912 
    13913     psMetadata *output = psMetadataAlloc();
    13914     for (long i = 0; i < psArrayLength(objects); i++) {
    13915         psMetadata *md = warpRunMetadataFromObject(objects->data[i]);
    13916         if (!psMetadataAddMetadata(
    13917             output,
    13918             PS_LIST_TAIL,
    13919             WARPRUN_TABLE_NAME,
    13920             PS_META_DUPLICATE_OK,
    13921             NULL,
    13922             md
    13923         )) {
    13924             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    13925             psFree(md);
    13926             psFree(output);
    13927             return false;
    13928         }
    13929         psFree(md);
    13930     }
    13931 
    13932     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    13933         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    13934         psFree(output);
    13935     }
    13936     psFree(output);
    13937 
    13938     return true;
    13939 }
    13940 bool warpRunPrintObject(FILE *stream, warpRunRow *object, bool mdcf)
    13941 {
    13942     PS_ASSERT_PTR_NON_NULL(object, false);
    13943 
    13944     psMetadata *md = warpRunMetadataFromObject(object);
    13945 
    13946     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    13947         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    13948         psFree(md);
    13949     }
    13950 
    13951     psFree(md);
    13952 
    13953     return true;
    13954 }
    13955 static void warpInputExpRowFree(warpInputExpRow *object);
    13956 
    13957 warpInputExpRow *warpInputExpRowAlloc(psS64 warp_id, psS64 cam_id, bool magiced)
    13958 {
    13959     warpInputExpRow *_object;
    13960 
    13961     _object = psAlloc(sizeof(warpInputExpRow));
    13962     psMemSetDeallocator(_object, (psFreeFunc)warpInputExpRowFree);
    13963 
    13964     _object->warp_id = warp_id;
    13965     _object->cam_id = cam_id;
    13966     _object->magiced = magiced;
    13967 
    13968     return _object;
    13969 }
    13970 
    13971 static void warpInputExpRowFree(warpInputExpRow *object)
    13972 {
    13973 }
    13974 
    13975 bool warpInputExpCreateTable(psDB *dbh)
    13976 {
    13977     psMetadata *md = psMetadataAlloc();
    13978     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
    13979         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    13980         psFree(md);
    13981         return false;
    13982     }
    13983     if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
    13984         psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
    13985         psFree(md);
    13986         return false;
    13987     }
    13988     if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, "Key", 0)) {
    13989         psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
    13990         psFree(md);
    13991         return false;
    13992     }
    13993 
    13994     bool status = psDBCreateTable(dbh, WARPINPUTEXP_TABLE_NAME, md);
    13995 
    13996     psFree(md);
    13997 
    13998     return status;
    13999 }
    14000 
    14001 bool warpInputExpDropTable(psDB *dbh)
    14002 {
    14003     return psDBDropTable(dbh, WARPINPUTEXP_TABLE_NAME);
    14004 }
    14005 
    14006 bool warpInputExpInsert(psDB * dbh, psS64 warp_id, psS64 cam_id, bool magiced)
    14007 {
    14008     psMetadata *md = psMetadataAlloc();
    14009     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
    14010         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14011         psFree(md);
    14012         return false;
    14013     }
    14014     if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
    14015         psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
    14016         psFree(md);
    14017         return false;
    14018     }
    14019     if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, magiced)) {
    14020         psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
    14021         psFree(md);
    14022         return false;
    14023     }
    14024 
    14025     bool status = psDBInsertOneRow(dbh, WARPINPUTEXP_TABLE_NAME, md);
    14026     psFree(md);
    14027 
    14028     return status;
    14029 }
    14030 
    14031 long long warpInputExpDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    14032 {
    14033     long long       deleted = 0;
    14034 
    14035     long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
    14036     if (count < 0) {
    14037         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
    14038         return count;
    14039 
    14040         deleted += count;
    14041     }
    14042 
    14043     return deleted;
    14044 }
    14045 bool warpInputExpInsertObject(psDB *dbh, warpInputExpRow *object)
    14046 {
    14047     return warpInputExpInsert(dbh, object->warp_id, object->cam_id, object->magiced);
    14048 }
    14049 
    14050 bool warpInputExpInsertObjects(psDB *dbh, psArray *objects)
    14051 {
    14052     for (long i = 0; i < psArrayLength(objects); i++) {
    14053         if (!warpInputExpInsertObject(dbh, objects->data[i])) {
    14054             return false;
    14055         }
    14056     }
    14057 
    14058     return true;
    14059 }
    14060 
    14061 bool warpInputExpInsertFits(psDB *dbh, const psFits *fits)
    14062 {
    14063     psArray         *rowSet;
    14064 
    14065     // move to (the first?) extension named  WARPINPUTEXP_TABLE_NAME
    14066     if (!psFitsMoveExtName(fits, WARPINPUTEXP_TABLE_NAME)) {
    14067         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPINPUTEXP_TABLE_NAME);
    14068         return false;
    14069     }
    14070 
    14071     // check HDU type
    14072     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    14073         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    14074         return false;
    14075     }
    14076 
    14077     // read fits table
    14078     rowSet = psFitsReadTable(fits);
    14079     if (!rowSet) {
    14080         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    14081         psFree(rowSet);
    14082         return false;
    14083     }
    14084 
    14085     if (!psDBInsertRows(dbh, WARPINPUTEXP_TABLE_NAME, rowSet)) {
    14086         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    14087         psFree(rowSet);
    14088         return false;
    14089     }
    14090 
    14091     psFree(rowSet);
    14092 
    14093     return true;
    14094 }
    14095 
    14096 bool warpInputExpSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    14097 {
    14098     psArray         *rowSet;
    14099 
    14100     rowSet = psDBSelectRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
    14101     if (!rowSet) {
    14102         return false;
    14103     }
    14104 
    14105     // output to fits
    14106     if (!psFitsWriteTable(fits, NULL, rowSet, WARPINPUTEXP_TABLE_NAME)) {
    14107         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    14108         psFree(rowSet);
    14109         return false;
    14110     }
    14111 
    14112     psFree(rowSet);
    14113 
    14114     return true;
    14115 }
    14116 
    14117 psMetadata *warpInputExpMetadataFromObject(const warpInputExpRow *object)
    14118 {
    14119     psMetadata *md = psMetadataAlloc();
    14120     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
    14121         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14122         psFree(md);
    14123         return false;
    14124     }
    14125     if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
    14126         psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
    14127         psFree(md);
    14128         return false;
    14129     }
    14130     if (!psMetadataAdd(md, PS_LIST_TAIL, "magiced", PS_DATA_BOOL, NULL, object->magiced)) {
    14131         psError(PS_ERR_UNKNOWN, false, "failed to add item magiced");
    14132         psFree(md);
    14133         return false;
    14134     }
    14135 
    14136 
    14137     return md;
    14138 }
    14139 
    14140 warpInputExpRow *warpInputExpObjectFromMetadata(psMetadata *md)
    14141 {
    14142 
    14143 bool status = false;
    14144     psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
    14145     if (!status) {
    14146         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
    14147         return false;
    14148     }
    14149     psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
    14150     if (!status) {
    14151         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
    14152         return false;
    14153     }
    14154     bool magiced = psMetadataLookupBool(&status, md, "magiced");
    14155     if (!status) {
    14156         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item magiced");
    14157         return false;
    14158     }
    14159 
    14160     return warpInputExpRowAlloc(warp_id, cam_id, magiced);
    14161 }
    14162 psArray *warpInputExpSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    14163 {
    14164     psArray         *rowSet;
    14165     psArray         *returnSet;
    14166     psU64           i;
    14167 
    14168     rowSet = psDBSelectRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
    14169     if (!rowSet) {
    14170         return NULL;
    14171     }
    14172 
    14173     // convert psMetadata rows to row objects
    14174 
    14175     returnSet = psArrayAllocEmpty(rowSet->n);
    14176 
    14177     for (i = 0; i < rowSet->n; i++) {
    14178         warpInputExpRow *object = warpInputExpObjectFromMetadata(rowSet->data[i]);
    14179         psArrayAdd(returnSet, 0, object);
    14180         psFree(object);
    14181     }
    14182 
    14183     psFree(rowSet);
    14184 
    14185     return returnSet;
    14186 }
    14187 bool warpInputExpDeleteObject(psDB *dbh, const warpInputExpRow *object)
    14188 {
    14189     psMetadata *where = warpInputExpMetadataFromObject(object);
    14190     long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, 0);
    14191     psFree(where);
    14192     if (count < 0) {
    14193         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
    14194         return false;
    14195     }
    14196     if (count > 1) {
    14197         // XXX should this be a psAbort() instead?  It is possible that
    14198         // having an object match multiple rows was by design.
    14199         psError(PS_ERR_UNKNOWN, true, "warpInputExpRow object matched more then one row.  Check your database schema");
    14200         return false;
    14201     }
    14202 
    14203     return true;
    14204 }
    14205 long long warpInputExpDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    14206 {
    14207     long long       deleted = 0;
    14208 
    14209     for (long long i = 0; i < objects->n; i++) {
    14210         warpInputExpRow *object = objects->data[i];
    14211         psMetadata *where = warpInputExpMetadataFromObject(object);
    14212         long long count = psDBDeleteRows(dbh, WARPINPUTEXP_TABLE_NAME, where, limit);
    14213         psFree(where);
    14214         if (count < 0) {
    14215             psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpInputExp");
    14216             return count;
    14217         }
    14218 
    14219         deleted += count;
    14220     }
    14221 
    14222     return deleted;
    14223 }
    14224 bool warpInputExpPrintObjects(FILE *stream, psArray *objects, bool mdcf)
    14225 {
    14226     PS_ASSERT_PTR_NON_NULL(objects, false);
    14227 
    14228     psMetadata *output = psMetadataAlloc();
    14229     for (long i = 0; i < psArrayLength(objects); i++) {
    14230         psMetadata *md = warpInputExpMetadataFromObject(objects->data[i]);
    14231         if (!psMetadataAddMetadata(
    14232             output,
    14233             PS_LIST_TAIL,
    14234             WARPINPUTEXP_TABLE_NAME,
    14235             PS_META_DUPLICATE_OK,
    14236             NULL,
    14237             md
    14238         )) {
    14239             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    14240             psFree(md);
    14241             psFree(output);
    14242             return false;
    14243         }
    14244         psFree(md);
    14245     }
    14246 
    14247     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    14248         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    14249         psFree(output);
    14250     }
    14251     psFree(output);
    14252 
    14253     return true;
    14254 }
    14255 bool warpInputExpPrintObject(FILE *stream, warpInputExpRow *object, bool mdcf)
    14256 {
    14257     PS_ASSERT_PTR_NON_NULL(object, false);
    14258 
    14259     psMetadata *md = warpInputExpMetadataFromObject(object);
    14260 
    14261     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    14262         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    14263         psFree(md);
    14264     }
    14265 
    14266     psFree(md);
    14267 
    14268     return true;
    14269 }
    14270 static void warpSkyCellMapRowFree(warpSkyCellMapRow *object);
    14271 
    14272 warpSkyCellMapRow *warpSkyCellMapRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
    14273 {
    14274     warpSkyCellMapRow *_object;
    14275 
    14276     _object = psAlloc(sizeof(warpSkyCellMapRow));
    14277     psMemSetDeallocator(_object, (psFreeFunc)warpSkyCellMapRowFree);
    14278 
    14279     _object->warp_id = warp_id;
    14280     _object->skycell_id = psStringCopy(skycell_id);
    14281     _object->tess_id = psStringCopy(tess_id);
    14282     _object->cam_id = cam_id;
    14283     _object->class_id = psStringCopy(class_id);
    14284     _object->fault = fault;
    14285 
    14286     return _object;
    14287 }
    14288 
    14289 static void warpSkyCellMapRowFree(warpSkyCellMapRow *object)
    14290 {
    14291     psFree(object->skycell_id);
    14292     psFree(object->tess_id);
    14293     psFree(object->class_id);
    14294 }
    14295 
    14296 bool warpSkyCellMapCreateTable(psDB *dbh)
    14297 {
    14298     psMetadata *md = psMetadataAlloc();
    14299     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
    14300         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14301         psFree(md);
    14302         return false;
    14303     }
    14304     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
    14305         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    14306         psFree(md);
    14307         return false;
    14308     }
    14309     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
    14310         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    14311         psFree(md);
    14312         return false;
    14313     }
    14314     if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, "Primary Key", 0)) {
    14315         psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
    14316         psFree(md);
    14317         return false;
    14318     }
    14319     if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, "Primary Key", "64")) {
    14320         psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
    14321         psFree(md);
    14322         return false;
    14323     }
    14324     if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, "Key", 0)) {
    14325         psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
    14326         psFree(md);
    14327         return false;
    14328     }
    14329 
    14330     bool status = psDBCreateTable(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
    14331 
    14332     psFree(md);
    14333 
    14334     return status;
    14335 }
    14336 
    14337 bool warpSkyCellMapDropTable(psDB *dbh)
    14338 {
    14339     return psDBDropTable(dbh, WARPSKYCELLMAP_TABLE_NAME);
    14340 }
    14341 
    14342 bool warpSkyCellMapInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, psS64 cam_id, const char *class_id, psS16 fault)
    14343 {
    14344     psMetadata *md = psMetadataAlloc();
    14345     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
    14346         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14347         psFree(md);
    14348         return false;
    14349     }
    14350     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
    14351         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    14352         psFree(md);
    14353         return false;
    14354     }
    14355     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
    14356         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    14357         psFree(md);
    14358         return false;
    14359     }
    14360     if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, cam_id)) {
    14361         psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
    14362         psFree(md);
    14363         return false;
    14364     }
    14365     if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, class_id)) {
    14366         psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
    14367         psFree(md);
    14368         return false;
    14369     }
    14370     if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, fault)) {
    14371         psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
    14372         psFree(md);
    14373         return false;
    14374     }
    14375 
    14376     bool status = psDBInsertOneRow(dbh, WARPSKYCELLMAP_TABLE_NAME, md);
    14377     psFree(md);
    14378 
    14379     return status;
    14380 }
    14381 
    14382 long long warpSkyCellMapDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    14383 {
    14384     long long       deleted = 0;
    14385 
    14386     long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
    14387     if (count < 0) {
    14388         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
    14389         return count;
    14390 
    14391         deleted += count;
    14392     }
    14393 
    14394     return deleted;
    14395 }
    14396 bool warpSkyCellMapInsertObject(psDB *dbh, warpSkyCellMapRow *object)
    14397 {
    14398     return warpSkyCellMapInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->cam_id, object->class_id, object->fault);
    14399 }
    14400 
    14401 bool warpSkyCellMapInsertObjects(psDB *dbh, psArray *objects)
    14402 {
    14403     for (long i = 0; i < psArrayLength(objects); i++) {
    14404         if (!warpSkyCellMapInsertObject(dbh, objects->data[i])) {
    14405             return false;
    14406         }
    14407     }
    14408 
    14409     return true;
    14410 }
    14411 
    14412 bool warpSkyCellMapInsertFits(psDB *dbh, const psFits *fits)
    14413 {
    14414     psArray         *rowSet;
    14415 
    14416     // move to (the first?) extension named  WARPSKYCELLMAP_TABLE_NAME
    14417     if (!psFitsMoveExtName(fits, WARPSKYCELLMAP_TABLE_NAME)) {
    14418         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYCELLMAP_TABLE_NAME);
    14419         return false;
    14420     }
    14421 
    14422     // check HDU type
    14423     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    14424         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    14425         return false;
    14426     }
    14427 
    14428     // read fits table
    14429     rowSet = psFitsReadTable(fits);
    14430     if (!rowSet) {
    14431         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    14432         psFree(rowSet);
    14433         return false;
    14434     }
    14435 
    14436     if (!psDBInsertRows(dbh, WARPSKYCELLMAP_TABLE_NAME, rowSet)) {
    14437         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    14438         psFree(rowSet);
    14439         return false;
    14440     }
    14441 
    14442     psFree(rowSet);
    14443 
    14444     return true;
    14445 }
    14446 
    14447 bool warpSkyCellMapSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    14448 {
    14449     psArray         *rowSet;
    14450 
    14451     rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
    14452     if (!rowSet) {
    14453         return false;
    14454     }
    14455 
    14456     // output to fits
    14457     if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYCELLMAP_TABLE_NAME)) {
    14458         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    14459         psFree(rowSet);
    14460         return false;
    14461     }
    14462 
    14463     psFree(rowSet);
    14464 
    14465     return true;
    14466 }
    14467 
    14468 psMetadata *warpSkyCellMapMetadataFromObject(const warpSkyCellMapRow *object)
    14469 {
    14470     psMetadata *md = psMetadataAlloc();
    14471     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
    14472         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14473         psFree(md);
    14474         return false;
    14475     }
    14476     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
    14477         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    14478         psFree(md);
    14479         return false;
    14480     }
    14481     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
    14482         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    14483         psFree(md);
    14484         return false;
    14485     }
    14486     if (!psMetadataAdd(md, PS_LIST_TAIL, "cam_id", PS_DATA_S64, NULL, object->cam_id)) {
    14487         psError(PS_ERR_UNKNOWN, false, "failed to add item cam_id");
    14488         psFree(md);
    14489         return false;
    14490     }
    14491     if (!psMetadataAdd(md, PS_LIST_TAIL, "class_id", PS_DATA_STRING, NULL, object->class_id)) {
    14492         psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
    14493         psFree(md);
    14494         return false;
    14495     }
    14496     if (!psMetadataAdd(md, PS_LIST_TAIL, "fault", PS_DATA_S16, NULL, object->fault)) {
    14497         psError(PS_ERR_UNKNOWN, false, "failed to add item fault");
    14498         psFree(md);
    14499         return false;
    14500     }
    14501 
    14502 
    14503     return md;
    14504 }
    14505 
    14506 warpSkyCellMapRow *warpSkyCellMapObjectFromMetadata(psMetadata *md)
    14507 {
    14508 
    14509 bool status = false;
    14510     psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
    14511     if (!status) {
    14512         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
    14513         return false;
    14514     }
    14515     char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
    14516     if (!status) {
    14517         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
    14518         return false;
    14519     }
    14520     char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
    14521     if (!status) {
    14522         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
    14523         return false;
    14524     }
    14525     psS64 cam_id = psMetadataLookupS64(&status, md, "cam_id");
    14526     if (!status) {
    14527         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item cam_id");
    14528         return false;
    14529     }
    14530     char* class_id = psMetadataLookupPtr(&status, md, "class_id");
    14531     if (!status) {
    14532         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item class_id");
    14533         return false;
    14534     }
    14535     psS16 fault = psMetadataLookupS16(&status, md, "fault");
    14536     if (!status) {
    14537         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item fault");
    14538         return false;
    14539     }
    14540 
    14541     return warpSkyCellMapRowAlloc(warp_id, skycell_id, tess_id, cam_id, class_id, fault);
    14542 }
    14543 psArray *warpSkyCellMapSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    14544 {
    14545     psArray         *rowSet;
    14546     psArray         *returnSet;
    14547     psU64           i;
    14548 
    14549     rowSet = psDBSelectRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
    14550     if (!rowSet) {
    14551         return NULL;
    14552     }
    14553 
    14554     // convert psMetadata rows to row objects
    14555 
    14556     returnSet = psArrayAllocEmpty(rowSet->n);
    14557 
    14558     for (i = 0; i < rowSet->n; i++) {
    14559         warpSkyCellMapRow *object = warpSkyCellMapObjectFromMetadata(rowSet->data[i]);
    14560         psArrayAdd(returnSet, 0, object);
    14561         psFree(object);
    14562     }
    14563 
    14564     psFree(rowSet);
    14565 
    14566     return returnSet;
    14567 }
    14568 bool warpSkyCellMapDeleteObject(psDB *dbh, const warpSkyCellMapRow *object)
    14569 {
    14570     psMetadata *where = warpSkyCellMapMetadataFromObject(object);
    14571     long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, 0);
    14572     psFree(where);
    14573     if (count < 0) {
    14574         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
    14575         return false;
    14576     }
    14577     if (count > 1) {
    14578         // XXX should this be a psAbort() instead?  It is possible that
    14579         // having an object match multiple rows was by design.
    14580         psError(PS_ERR_UNKNOWN, true, "warpSkyCellMapRow object matched more then one row.  Check your database schema");
    14581         return false;
    14582     }
    14583 
    14584     return true;
    14585 }
    14586 long long warpSkyCellMapDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    14587 {
    14588     long long       deleted = 0;
    14589 
    14590     for (long long i = 0; i < objects->n; i++) {
    14591         warpSkyCellMapRow *object = objects->data[i];
    14592         psMetadata *where = warpSkyCellMapMetadataFromObject(object);
    14593         long long count = psDBDeleteRows(dbh, WARPSKYCELLMAP_TABLE_NAME, where, limit);
    14594         psFree(where);
    14595         if (count < 0) {
    14596             psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyCellMap");
    14597             return count;
    14598         }
    14599 
    14600         deleted += count;
    14601     }
    14602 
    14603     return deleted;
    14604 }
    14605 bool warpSkyCellMapPrintObjects(FILE *stream, psArray *objects, bool mdcf)
    14606 {
    14607     PS_ASSERT_PTR_NON_NULL(objects, false);
    14608 
    14609     psMetadata *output = psMetadataAlloc();
    14610     for (long i = 0; i < psArrayLength(objects); i++) {
    14611         psMetadata *md = warpSkyCellMapMetadataFromObject(objects->data[i]);
    14612         if (!psMetadataAddMetadata(
    14613             output,
    14614             PS_LIST_TAIL,
    14615             WARPSKYCELLMAP_TABLE_NAME,
    14616             PS_META_DUPLICATE_OK,
    14617             NULL,
    14618             md
    14619         )) {
    14620             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    14621             psFree(md);
    14622             psFree(output);
    14623             return false;
    14624         }
    14625         psFree(md);
    14626     }
    14627 
    14628     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    14629         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    14630         psFree(output);
    14631     }
    14632     psFree(output);
    14633 
    14634     return true;
    14635 }
    14636 bool warpSkyCellMapPrintObject(FILE *stream, warpSkyCellMapRow *object, bool mdcf)
    14637 {
    14638     PS_ASSERT_PTR_NON_NULL(object, false);
    14639 
    14640     psMetadata *md = warpSkyCellMapMetadataFromObject(object);
    14641 
    14642     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    14643         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    14644         psFree(md);
    14645     }
    14646 
    14647     psFree(md);
    14648 
    14649     return true;
    14650 }
    14651 static void warpSkyfileRowFree(warpSkyfileRow *object);
    14652 
    14653 warpSkyfileRow *warpSkyfileRowAlloc(psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
    14654 {
    14655     warpSkyfileRow  *_object;
    14656 
    14657     _object = psAlloc(sizeof(warpSkyfileRow));
    14658     psMemSetDeallocator(_object, (psFreeFunc)warpSkyfileRowFree);
    14659 
    14660     _object->warp_id = warp_id;
    14661     _object->skycell_id = psStringCopy(skycell_id);
    14662     _object->tess_id = psStringCopy(tess_id);
    14663     _object->uri = psStringCopy(uri);
    14664     _object->bg = bg;
    14665     _object->bg_stdev = bg_stdev;
    14666 
    14667     return _object;
    14668 }
    14669 
    14670 static void warpSkyfileRowFree(warpSkyfileRow *object)
    14671 {
    14672     psFree(object->skycell_id);
    14673     psFree(object->tess_id);
    14674     psFree(object->uri);
    14675 }
    14676 
    14677 bool warpSkyfileCreateTable(psDB *dbh)
    14678 {
    14679     psMetadata *md = psMetadataAlloc();
    14680     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
    14681         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14682         psFree(md);
    14683         return false;
    14684     }
    14685     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
    14686         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    14687         psFree(md);
    14688         return false;
    14689     }
    14690     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
    14691         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    14692         psFree(md);
    14693         return false;
    14694     }
    14695     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
    14696         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    14697         psFree(md);
    14698         return false;
    14699     }
    14700     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
    14701         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    14702         psFree(md);
    14703         return false;
    14704     }
    14705     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
    14706         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    14707         psFree(md);
    14708         return false;
    14709     }
    14710 
    14711     bool status = psDBCreateTable(dbh, WARPSKYFILE_TABLE_NAME, md);
    14712 
    14713     psFree(md);
    14714 
    14715     return status;
    14716 }
    14717 
    14718 bool warpSkyfileDropTable(psDB *dbh)
    14719 {
    14720     return psDBDropTable(dbh, WARPSKYFILE_TABLE_NAME);
    14721 }
    14722 
    14723 bool warpSkyfileInsert(psDB * dbh, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *uri, psF64 bg, psF64 bg_stdev)
    14724 {
    14725     psMetadata *md = psMetadataAlloc();
    14726     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
    14727         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14728         psFree(md);
    14729         return false;
    14730     }
    14731     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
    14732         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    14733         psFree(md);
    14734         return false;
    14735     }
    14736     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
    14737         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    14738         psFree(md);
    14739         return false;
    14740     }
    14741     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
    14742         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    14743         psFree(md);
    14744         return false;
    14745     }
    14746     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
    14747         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    14748         psFree(md);
    14749         return false;
    14750     }
    14751     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
    14752         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    14753         psFree(md);
    14754         return false;
    14755     }
    14756 
    14757     bool status = psDBInsertOneRow(dbh, WARPSKYFILE_TABLE_NAME, md);
    14758     psFree(md);
    14759 
    14760     return status;
    14761 }
    14762 
    14763 long long warpSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    14764 {
    14765     long long       deleted = 0;
    14766 
    14767     long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
    14768     if (count < 0) {
    14769         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
    14770         return count;
    14771 
    14772         deleted += count;
    14773     }
    14774 
    14775     return deleted;
    14776 }
    14777 bool warpSkyfileInsertObject(psDB *dbh, warpSkyfileRow *object)
    14778 {
    14779     return warpSkyfileInsert(dbh, object->warp_id, object->skycell_id, object->tess_id, object->uri, object->bg, object->bg_stdev);
    14780 }
    14781 
    14782 bool warpSkyfileInsertObjects(psDB *dbh, psArray *objects)
    14783 {
    14784     for (long i = 0; i < psArrayLength(objects); i++) {
    14785         if (!warpSkyfileInsertObject(dbh, objects->data[i])) {
    14786             return false;
    14787         }
    14788     }
    14789 
    14790     return true;
    14791 }
    14792 
    14793 bool warpSkyfileInsertFits(psDB *dbh, const psFits *fits)
    14794 {
    14795     psArray         *rowSet;
    14796 
    14797     // move to (the first?) extension named  WARPSKYFILE_TABLE_NAME
    14798     if (!psFitsMoveExtName(fits, WARPSKYFILE_TABLE_NAME)) {
    14799         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", WARPSKYFILE_TABLE_NAME);
    14800         return false;
    14801     }
    14802 
    14803     // check HDU type
    14804     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    14805         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    14806         return false;
    14807     }
    14808 
    14809     // read fits table
    14810     rowSet = psFitsReadTable(fits);
    14811     if (!rowSet) {
    14812         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    14813         psFree(rowSet);
    14814         return false;
    14815     }
    14816 
    14817     if (!psDBInsertRows(dbh, WARPSKYFILE_TABLE_NAME, rowSet)) {
    14818         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    14819         psFree(rowSet);
    14820         return false;
    14821     }
    14822 
    14823     psFree(rowSet);
    14824 
    14825     return true;
    14826 }
    14827 
    14828 bool warpSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    14829 {
    14830     psArray         *rowSet;
    14831 
    14832     rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
    14833     if (!rowSet) {
    14834         return false;
    14835     }
    14836 
    14837     // output to fits
    14838     if (!psFitsWriteTable(fits, NULL, rowSet, WARPSKYFILE_TABLE_NAME)) {
    14839         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    14840         psFree(rowSet);
    14841         return false;
    14842     }
    14843 
    14844     psFree(rowSet);
    14845 
    14846     return true;
    14847 }
    14848 
    14849 psMetadata *warpSkyfileMetadataFromObject(const warpSkyfileRow *object)
    14850 {
    14851     psMetadata *md = psMetadataAlloc();
    14852     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
    14853         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    14854         psFree(md);
    14855         return false;
    14856     }
    14857     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
    14858         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    14859         psFree(md);
    14860         return false;
    14861     }
    14862     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
    14863         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    14864         psFree(md);
    14865         return false;
    14866     }
    14867     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
    14868         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    14869         psFree(md);
    14870         return false;
    14871     }
    14872     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
    14873         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    14874         psFree(md);
    14875         return false;
    14876     }
    14877     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
    14878         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    14879         psFree(md);
    14880         return false;
    14881     }
    14882 
    14883 
    14884     return md;
    14885 }
    14886 
    14887 warpSkyfileRow *warpSkyfileObjectFromMetadata(psMetadata *md)
    14888 {
    14889 
    14890 bool status = false;
    14891     psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
    14892     if (!status) {
    14893         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
    14894         return false;
    14895     }
    14896     char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
    14897     if (!status) {
    14898         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
    14899         return false;
    14900     }
    14901     char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
    14902     if (!status) {
    14903         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
    14904         return false;
    14905     }
    14906     char* uri = psMetadataLookupPtr(&status, md, "uri");
    14907     if (!status) {
    14908         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
    14909         return false;
    14910     }
    14911     psF64 bg = psMetadataLookupF64(&status, md, "bg");
    14912     if (!status) {
    14913         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
    14914         return false;
    14915     }
    14916     psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
    14917     if (!status) {
    14918         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
    14919         return false;
    14920     }
    14921 
    14922     return warpSkyfileRowAlloc(warp_id, skycell_id, tess_id, uri, bg, bg_stdev);
    14923 }
    14924 psArray *warpSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    14925 {
    14926     psArray         *rowSet;
    14927     psArray         *returnSet;
    14928     psU64           i;
    14929 
    14930     rowSet = psDBSelectRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
    14931     if (!rowSet) {
    14932         return NULL;
    14933     }
    14934 
    14935     // convert psMetadata rows to row objects
    14936 
    14937     returnSet = psArrayAllocEmpty(rowSet->n);
    14938 
    14939     for (i = 0; i < rowSet->n; i++) {
    14940         warpSkyfileRow *object = warpSkyfileObjectFromMetadata(rowSet->data[i]);
    14941         psArrayAdd(returnSet, 0, object);
    14942         psFree(object);
    14943     }
    14944 
    14945     psFree(rowSet);
    14946 
    14947     return returnSet;
    14948 }
    14949 bool warpSkyfileDeleteObject(psDB *dbh, const warpSkyfileRow *object)
    14950 {
    14951     psMetadata *where = warpSkyfileMetadataFromObject(object);
    14952     long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, 0);
    14953     psFree(where);
    14954     if (count < 0) {
    14955         psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
    14956         return false;
    14957     }
    14958     if (count > 1) {
    14959         // XXX should this be a psAbort() instead?  It is possible that
    14960         // having an object match multiple rows was by design.
    14961         psError(PS_ERR_UNKNOWN, true, "warpSkyfileRow object matched more then one row.  Check your database schema");
    14962         return false;
    14963     }
    14964 
    14965     return true;
    14966 }
    14967 long long warpSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    14968 {
    14969     long long       deleted = 0;
    14970 
    14971     for (long long i = 0; i < objects->n; i++) {
    14972         warpSkyfileRow *object = objects->data[i];
    14973         psMetadata *where = warpSkyfileMetadataFromObject(object);
    14974         long long count = psDBDeleteRows(dbh, WARPSKYFILE_TABLE_NAME, where, limit);
    14975         psFree(where);
    14976         if (count < 0) {
    14977             psError(PS_ERR_UNKNOWN, true, "failed to delete row from warpSkyfile");
    14978             return count;
    14979         }
    14980 
    14981         deleted += count;
    14982     }
    14983 
    14984     return deleted;
    14985 }
    14986 bool warpSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
    14987 {
    14988     PS_ASSERT_PTR_NON_NULL(objects, false);
    14989 
    14990     psMetadata *output = psMetadataAlloc();
    14991     for (long i = 0; i < psArrayLength(objects); i++) {
    14992         psMetadata *md = warpSkyfileMetadataFromObject(objects->data[i]);
    14993         if (!psMetadataAddMetadata(
    14994             output,
    14995             PS_LIST_TAIL,
    14996             WARPSKYFILE_TABLE_NAME,
    14997             PS_META_DUPLICATE_OK,
    14998             NULL,
    14999             md
    15000         )) {
    15001             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    15002             psFree(md);
    15003             psFree(output);
    15004             return false;
    15005         }
    15006         psFree(md);
    15007     }
    15008 
    15009     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    15010         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    15011         psFree(output);
    15012     }
    15013     psFree(output);
    15014 
    15015     return true;
    15016 }
    15017 bool warpSkyfilePrintObject(FILE *stream, warpSkyfileRow *object, bool mdcf)
    15018 {
    15019     PS_ASSERT_PTR_NON_NULL(object, false);
    15020 
    15021     psMetadata *md = warpSkyfileMetadataFromObject(object);
    15022 
    15023     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    15024         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    15025         psFree(md);
    15026     }
    15027 
    15028     psFree(md);
    15029 
    15030     return true;
    15031 }
    15032 static void diffRunRowFree(diffRunRow *object);
    15033 
    15034 diffRunRow *diffRunRowAlloc(psS64 diff_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
    15035 {
    15036     diffRunRow      *_object;
    15037 
    15038     _object = psAlloc(sizeof(diffRunRow));
    15039     psMemSetDeallocator(_object, (psFreeFunc)diffRunRowFree);
    15040 
    15041     _object->diff_id = diff_id;
    15042     _object->state = psStringCopy(state);
    15043     _object->workdir = psStringCopy(workdir);
    15044     _object->dvodb = psStringCopy(dvodb);
    15045     _object->registered = psTimeCopy(registered);
    15046     _object->skycell_id = psStringCopy(skycell_id);
    15047     _object->tess_id = psStringCopy(tess_id);
    15048 
    15049     return _object;
    15050 }
    15051 
    15052 static void diffRunRowFree(diffRunRow *object)
    15053 {
    15054     psFree(object->state);
    15055     psFree(object->workdir);
    15056     psFree(object->dvodb);
    15057     psFree(object->registered);
    15058     psFree(object->skycell_id);
    15059     psFree(object->tess_id);
    15060 }
    15061 
    15062 bool diffRunCreateTable(psDB *dbh)
    15063 {
    15064     psMetadata *md = psMetadataAlloc();
    15065     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
    15066         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15067         psFree(md);
    15068         return false;
    15069     }
    15070     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
    15071         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    15072         psFree(md);
    15073         return false;
    15074     }
    15075     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
    15076         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    15077         psFree(md);
    15078         return false;
    15079     }
    15080     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
    15081         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    15082         psFree(md);
    15083         return false;
    15084     }
    15085     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
    15086         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    15087         psFree(md);
    15088         return false;
    15089     }
    15090     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
    15091         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    15092         psFree(md);
    15093         return false;
    15094     }
    15095     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
    15096         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    15097         psFree(md);
    15098         return false;
    15099     }
    15100 
    15101     bool status = psDBCreateTable(dbh, DIFFRUN_TABLE_NAME, md);
    15102 
    15103     psFree(md);
    15104 
    15105     return status;
    15106 }
    15107 
    15108 bool diffRunDropTable(psDB *dbh)
    15109 {
    15110     return psDBDropTable(dbh, DIFFRUN_TABLE_NAME);
    15111 }
    15112 
    15113 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)
    15114 {
    15115     psMetadata *md = psMetadataAlloc();
    15116     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
    15117         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15118         psFree(md);
    15119         return false;
    15120     }
    15121     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
    15122         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    15123         psFree(md);
    15124         return false;
    15125     }
    15126     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
    15127         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    15128         psFree(md);
    15129         return false;
    15130     }
    15131     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
    15132         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    15133         psFree(md);
    15134         return false;
    15135     }
    15136     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
    15137         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    15138         psFree(md);
    15139         return false;
    15140     }
    15141     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
    15142         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    15143         psFree(md);
    15144         return false;
    15145     }
    15146     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
    15147         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    15148         psFree(md);
    15149         return false;
    15150     }
    15151 
    15152     bool status = psDBInsertOneRow(dbh, DIFFRUN_TABLE_NAME, md);
    15153     psFree(md);
    15154 
    15155     return status;
    15156 }
    15157 
    15158 long long diffRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    15159 {
    15160     long long       deleted = 0;
    15161 
    15162     long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
    15163     if (count < 0) {
    15164         psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
    15165         return count;
    15166 
    15167         deleted += count;
    15168     }
    15169 
    15170     return deleted;
    15171 }
    15172 bool diffRunInsertObject(psDB *dbh, diffRunRow *object)
    15173 {
    15174     return diffRunInsert(dbh, object->diff_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
    15175 }
    15176 
    15177 bool diffRunInsertObjects(psDB *dbh, psArray *objects)
    15178 {
    15179     for (long i = 0; i < psArrayLength(objects); i++) {
    15180         if (!diffRunInsertObject(dbh, objects->data[i])) {
    15181             return false;
    15182         }
    15183     }
    15184 
    15185     return true;
    15186 }
    15187 
    15188 bool diffRunInsertFits(psDB *dbh, const psFits *fits)
    15189 {
    15190     psArray         *rowSet;
    15191 
    15192     // move to (the first?) extension named  DIFFRUN_TABLE_NAME
    15193     if (!psFitsMoveExtName(fits, DIFFRUN_TABLE_NAME)) {
    15194         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFRUN_TABLE_NAME);
    15195         return false;
    15196     }
    15197 
    15198     // check HDU type
    15199     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    15200         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    15201         return false;
    15202     }
    15203 
    15204     // read fits table
    15205     rowSet = psFitsReadTable(fits);
    15206     if (!rowSet) {
    15207         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    15208         psFree(rowSet);
    15209         return false;
    15210     }
    15211 
    15212     if (!psDBInsertRows(dbh, DIFFRUN_TABLE_NAME, rowSet)) {
    15213         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    15214         psFree(rowSet);
    15215         return false;
    15216     }
    15217 
    15218     psFree(rowSet);
    15219 
    15220     return true;
    15221 }
    15222 
    15223 bool diffRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    15224 {
    15225     psArray         *rowSet;
    15226 
    15227     rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
    15228     if (!rowSet) {
    15229         return false;
    15230     }
    15231 
    15232     // output to fits
    15233     if (!psFitsWriteTable(fits, NULL, rowSet, DIFFRUN_TABLE_NAME)) {
    15234         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    15235         psFree(rowSet);
    15236         return false;
    15237     }
    15238 
    15239     psFree(rowSet);
    15240 
    15241     return true;
    15242 }
    15243 
    15244 psMetadata *diffRunMetadataFromObject(const diffRunRow *object)
    15245 {
    15246     psMetadata *md = psMetadataAlloc();
    15247     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
    15248         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15249         psFree(md);
    15250         return false;
    15251     }
    15252     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
    15253         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    15254         psFree(md);
    15255         return false;
    15256     }
    15257     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
    15258         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    15259         psFree(md);
    15260         return false;
    15261     }
    15262     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
    15263         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    15264         psFree(md);
    15265         return false;
    15266     }
    15267     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
    15268         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    15269         psFree(md);
    15270         return false;
    15271     }
    15272     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
    15273         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    15274         psFree(md);
    15275         return false;
    15276     }
    15277     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
    15278         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    15279         psFree(md);
    15280         return false;
    15281     }
    15282 
    15283 
    15284     return md;
    15285 }
    15286 
    15287 diffRunRow *diffRunObjectFromMetadata(psMetadata *md)
    15288 {
    15289 
    15290 bool status = false;
    15291     psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
    15292     if (!status) {
    15293         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
    15294         return false;
    15295     }
    15296     char* state = psMetadataLookupPtr(&status, md, "state");
    15297     if (!status) {
    15298         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
    15299         return false;
    15300     }
    15301     char* workdir = psMetadataLookupPtr(&status, md, "workdir");
    15302     if (!status) {
    15303         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
    15304         return false;
    15305     }
    15306     char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
    15307     if (!status) {
    15308         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
    15309         return false;
    15310     }
    15311     psTime* registered = psMetadataLookupPtr(&status, md, "registered");
    15312     if (!status) {
    15313         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
    15314         return false;
    15315     }
    15316     char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
    15317     if (!status) {
    15318         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
    15319         return false;
    15320     }
    15321     char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
    15322     if (!status) {
    15323         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
    15324         return false;
    15325     }
    15326 
    15327     return diffRunRowAlloc(diff_id, state, workdir, dvodb, registered, skycell_id, tess_id);
    15328 }
    15329 psArray *diffRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    15330 {
    15331     psArray         *rowSet;
    15332     psArray         *returnSet;
    15333     psU64           i;
    15334 
    15335     rowSet = psDBSelectRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
    15336     if (!rowSet) {
    15337         return NULL;
    15338     }
    15339 
    15340     // convert psMetadata rows to row objects
    15341 
    15342     returnSet = psArrayAllocEmpty(rowSet->n);
    15343 
    15344     for (i = 0; i < rowSet->n; i++) {
    15345         diffRunRow *object = diffRunObjectFromMetadata(rowSet->data[i]);
    15346         psArrayAdd(returnSet, 0, object);
    15347         psFree(object);
    15348     }
    15349 
    15350     psFree(rowSet);
    15351 
    15352     return returnSet;
    15353 }
    15354 bool diffRunDeleteObject(psDB *dbh, const diffRunRow *object)
    15355 {
    15356     psMetadata *where = diffRunMetadataFromObject(object);
    15357     long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, 0);
    15358     psFree(where);
    15359     if (count < 0) {
    15360         psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
    15361         return false;
    15362     }
    15363     if (count > 1) {
    15364         // XXX should this be a psAbort() instead?  It is possible that
    15365         // having an object match multiple rows was by design.
    15366         psError(PS_ERR_UNKNOWN, true, "diffRunRow object matched more then one row.  Check your database schema");
    15367         return false;
    15368     }
    15369 
    15370     return true;
    15371 }
    15372 long long diffRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    15373 {
    15374     long long       deleted = 0;
    15375 
    15376     for (long long i = 0; i < objects->n; i++) {
    15377         diffRunRow *object = objects->data[i];
    15378         psMetadata *where = diffRunMetadataFromObject(object);
    15379         long long count = psDBDeleteRows(dbh, DIFFRUN_TABLE_NAME, where, limit);
    15380         psFree(where);
    15381         if (count < 0) {
    15382             psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffRun");
    15383             return count;
    15384         }
    15385 
    15386         deleted += count;
    15387     }
    15388 
    15389     return deleted;
    15390 }
    15391 bool diffRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
    15392 {
    15393     PS_ASSERT_PTR_NON_NULL(objects, false);
    15394 
    15395     psMetadata *output = psMetadataAlloc();
    15396     for (long i = 0; i < psArrayLength(objects); i++) {
    15397         psMetadata *md = diffRunMetadataFromObject(objects->data[i]);
    15398         if (!psMetadataAddMetadata(
    15399             output,
    15400             PS_LIST_TAIL,
    15401             DIFFRUN_TABLE_NAME,
    15402             PS_META_DUPLICATE_OK,
    15403             NULL,
    15404             md
    15405         )) {
    15406             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    15407             psFree(md);
    15408             psFree(output);
    15409             return false;
    15410         }
    15411         psFree(md);
    15412     }
    15413 
    15414     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    15415         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    15416         psFree(output);
    15417     }
    15418     psFree(output);
    15419 
    15420     return true;
    15421 }
    15422 bool diffRunPrintObject(FILE *stream, diffRunRow *object, bool mdcf)
    15423 {
    15424     PS_ASSERT_PTR_NON_NULL(object, false);
    15425 
    15426     psMetadata *md = diffRunMetadataFromObject(object);
    15427 
    15428     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    15429         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    15430         psFree(md);
    15431     }
    15432 
    15433     psFree(md);
    15434 
    15435     return true;
    15436 }
    15437 static void diffInputSkyfileRowFree(diffInputSkyfileRow *object);
    15438 
    15439 diffInputSkyfileRow *diffInputSkyfileRowAlloc(psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
    15440 {
    15441     diffInputSkyfileRow *_object;
    15442 
    15443     _object = psAlloc(sizeof(diffInputSkyfileRow));
    15444     psMemSetDeallocator(_object, (psFreeFunc)diffInputSkyfileRowFree);
    15445 
    15446     _object->diff_id = diff_id;
    15447     _object->warp_id = warp_id;
    15448     _object->skycell_id = psStringCopy(skycell_id);
    15449     _object->tess_id = psStringCopy(tess_id);
    15450     _object->kind = psStringCopy(kind);
    15451     _object->template = template;
    15452 
    15453     return _object;
    15454 }
    15455 
    15456 static void diffInputSkyfileRowFree(diffInputSkyfileRow *object)
    15457 {
    15458     psFree(object->skycell_id);
    15459     psFree(object->tess_id);
    15460     psFree(object->kind);
    15461 }
    15462 
    15463 bool diffInputSkyfileCreateTable(psDB *dbh)
    15464 {
    15465     psMetadata *md = psMetadataAlloc();
    15466     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key", 0)) {
    15467         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15468         psFree(md);
    15469         return false;
    15470     }
    15471     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
    15472         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    15473         psFree(md);
    15474         return false;
    15475     }
    15476     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Primary Key", "64")) {
    15477         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    15478         psFree(md);
    15479         return false;
    15480     }
    15481     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Primary Key", "64")) {
    15482         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    15483         psFree(md);
    15484         return false;
    15485     }
    15486     if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, "Key", "64")) {
    15487         psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
    15488         psFree(md);
    15489         return false;
    15490     }
    15491     if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, 0)) {
    15492         psError(PS_ERR_UNKNOWN, false, "failed to add item template");
    15493         psFree(md);
    15494         return false;
    15495     }
    15496 
    15497     bool status = psDBCreateTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
    15498 
    15499     psFree(md);
    15500 
    15501     return status;
    15502 }
    15503 
    15504 bool diffInputSkyfileDropTable(psDB *dbh)
    15505 {
    15506     return psDBDropTable(dbh, DIFFINPUTSKYFILE_TABLE_NAME);
    15507 }
    15508 
    15509 bool diffInputSkyfileInsert(psDB * dbh, psS64 diff_id, psS64 warp_id, const char *skycell_id, const char *tess_id, const char *kind, bool template)
    15510 {
    15511     psMetadata *md = psMetadataAlloc();
    15512     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
    15513         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15514         psFree(md);
    15515         return false;
    15516     }
    15517     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
    15518         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    15519         psFree(md);
    15520         return false;
    15521     }
    15522     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
    15523         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    15524         psFree(md);
    15525         return false;
    15526     }
    15527     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
    15528         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    15529         psFree(md);
    15530         return false;
    15531     }
    15532     if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, kind)) {
    15533         psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
    15534         psFree(md);
    15535         return false;
    15536     }
    15537     if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, template)) {
    15538         psError(PS_ERR_UNKNOWN, false, "failed to add item template");
    15539         psFree(md);
    15540         return false;
    15541     }
    15542 
    15543     bool status = psDBInsertOneRow(dbh, DIFFINPUTSKYFILE_TABLE_NAME, md);
    15544     psFree(md);
    15545 
    15546     return status;
    15547 }
    15548 
    15549 long long diffInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    15550 {
    15551     long long       deleted = 0;
    15552 
    15553     long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
    15554     if (count < 0) {
    15555         psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
    15556         return count;
    15557 
    15558         deleted += count;
    15559     }
    15560 
    15561     return deleted;
    15562 }
    15563 bool diffInputSkyfileInsertObject(psDB *dbh, diffInputSkyfileRow *object)
    15564 {
    15565     return diffInputSkyfileInsert(dbh, object->diff_id, object->warp_id, object->skycell_id, object->tess_id, object->kind, object->template);
    15566 }
    15567 
    15568 bool diffInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
    15569 {
    15570     for (long i = 0; i < psArrayLength(objects); i++) {
    15571         if (!diffInputSkyfileInsertObject(dbh, objects->data[i])) {
    15572             return false;
    15573         }
    15574     }
    15575 
    15576     return true;
    15577 }
    15578 
    15579 bool diffInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
    15580 {
    15581     psArray         *rowSet;
    15582 
    15583     // move to (the first?) extension named  DIFFINPUTSKYFILE_TABLE_NAME
    15584     if (!psFitsMoveExtName(fits, DIFFINPUTSKYFILE_TABLE_NAME)) {
    15585         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFINPUTSKYFILE_TABLE_NAME);
    15586         return false;
    15587     }
    15588 
    15589     // check HDU type
    15590     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    15591         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    15592         return false;
    15593     }
    15594 
    15595     // read fits table
    15596     rowSet = psFitsReadTable(fits);
    15597     if (!rowSet) {
    15598         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    15599         psFree(rowSet);
    15600         return false;
    15601     }
    15602 
    15603     if (!psDBInsertRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, rowSet)) {
    15604         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    15605         psFree(rowSet);
    15606         return false;
    15607     }
    15608 
    15609     psFree(rowSet);
    15610 
    15611     return true;
    15612 }
    15613 
    15614 bool diffInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    15615 {
    15616     psArray         *rowSet;
    15617 
    15618     rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
    15619     if (!rowSet) {
    15620         return false;
    15621     }
    15622 
    15623     // output to fits
    15624     if (!psFitsWriteTable(fits, NULL, rowSet, DIFFINPUTSKYFILE_TABLE_NAME)) {
    15625         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    15626         psFree(rowSet);
    15627         return false;
    15628     }
    15629 
    15630     psFree(rowSet);
    15631 
    15632     return true;
    15633 }
    15634 
    15635 psMetadata *diffInputSkyfileMetadataFromObject(const diffInputSkyfileRow *object)
    15636 {
    15637     psMetadata *md = psMetadataAlloc();
    15638     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
    15639         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15640         psFree(md);
    15641         return false;
    15642     }
    15643     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
    15644         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    15645         psFree(md);
    15646         return false;
    15647     }
    15648     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
    15649         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    15650         psFree(md);
    15651         return false;
    15652     }
    15653     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
    15654         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    15655         psFree(md);
    15656         return false;
    15657     }
    15658     if (!psMetadataAdd(md, PS_LIST_TAIL, "kind", PS_DATA_STRING, NULL, object->kind)) {
    15659         psError(PS_ERR_UNKNOWN, false, "failed to add item kind");
    15660         psFree(md);
    15661         return false;
    15662     }
    15663     if (!psMetadataAdd(md, PS_LIST_TAIL, "template", PS_DATA_BOOL, NULL, object->template)) {
    15664         psError(PS_ERR_UNKNOWN, false, "failed to add item template");
    15665         psFree(md);
    15666         return false;
    15667     }
    15668 
    15669 
    15670     return md;
    15671 }
    15672 
    15673 diffInputSkyfileRow *diffInputSkyfileObjectFromMetadata(psMetadata *md)
    15674 {
    15675 
    15676 bool status = false;
    15677     psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
    15678     if (!status) {
    15679         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
    15680         return false;
    15681     }
    15682     psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
    15683     if (!status) {
    15684         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
    15685         return false;
    15686     }
    15687     char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
    15688     if (!status) {
    15689         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
    15690         return false;
    15691     }
    15692     char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
    15693     if (!status) {
    15694         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
    15695         return false;
    15696     }
    15697     char* kind = psMetadataLookupPtr(&status, md, "kind");
    15698     if (!status) {
    15699         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item kind");
    15700         return false;
    15701     }
    15702     bool template = psMetadataLookupBool(&status, md, "template");
    15703     if (!status) {
    15704         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item template");
    15705         return false;
    15706     }
    15707 
    15708     return diffInputSkyfileRowAlloc(diff_id, warp_id, skycell_id, tess_id, kind, template);
    15709 }
    15710 psArray *diffInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    15711 {
    15712     psArray         *rowSet;
    15713     psArray         *returnSet;
    15714     psU64           i;
    15715 
    15716     rowSet = psDBSelectRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
    15717     if (!rowSet) {
    15718         return NULL;
    15719     }
    15720 
    15721     // convert psMetadata rows to row objects
    15722 
    15723     returnSet = psArrayAllocEmpty(rowSet->n);
    15724 
    15725     for (i = 0; i < rowSet->n; i++) {
    15726         diffInputSkyfileRow *object = diffInputSkyfileObjectFromMetadata(rowSet->data[i]);
    15727         psArrayAdd(returnSet, 0, object);
    15728         psFree(object);
    15729     }
    15730 
    15731     psFree(rowSet);
    15732 
    15733     return returnSet;
    15734 }
    15735 bool diffInputSkyfileDeleteObject(psDB *dbh, const diffInputSkyfileRow *object)
    15736 {
    15737     psMetadata *where = diffInputSkyfileMetadataFromObject(object);
    15738     long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, 0);
    15739     psFree(where);
    15740     if (count < 0) {
    15741         psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
    15742         return false;
    15743     }
    15744     if (count > 1) {
    15745         // XXX should this be a psAbort() instead?  It is possible that
    15746         // having an object match multiple rows was by design.
    15747         psError(PS_ERR_UNKNOWN, true, "diffInputSkyfileRow object matched more then one row.  Check your database schema");
    15748         return false;
    15749     }
    15750 
    15751     return true;
    15752 }
    15753 long long diffInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    15754 {
    15755     long long       deleted = 0;
    15756 
    15757     for (long long i = 0; i < objects->n; i++) {
    15758         diffInputSkyfileRow *object = objects->data[i];
    15759         psMetadata *where = diffInputSkyfileMetadataFromObject(object);
    15760         long long count = psDBDeleteRows(dbh, DIFFINPUTSKYFILE_TABLE_NAME, where, limit);
    15761         psFree(where);
    15762         if (count < 0) {
    15763             psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffInputSkyfile");
    15764             return count;
    15765         }
    15766 
    15767         deleted += count;
    15768     }
    15769 
    15770     return deleted;
    15771 }
    15772 bool diffInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
    15773 {
    15774     PS_ASSERT_PTR_NON_NULL(objects, false);
    15775 
    15776     psMetadata *output = psMetadataAlloc();
    15777     for (long i = 0; i < psArrayLength(objects); i++) {
    15778         psMetadata *md = diffInputSkyfileMetadataFromObject(objects->data[i]);
    15779         if (!psMetadataAddMetadata(
    15780             output,
    15781             PS_LIST_TAIL,
    15782             DIFFINPUTSKYFILE_TABLE_NAME,
    15783             PS_META_DUPLICATE_OK,
    15784             NULL,
    15785             md
    15786         )) {
    15787             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    15788             psFree(md);
    15789             psFree(output);
    15790             return false;
    15791         }
    15792         psFree(md);
    15793     }
    15794 
    15795     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    15796         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    15797         psFree(output);
    15798     }
    15799     psFree(output);
    15800 
    15801     return true;
    15802 }
    15803 bool diffInputSkyfilePrintObject(FILE *stream, diffInputSkyfileRow *object, bool mdcf)
    15804 {
    15805     PS_ASSERT_PTR_NON_NULL(object, false);
    15806 
    15807     psMetadata *md = diffInputSkyfileMetadataFromObject(object);
    15808 
    15809     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    15810         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    15811         psFree(md);
    15812     }
    15813 
    15814     psFree(md);
    15815 
    15816     return true;
    15817 }
    15818 static void diffSkyfileRowFree(diffSkyfileRow *object);
    15819 
    15820 diffSkyfileRow *diffSkyfileRowAlloc(psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
    15821 {
    15822     diffSkyfileRow  *_object;
    15823 
    15824     _object = psAlloc(sizeof(diffSkyfileRow));
    15825     psMemSetDeallocator(_object, (psFreeFunc)diffSkyfileRowFree);
    15826 
    15827     _object->diff_id = diff_id;
    15828     _object->uri = psStringCopy(uri);
    15829     _object->bg = bg;
    15830     _object->bg_stdev = bg_stdev;
    15831 
    15832     return _object;
    15833 }
    15834 
    15835 static void diffSkyfileRowFree(diffSkyfileRow *object)
    15836 {
    15837     psFree(object->uri);
    15838 }
    15839 
    15840 bool diffSkyfileCreateTable(psDB *dbh)
    15841 {
    15842     psMetadata *md = psMetadataAlloc();
    15843     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, "Primary Key", 0)) {
    15844         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15845         psFree(md);
    15846         return false;
    15847     }
    15848     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
    15849         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    15850         psFree(md);
    15851         return false;
    15852     }
    15853     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
    15854         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    15855         psFree(md);
    15856         return false;
    15857     }
    15858     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
    15859         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    15860         psFree(md);
    15861         return false;
    15862     }
    15863 
    15864     bool status = psDBCreateTable(dbh, DIFFSKYFILE_TABLE_NAME, md);
    15865 
    15866     psFree(md);
    15867 
    15868     return status;
    15869 }
    15870 
    15871 bool diffSkyfileDropTable(psDB *dbh)
    15872 {
    15873     return psDBDropTable(dbh, DIFFSKYFILE_TABLE_NAME);
    15874 }
    15875 
    15876 bool diffSkyfileInsert(psDB * dbh, psS64 diff_id, const char *uri, psF64 bg, psF64 bg_stdev)
    15877 {
    15878     psMetadata *md = psMetadataAlloc();
    15879     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, diff_id)) {
    15880         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15881         psFree(md);
    15882         return false;
    15883     }
    15884     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
    15885         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    15886         psFree(md);
    15887         return false;
    15888     }
    15889     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
    15890         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    15891         psFree(md);
    15892         return false;
    15893     }
    15894     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
    15895         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    15896         psFree(md);
    15897         return false;
    15898     }
    15899 
    15900     bool status = psDBInsertOneRow(dbh, DIFFSKYFILE_TABLE_NAME, md);
    15901     psFree(md);
    15902 
    15903     return status;
    15904 }
    15905 
    15906 long long diffSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    15907 {
    15908     long long       deleted = 0;
    15909 
    15910     long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
    15911     if (count < 0) {
    15912         psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
    15913         return count;
    15914 
    15915         deleted += count;
    15916     }
    15917 
    15918     return deleted;
    15919 }
    15920 bool diffSkyfileInsertObject(psDB *dbh, diffSkyfileRow *object)
    15921 {
    15922     return diffSkyfileInsert(dbh, object->diff_id, object->uri, object->bg, object->bg_stdev);
    15923 }
    15924 
    15925 bool diffSkyfileInsertObjects(psDB *dbh, psArray *objects)
    15926 {
    15927     for (long i = 0; i < psArrayLength(objects); i++) {
    15928         if (!diffSkyfileInsertObject(dbh, objects->data[i])) {
    15929             return false;
    15930         }
    15931     }
    15932 
    15933     return true;
    15934 }
    15935 
    15936 bool diffSkyfileInsertFits(psDB *dbh, const psFits *fits)
    15937 {
    15938     psArray         *rowSet;
    15939 
    15940     // move to (the first?) extension named  DIFFSKYFILE_TABLE_NAME
    15941     if (!psFitsMoveExtName(fits, DIFFSKYFILE_TABLE_NAME)) {
    15942         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", DIFFSKYFILE_TABLE_NAME);
    15943         return false;
    15944     }
    15945 
    15946     // check HDU type
    15947     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    15948         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    15949         return false;
    15950     }
    15951 
    15952     // read fits table
    15953     rowSet = psFitsReadTable(fits);
    15954     if (!rowSet) {
    15955         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    15956         psFree(rowSet);
    15957         return false;
    15958     }
    15959 
    15960     if (!psDBInsertRows(dbh, DIFFSKYFILE_TABLE_NAME, rowSet)) {
    15961         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    15962         psFree(rowSet);
    15963         return false;
    15964     }
    15965 
    15966     psFree(rowSet);
    15967 
    15968     return true;
    15969 }
    15970 
    15971 bool diffSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    15972 {
    15973     psArray         *rowSet;
    15974 
    15975     rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
    15976     if (!rowSet) {
    15977         return false;
    15978     }
    15979 
    15980     // output to fits
    15981     if (!psFitsWriteTable(fits, NULL, rowSet, DIFFSKYFILE_TABLE_NAME)) {
    15982         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    15983         psFree(rowSet);
    15984         return false;
    15985     }
    15986 
    15987     psFree(rowSet);
    15988 
    15989     return true;
    15990 }
    15991 
    15992 psMetadata *diffSkyfileMetadataFromObject(const diffSkyfileRow *object)
    15993 {
    15994     psMetadata *md = psMetadataAlloc();
    15995     if (!psMetadataAdd(md, PS_LIST_TAIL, "diff_id", PS_DATA_S64, NULL, object->diff_id)) {
    15996         psError(PS_ERR_UNKNOWN, false, "failed to add item diff_id");
    15997         psFree(md);
    15998         return false;
    15999     }
    16000     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
    16001         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    16002         psFree(md);
    16003         return false;
    16004     }
    16005     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
    16006         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    16007         psFree(md);
    16008         return false;
    16009     }
    16010     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
    16011         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    16012         psFree(md);
    16013         return false;
    16014     }
    16015 
    16016 
    16017     return md;
    16018 }
    16019 
    16020 diffSkyfileRow *diffSkyfileObjectFromMetadata(psMetadata *md)
    16021 {
    16022 
    16023 bool status = false;
    16024     psS64 diff_id = psMetadataLookupS64(&status, md, "diff_id");
    16025     if (!status) {
    16026         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item diff_id");
    16027         return false;
    16028     }
    16029     char* uri = psMetadataLookupPtr(&status, md, "uri");
    16030     if (!status) {
    16031         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
    16032         return false;
    16033     }
    16034     psF64 bg = psMetadataLookupF64(&status, md, "bg");
    16035     if (!status) {
    16036         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
    16037         return false;
    16038     }
    16039     psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
    16040     if (!status) {
    16041         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
    16042         return false;
    16043     }
    16044 
    16045     return diffSkyfileRowAlloc(diff_id, uri, bg, bg_stdev);
    16046 }
    16047 psArray *diffSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    16048 {
    16049     psArray         *rowSet;
    16050     psArray         *returnSet;
    16051     psU64           i;
    16052 
    16053     rowSet = psDBSelectRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
    16054     if (!rowSet) {
    16055         return NULL;
    16056     }
    16057 
    16058     // convert psMetadata rows to row objects
    16059 
    16060     returnSet = psArrayAllocEmpty(rowSet->n);
    16061 
    16062     for (i = 0; i < rowSet->n; i++) {
    16063         diffSkyfileRow *object = diffSkyfileObjectFromMetadata(rowSet->data[i]);
    16064         psArrayAdd(returnSet, 0, object);
    16065         psFree(object);
    16066     }
    16067 
    16068     psFree(rowSet);
    16069 
    16070     return returnSet;
    16071 }
    16072 bool diffSkyfileDeleteObject(psDB *dbh, const diffSkyfileRow *object)
    16073 {
    16074     psMetadata *where = diffSkyfileMetadataFromObject(object);
    16075     long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, 0);
    16076     psFree(where);
    16077     if (count < 0) {
    16078         psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
    16079         return false;
    16080     }
    16081     if (count > 1) {
    16082         // XXX should this be a psAbort() instead?  It is possible that
    16083         // having an object match multiple rows was by design.
    16084         psError(PS_ERR_UNKNOWN, true, "diffSkyfileRow object matched more then one row.  Check your database schema");
    16085         return false;
    16086     }
    16087 
    16088     return true;
    16089 }
    16090 long long diffSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    16091 {
    16092     long long       deleted = 0;
    16093 
    16094     for (long long i = 0; i < objects->n; i++) {
    16095         diffSkyfileRow *object = objects->data[i];
    16096         psMetadata *where = diffSkyfileMetadataFromObject(object);
    16097         long long count = psDBDeleteRows(dbh, DIFFSKYFILE_TABLE_NAME, where, limit);
    16098         psFree(where);
    16099         if (count < 0) {
    16100             psError(PS_ERR_UNKNOWN, true, "failed to delete row from diffSkyfile");
    16101             return count;
    16102         }
    16103 
    16104         deleted += count;
    16105     }
    16106 
    16107     return deleted;
    16108 }
    16109 bool diffSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
    16110 {
    16111     PS_ASSERT_PTR_NON_NULL(objects, false);
    16112 
    16113     psMetadata *output = psMetadataAlloc();
    16114     for (long i = 0; i < psArrayLength(objects); i++) {
    16115         psMetadata *md = diffSkyfileMetadataFromObject(objects->data[i]);
    16116         if (!psMetadataAddMetadata(
    16117             output,
    16118             PS_LIST_TAIL,
    16119             DIFFSKYFILE_TABLE_NAME,
    16120             PS_META_DUPLICATE_OK,
    16121             NULL,
    16122             md
    16123         )) {
    16124             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    16125             psFree(md);
    16126             psFree(output);
    16127             return false;
    16128         }
    16129         psFree(md);
    16130     }
    16131 
    16132     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    16133         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    16134         psFree(output);
    16135     }
    16136     psFree(output);
    16137 
    16138     return true;
    16139 }
    16140 bool diffSkyfilePrintObject(FILE *stream, diffSkyfileRow *object, bool mdcf)
    16141 {
    16142     PS_ASSERT_PTR_NON_NULL(object, false);
    16143 
    16144     psMetadata *md = diffSkyfileMetadataFromObject(object);
    16145 
    16146     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    16147         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    16148         psFree(md);
    16149     }
    16150 
    16151     psFree(md);
    16152 
    16153     return true;
    16154 }
    16155 static void stackRunRowFree(stackRunRow *object);
    16156 
    16157 stackRunRow *stackRunRowAlloc(psS64 stack_id, const char *state, const char *workdir, const char *dvodb, psTime* registered, const char *skycell_id, const char *tess_id)
    16158 {
    16159     stackRunRow     *_object;
    16160 
    16161     _object = psAlloc(sizeof(stackRunRow));
    16162     psMemSetDeallocator(_object, (psFreeFunc)stackRunRowFree);
    16163 
    16164     _object->stack_id = stack_id;
    16165     _object->state = psStringCopy(state);
    16166     _object->workdir = psStringCopy(workdir);
    16167     _object->dvodb = psStringCopy(dvodb);
    16168     _object->registered = psTimeCopy(registered);
    16169     _object->skycell_id = psStringCopy(skycell_id);
    16170     _object->tess_id = psStringCopy(tess_id);
    16171 
    16172     return _object;
    16173 }
    16174 
    16175 static void stackRunRowFree(stackRunRow *object)
    16176 {
    16177     psFree(object->state);
    16178     psFree(object->workdir);
    16179     psFree(object->dvodb);
    16180     psFree(object->registered);
    16181     psFree(object->skycell_id);
    16182     psFree(object->tess_id);
    16183 }
    16184 
    16185 bool stackRunCreateTable(psDB *dbh)
    16186 {
    16187     psMetadata *md = psMetadataAlloc();
    16188     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key AUTO_INCREMENT", 0)) {
    16189         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16190         psFree(md);
    16191         return false;
    16192     }
    16193     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, "Key", "64")) {
    16194         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    16195         psFree(md);
    16196         return false;
    16197     }
    16198     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, "255")) {
    16199         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    16200         psFree(md);
    16201         return false;
    16202     }
    16203     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, "255")) {
    16204         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    16205         psFree(md);
    16206         return false;
    16207     }
    16208     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, NULL)) {
    16209         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    16210         psFree(md);
    16211         return false;
    16212     }
    16213     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, "Key", "64")) {
    16214         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    16215         psFree(md);
    16216         return false;
    16217     }
    16218     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, "Key", "64")) {
    16219         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    16220         psFree(md);
    16221         return false;
    16222     }
    16223 
    16224     bool status = psDBCreateTable(dbh, STACKRUN_TABLE_NAME, md);
    16225 
    16226     psFree(md);
    16227 
    16228     return status;
    16229 }
    16230 
    16231 bool stackRunDropTable(psDB *dbh)
    16232 {
    16233     return psDBDropTable(dbh, STACKRUN_TABLE_NAME);
    16234 }
    16235 
    16236 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)
    16237 {
    16238     psMetadata *md = psMetadataAlloc();
    16239     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
    16240         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16241         psFree(md);
    16242         return false;
    16243     }
    16244     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, state)) {
    16245         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    16246         psFree(md);
    16247         return false;
    16248     }
    16249     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, workdir)) {
    16250         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    16251         psFree(md);
    16252         return false;
    16253     }
    16254     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, dvodb)) {
    16255         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    16256         psFree(md);
    16257         return false;
    16258     }
    16259     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, registered)) {
    16260         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    16261         psFree(md);
    16262         return false;
    16263     }
    16264     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, skycell_id)) {
    16265         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    16266         psFree(md);
    16267         return false;
    16268     }
    16269     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, tess_id)) {
    16270         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    16271         psFree(md);
    16272         return false;
    16273     }
    16274 
    16275     bool status = psDBInsertOneRow(dbh, STACKRUN_TABLE_NAME, md);
    16276     psFree(md);
    16277 
    16278     return status;
    16279 }
    16280 
    16281 long long stackRunDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    16282 {
    16283     long long       deleted = 0;
    16284 
    16285     long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
    16286     if (count < 0) {
    16287         psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
    16288         return count;
    16289 
    16290         deleted += count;
    16291     }
    16292 
    16293     return deleted;
    16294 }
    16295 bool stackRunInsertObject(psDB *dbh, stackRunRow *object)
    16296 {
    16297     return stackRunInsert(dbh, object->stack_id, object->state, object->workdir, object->dvodb, object->registered, object->skycell_id, object->tess_id);
    16298 }
    16299 
    16300 bool stackRunInsertObjects(psDB *dbh, psArray *objects)
    16301 {
    16302     for (long i = 0; i < psArrayLength(objects); i++) {
    16303         if (!stackRunInsertObject(dbh, objects->data[i])) {
    16304             return false;
    16305         }
    16306     }
    16307 
    16308     return true;
    16309 }
    16310 
    16311 bool stackRunInsertFits(psDB *dbh, const psFits *fits)
    16312 {
    16313     psArray         *rowSet;
    16314 
    16315     // move to (the first?) extension named  STACKRUN_TABLE_NAME
    16316     if (!psFitsMoveExtName(fits, STACKRUN_TABLE_NAME)) {
    16317         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKRUN_TABLE_NAME);
    16318         return false;
    16319     }
    16320 
    16321     // check HDU type
    16322     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    16323         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    16324         return false;
    16325     }
    16326 
    16327     // read fits table
    16328     rowSet = psFitsReadTable(fits);
    16329     if (!rowSet) {
    16330         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    16331         psFree(rowSet);
    16332         return false;
    16333     }
    16334 
    16335     if (!psDBInsertRows(dbh, STACKRUN_TABLE_NAME, rowSet)) {
    16336         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    16337         psFree(rowSet);
    16338         return false;
    16339     }
    16340 
    16341     psFree(rowSet);
    16342 
    16343     return true;
    16344 }
    16345 
    16346 bool stackRunSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    16347 {
    16348     psArray         *rowSet;
    16349 
    16350     rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
    16351     if (!rowSet) {
    16352         return false;
    16353     }
    16354 
    16355     // output to fits
    16356     if (!psFitsWriteTable(fits, NULL, rowSet, STACKRUN_TABLE_NAME)) {
    16357         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    16358         psFree(rowSet);
    16359         return false;
    16360     }
    16361 
    16362     psFree(rowSet);
    16363 
    16364     return true;
    16365 }
    16366 
    16367 psMetadata *stackRunMetadataFromObject(const stackRunRow *object)
    16368 {
    16369     psMetadata *md = psMetadataAlloc();
    16370     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
    16371         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16372         psFree(md);
    16373         return false;
    16374     }
    16375     if (!psMetadataAdd(md, PS_LIST_TAIL, "state", PS_DATA_STRING, NULL, object->state)) {
    16376         psError(PS_ERR_UNKNOWN, false, "failed to add item state");
    16377         psFree(md);
    16378         return false;
    16379     }
    16380     if (!psMetadataAdd(md, PS_LIST_TAIL, "workdir", PS_DATA_STRING, NULL, object->workdir)) {
    16381         psError(PS_ERR_UNKNOWN, false, "failed to add item workdir");
    16382         psFree(md);
    16383         return false;
    16384     }
    16385     if (!psMetadataAdd(md, PS_LIST_TAIL, "dvodb", PS_DATA_STRING, NULL, object->dvodb)) {
    16386         psError(PS_ERR_UNKNOWN, false, "failed to add item dvodb");
    16387         psFree(md);
    16388         return false;
    16389     }
    16390     if (!psMetadataAdd(md, PS_LIST_TAIL, "registered", PS_DATA_TIME, NULL, object->registered)) {
    16391         psError(PS_ERR_UNKNOWN, false, "failed to add item registered");
    16392         psFree(md);
    16393         return false;
    16394     }
    16395     if (!psMetadataAdd(md, PS_LIST_TAIL, "skycell_id", PS_DATA_STRING, NULL, object->skycell_id)) {
    16396         psError(PS_ERR_UNKNOWN, false, "failed to add item skycell_id");
    16397         psFree(md);
    16398         return false;
    16399     }
    16400     if (!psMetadataAdd(md, PS_LIST_TAIL, "tess_id", PS_DATA_STRING, NULL, object->tess_id)) {
    16401         psError(PS_ERR_UNKNOWN, false, "failed to add item tess_id");
    16402         psFree(md);
    16403         return false;
    16404     }
    16405 
    16406 
    16407     return md;
    16408 }
    16409 
    16410 stackRunRow *stackRunObjectFromMetadata(psMetadata *md)
    16411 {
    16412 
    16413 bool status = false;
    16414     psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
    16415     if (!status) {
    16416         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
    16417         return false;
    16418     }
    16419     char* state = psMetadataLookupPtr(&status, md, "state");
    16420     if (!status) {
    16421         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item state");
    16422         return false;
    16423     }
    16424     char* workdir = psMetadataLookupPtr(&status, md, "workdir");
    16425     if (!status) {
    16426         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item workdir");
    16427         return false;
    16428     }
    16429     char* dvodb = psMetadataLookupPtr(&status, md, "dvodb");
    16430     if (!status) {
    16431         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item dvodb");
    16432         return false;
    16433     }
    16434     psTime* registered = psMetadataLookupPtr(&status, md, "registered");
    16435     if (!status) {
    16436         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item registered");
    16437         return false;
    16438     }
    16439     char* skycell_id = psMetadataLookupPtr(&status, md, "skycell_id");
    16440     if (!status) {
    16441         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item skycell_id");
    16442         return false;
    16443     }
    16444     char* tess_id = psMetadataLookupPtr(&status, md, "tess_id");
    16445     if (!status) {
    16446         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item tess_id");
    16447         return false;
    16448     }
    16449 
    16450     return stackRunRowAlloc(stack_id, state, workdir, dvodb, registered, skycell_id, tess_id);
    16451 }
    16452 psArray *stackRunSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    16453 {
    16454     psArray         *rowSet;
    16455     psArray         *returnSet;
    16456     psU64           i;
    16457 
    16458     rowSet = psDBSelectRows(dbh, STACKRUN_TABLE_NAME, where, limit);
    16459     if (!rowSet) {
    16460         return NULL;
    16461     }
    16462 
    16463     // convert psMetadata rows to row objects
    16464 
    16465     returnSet = psArrayAllocEmpty(rowSet->n);
    16466 
    16467     for (i = 0; i < rowSet->n; i++) {
    16468         stackRunRow *object = stackRunObjectFromMetadata(rowSet->data[i]);
    16469         psArrayAdd(returnSet, 0, object);
    16470         psFree(object);
    16471     }
    16472 
    16473     psFree(rowSet);
    16474 
    16475     return returnSet;
    16476 }
    16477 bool stackRunDeleteObject(psDB *dbh, const stackRunRow *object)
    16478 {
    16479     psMetadata *where = stackRunMetadataFromObject(object);
    16480     long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, 0);
    16481     psFree(where);
    16482     if (count < 0) {
    16483         psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
    16484         return false;
    16485     }
    16486     if (count > 1) {
    16487         // XXX should this be a psAbort() instead?  It is possible that
    16488         // having an object match multiple rows was by design.
    16489         psError(PS_ERR_UNKNOWN, true, "stackRunRow object matched more then one row.  Check your database schema");
    16490         return false;
    16491     }
    16492 
    16493     return true;
    16494 }
    16495 long long stackRunDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    16496 {
    16497     long long       deleted = 0;
    16498 
    16499     for (long long i = 0; i < objects->n; i++) {
    16500         stackRunRow *object = objects->data[i];
    16501         psMetadata *where = stackRunMetadataFromObject(object);
    16502         long long count = psDBDeleteRows(dbh, STACKRUN_TABLE_NAME, where, limit);
    16503         psFree(where);
    16504         if (count < 0) {
    16505             psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackRun");
    16506             return count;
    16507         }
    16508 
    16509         deleted += count;
    16510     }
    16511 
    16512     return deleted;
    16513 }
    16514 bool stackRunPrintObjects(FILE *stream, psArray *objects, bool mdcf)
    16515 {
    16516     PS_ASSERT_PTR_NON_NULL(objects, false);
    16517 
    16518     psMetadata *output = psMetadataAlloc();
    16519     for (long i = 0; i < psArrayLength(objects); i++) {
    16520         psMetadata *md = stackRunMetadataFromObject(objects->data[i]);
    16521         if (!psMetadataAddMetadata(
    16522             output,
    16523             PS_LIST_TAIL,
    16524             STACKRUN_TABLE_NAME,
    16525             PS_META_DUPLICATE_OK,
    16526             NULL,
    16527             md
    16528         )) {
    16529             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    16530             psFree(md);
    16531             psFree(output);
    16532             return false;
    16533         }
    16534         psFree(md);
    16535     }
    16536 
    16537     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    16538         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    16539         psFree(output);
    16540     }
    16541     psFree(output);
    16542 
    16543     return true;
    16544 }
    16545 bool stackRunPrintObject(FILE *stream, stackRunRow *object, bool mdcf)
    16546 {
    16547     PS_ASSERT_PTR_NON_NULL(object, false);
    16548 
    16549     psMetadata *md = stackRunMetadataFromObject(object);
    16550 
    16551     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    16552         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    16553         psFree(md);
    16554     }
    16555 
    16556     psFree(md);
    16557 
    16558     return true;
    16559 }
    16560 static void stackInputSkyfileRowFree(stackInputSkyfileRow *object);
    16561 
    16562 stackInputSkyfileRow *stackInputSkyfileRowAlloc(psS64 stack_id, psS64 warp_id)
    16563 {
    16564     stackInputSkyfileRow *_object;
    16565 
    16566     _object = psAlloc(sizeof(stackInputSkyfileRow));
    16567     psMemSetDeallocator(_object, (psFreeFunc)stackInputSkyfileRowFree);
    16568 
    16569     _object->stack_id = stack_id;
    16570     _object->warp_id = warp_id;
    16571 
    16572     return _object;
    16573 }
    16574 
    16575 static void stackInputSkyfileRowFree(stackInputSkyfileRow *object)
    16576 {
    16577 }
    16578 
    16579 bool stackInputSkyfileCreateTable(psDB *dbh)
    16580 {
    16581     psMetadata *md = psMetadataAlloc();
    16582     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key", 0)) {
    16583         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16584         psFree(md);
    16585         return false;
    16586     }
    16587     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, "Primary Key", 0)) {
    16588         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    16589         psFree(md);
    16590         return false;
    16591     }
    16592 
    16593     bool status = psDBCreateTable(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
    16594 
    16595     psFree(md);
    16596 
    16597     return status;
    16598 }
    16599 
    16600 bool stackInputSkyfileDropTable(psDB *dbh)
    16601 {
    16602     return psDBDropTable(dbh, STACKINPUTSKYFILE_TABLE_NAME);
    16603 }
    16604 
    16605 bool stackInputSkyfileInsert(psDB * dbh, psS64 stack_id, psS64 warp_id)
    16606 {
    16607     psMetadata *md = psMetadataAlloc();
    16608     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
    16609         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16610         psFree(md);
    16611         return false;
    16612     }
    16613     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, warp_id)) {
    16614         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    16615         psFree(md);
    16616         return false;
    16617     }
    16618 
    16619     bool status = psDBInsertOneRow(dbh, STACKINPUTSKYFILE_TABLE_NAME, md);
    16620     psFree(md);
    16621 
    16622     return status;
    16623 }
    16624 
    16625 long long stackInputSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    16626 {
    16627     long long       deleted = 0;
    16628 
    16629     long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
    16630     if (count < 0) {
    16631         psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
    16632         return count;
    16633 
    16634         deleted += count;
    16635     }
    16636 
    16637     return deleted;
    16638 }
    16639 bool stackInputSkyfileInsertObject(psDB *dbh, stackInputSkyfileRow *object)
    16640 {
    16641     return stackInputSkyfileInsert(dbh, object->stack_id, object->warp_id);
    16642 }
    16643 
    16644 bool stackInputSkyfileInsertObjects(psDB *dbh, psArray *objects)
    16645 {
    16646     for (long i = 0; i < psArrayLength(objects); i++) {
    16647         if (!stackInputSkyfileInsertObject(dbh, objects->data[i])) {
    16648             return false;
    16649         }
    16650     }
    16651 
    16652     return true;
    16653 }
    16654 
    16655 bool stackInputSkyfileInsertFits(psDB *dbh, const psFits *fits)
    16656 {
    16657     psArray         *rowSet;
    16658 
    16659     // move to (the first?) extension named  STACKINPUTSKYFILE_TABLE_NAME
    16660     if (!psFitsMoveExtName(fits, STACKINPUTSKYFILE_TABLE_NAME)) {
    16661         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKINPUTSKYFILE_TABLE_NAME);
    16662         return false;
    16663     }
    16664 
    16665     // check HDU type
    16666     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    16667         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    16668         return false;
    16669     }
    16670 
    16671     // read fits table
    16672     rowSet = psFitsReadTable(fits);
    16673     if (!rowSet) {
    16674         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    16675         psFree(rowSet);
    16676         return false;
    16677     }
    16678 
    16679     if (!psDBInsertRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, rowSet)) {
    16680         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    16681         psFree(rowSet);
    16682         return false;
    16683     }
    16684 
    16685     psFree(rowSet);
    16686 
    16687     return true;
    16688 }
    16689 
    16690 bool stackInputSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    16691 {
    16692     psArray         *rowSet;
    16693 
    16694     rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
    16695     if (!rowSet) {
    16696         return false;
    16697     }
    16698 
    16699     // output to fits
    16700     if (!psFitsWriteTable(fits, NULL, rowSet, STACKINPUTSKYFILE_TABLE_NAME)) {
    16701         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    16702         psFree(rowSet);
    16703         return false;
    16704     }
    16705 
    16706     psFree(rowSet);
    16707 
    16708     return true;
    16709 }
    16710 
    16711 psMetadata *stackInputSkyfileMetadataFromObject(const stackInputSkyfileRow *object)
    16712 {
    16713     psMetadata *md = psMetadataAlloc();
    16714     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
    16715         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16716         psFree(md);
    16717         return false;
    16718     }
    16719     if (!psMetadataAdd(md, PS_LIST_TAIL, "warp_id", PS_DATA_S64, NULL, object->warp_id)) {
    16720         psError(PS_ERR_UNKNOWN, false, "failed to add item warp_id");
    16721         psFree(md);
    16722         return false;
    16723     }
    16724 
    16725 
    16726     return md;
    16727 }
    16728 
    16729 stackInputSkyfileRow *stackInputSkyfileObjectFromMetadata(psMetadata *md)
    16730 {
    16731 
    16732 bool status = false;
    16733     psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
    16734     if (!status) {
    16735         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
    16736         return false;
    16737     }
    16738     psS64 warp_id = psMetadataLookupS64(&status, md, "warp_id");
    16739     if (!status) {
    16740         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item warp_id");
    16741         return false;
    16742     }
    16743 
    16744     return stackInputSkyfileRowAlloc(stack_id, warp_id);
    16745 }
    16746 psArray *stackInputSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    16747 {
    16748     psArray         *rowSet;
    16749     psArray         *returnSet;
    16750     psU64           i;
    16751 
    16752     rowSet = psDBSelectRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
    16753     if (!rowSet) {
    16754         return NULL;
    16755     }
    16756 
    16757     // convert psMetadata rows to row objects
    16758 
    16759     returnSet = psArrayAllocEmpty(rowSet->n);
    16760 
    16761     for (i = 0; i < rowSet->n; i++) {
    16762         stackInputSkyfileRow *object = stackInputSkyfileObjectFromMetadata(rowSet->data[i]);
    16763         psArrayAdd(returnSet, 0, object);
    16764         psFree(object);
    16765     }
    16766 
    16767     psFree(rowSet);
    16768 
    16769     return returnSet;
    16770 }
    16771 bool stackInputSkyfileDeleteObject(psDB *dbh, const stackInputSkyfileRow *object)
    16772 {
    16773     psMetadata *where = stackInputSkyfileMetadataFromObject(object);
    16774     long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, 0);
    16775     psFree(where);
    16776     if (count < 0) {
    16777         psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
    16778         return false;
    16779     }
    16780     if (count > 1) {
    16781         // XXX should this be a psAbort() instead?  It is possible that
    16782         // having an object match multiple rows was by design.
    16783         psError(PS_ERR_UNKNOWN, true, "stackInputSkyfileRow object matched more then one row.  Check your database schema");
    16784         return false;
    16785     }
    16786 
    16787     return true;
    16788 }
    16789 long long stackInputSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    16790 {
    16791     long long       deleted = 0;
    16792 
    16793     for (long long i = 0; i < objects->n; i++) {
    16794         stackInputSkyfileRow *object = objects->data[i];
    16795         psMetadata *where = stackInputSkyfileMetadataFromObject(object);
    16796         long long count = psDBDeleteRows(dbh, STACKINPUTSKYFILE_TABLE_NAME, where, limit);
    16797         psFree(where);
    16798         if (count < 0) {
    16799             psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackInputSkyfile");
    16800             return count;
    16801         }
    16802 
    16803         deleted += count;
    16804     }
    16805 
    16806     return deleted;
    16807 }
    16808 bool stackInputSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
    16809 {
    16810     PS_ASSERT_PTR_NON_NULL(objects, false);
    16811 
    16812     psMetadata *output = psMetadataAlloc();
    16813     for (long i = 0; i < psArrayLength(objects); i++) {
    16814         psMetadata *md = stackInputSkyfileMetadataFromObject(objects->data[i]);
    16815         if (!psMetadataAddMetadata(
    16816             output,
    16817             PS_LIST_TAIL,
    16818             STACKINPUTSKYFILE_TABLE_NAME,
    16819             PS_META_DUPLICATE_OK,
    16820             NULL,
    16821             md
    16822         )) {
    16823             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    16824             psFree(md);
    16825             psFree(output);
    16826             return false;
    16827         }
    16828         psFree(md);
    16829     }
    16830 
    16831     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    16832         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    16833         psFree(output);
    16834     }
    16835     psFree(output);
    16836 
    16837     return true;
    16838 }
    16839 bool stackInputSkyfilePrintObject(FILE *stream, stackInputSkyfileRow *object, bool mdcf)
    16840 {
    16841     PS_ASSERT_PTR_NON_NULL(object, false);
    16842 
    16843     psMetadata *md = stackInputSkyfileMetadataFromObject(object);
    16844 
    16845     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    16846         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    16847         psFree(md);
    16848     }
    16849 
    16850     psFree(md);
    16851 
    16852     return true;
    16853 }
    16854 static void stackSumSkyfileRowFree(stackSumSkyfileRow *object);
    16855 
    16856 stackSumSkyfileRow *stackSumSkyfileRowAlloc(psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
    16857 {
    16858     stackSumSkyfileRow *_object;
    16859 
    16860     _object = psAlloc(sizeof(stackSumSkyfileRow));
    16861     psMemSetDeallocator(_object, (psFreeFunc)stackSumSkyfileRowFree);
    16862 
    16863     _object->stack_id = stack_id;
    16864     _object->uri = psStringCopy(uri);
    16865     _object->bg = bg;
    16866     _object->bg_stdev = bg_stdev;
    16867 
    16868     return _object;
    16869 }
    16870 
    16871 static void stackSumSkyfileRowFree(stackSumSkyfileRow *object)
    16872 {
    16873     psFree(object->uri);
    16874 }
    16875 
    16876 bool stackSumSkyfileCreateTable(psDB *dbh)
    16877 {
    16878     psMetadata *md = psMetadataAlloc();
    16879     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, "Primary Key", 0)) {
    16880         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16881         psFree(md);
    16882         return false;
    16883     }
    16884     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, "255")) {
    16885         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    16886         psFree(md);
    16887         return false;
    16888     }
    16889     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, 0.0)) {
    16890         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    16891         psFree(md);
    16892         return false;
    16893     }
    16894     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, 0.0)) {
    16895         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    16896         psFree(md);
    16897         return false;
    16898     }
    16899 
    16900     bool status = psDBCreateTable(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
    16901 
    16902     psFree(md);
    16903 
    16904     return status;
    16905 }
    16906 
    16907 bool stackSumSkyfileDropTable(psDB *dbh)
    16908 {
    16909     return psDBDropTable(dbh, STACKSUMSKYFILE_TABLE_NAME);
    16910 }
    16911 
    16912 bool stackSumSkyfileInsert(psDB * dbh, psS64 stack_id, const char *uri, psF64 bg, psF64 bg_stdev)
    16913 {
    16914     psMetadata *md = psMetadataAlloc();
    16915     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, stack_id)) {
    16916         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    16917         psFree(md);
    16918         return false;
    16919     }
    16920     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, uri)) {
    16921         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    16922         psFree(md);
    16923         return false;
    16924     }
    16925     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, bg)) {
    16926         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    16927         psFree(md);
    16928         return false;
    16929     }
    16930     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, bg_stdev)) {
    16931         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    16932         psFree(md);
    16933         return false;
    16934     }
    16935 
    16936     bool status = psDBInsertOneRow(dbh, STACKSUMSKYFILE_TABLE_NAME, md);
    16937     psFree(md);
    16938 
    16939     return status;
    16940 }
    16941 
    16942 long long stackSumSkyfileDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
    16943 {
    16944     long long       deleted = 0;
    16945 
    16946     long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
    16947     if (count < 0) {
    16948         psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
    16949         return count;
    16950 
    16951         deleted += count;
    16952     }
    16953 
    16954     return deleted;
    16955 }
    16956 bool stackSumSkyfileInsertObject(psDB *dbh, stackSumSkyfileRow *object)
    16957 {
    16958     return stackSumSkyfileInsert(dbh, object->stack_id, object->uri, object->bg, object->bg_stdev);
    16959 }
    16960 
    16961 bool stackSumSkyfileInsertObjects(psDB *dbh, psArray *objects)
    16962 {
    16963     for (long i = 0; i < psArrayLength(objects); i++) {
    16964         if (!stackSumSkyfileInsertObject(dbh, objects->data[i])) {
    16965             return false;
    16966         }
    16967     }
    16968 
    16969     return true;
    16970 }
    16971 
    16972 bool stackSumSkyfileInsertFits(psDB *dbh, const psFits *fits)
    16973 {
    16974     psArray         *rowSet;
    16975 
    16976     // move to (the first?) extension named  STACKSUMSKYFILE_TABLE_NAME
    16977     if (!psFitsMoveExtName(fits, STACKSUMSKYFILE_TABLE_NAME)) {
    16978         psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", STACKSUMSKYFILE_TABLE_NAME);
    16979         return false;
    16980     }
    16981 
    16982     // check HDU type
    16983     if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
    16984         psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
    16985         return false;
    16986     }
    16987 
    16988     // read fits table
    16989     rowSet = psFitsReadTable(fits);
    16990     if (!rowSet) {
    16991         psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
    16992         psFree(rowSet);
    16993         return false;
    16994     }
    16995 
    16996     if (!psDBInsertRows(dbh, STACKSUMSKYFILE_TABLE_NAME, rowSet)) {
    16997         psError(PS_ERR_UNKNOWN, false, "databse insert failed");
    16998         psFree(rowSet);
    16999         return false;
    17000     }
    17001 
    17002     psFree(rowSet);
    17003 
    17004     return true;
    17005 }
    17006 
    17007 bool stackSumSkyfileSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
    17008 {
    17009     psArray         *rowSet;
    17010 
    17011     rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
    17012     if (!rowSet) {
    17013         return false;
    17014     }
    17015 
    17016     // output to fits
    17017     if (!psFitsWriteTable(fits, NULL, rowSet, STACKSUMSKYFILE_TABLE_NAME)) {
    17018         psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
    17019         psFree(rowSet);
    17020         return false;
    17021     }
    17022 
    17023     psFree(rowSet);
    17024 
    17025     return true;
    17026 }
    17027 
    17028 psMetadata *stackSumSkyfileMetadataFromObject(const stackSumSkyfileRow *object)
    17029 {
    17030     psMetadata *md = psMetadataAlloc();
    17031     if (!psMetadataAdd(md, PS_LIST_TAIL, "stack_id", PS_DATA_S64, NULL, object->stack_id)) {
    17032         psError(PS_ERR_UNKNOWN, false, "failed to add item stack_id");
    17033         psFree(md);
    17034         return false;
    17035     }
    17036     if (!psMetadataAdd(md, PS_LIST_TAIL, "uri", PS_DATA_STRING, NULL, object->uri)) {
    17037         psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    17038         psFree(md);
    17039         return false;
    17040     }
    17041     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg", PS_DATA_F64, NULL, object->bg)) {
    17042         psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    17043         psFree(md);
    17044         return false;
    17045     }
    17046     if (!psMetadataAdd(md, PS_LIST_TAIL, "bg_stdev", PS_DATA_F64, NULL, object->bg_stdev)) {
    17047         psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    17048         psFree(md);
    17049         return false;
    17050     }
    17051 
    17052 
    17053     return md;
    17054 }
    17055 
    17056 stackSumSkyfileRow *stackSumSkyfileObjectFromMetadata(psMetadata *md)
    17057 {
    17058 
    17059 bool status = false;
    17060     psS64 stack_id = psMetadataLookupS64(&status, md, "stack_id");
    17061     if (!status) {
    17062         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item stack_id");
    17063         return false;
    17064     }
    17065     char* uri = psMetadataLookupPtr(&status, md, "uri");
    17066     if (!status) {
    17067         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item uri");
    17068         return false;
    17069     }
    17070     psF64 bg = psMetadataLookupF64(&status, md, "bg");
    17071     if (!status) {
    17072         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg");
    17073         return false;
    17074     }
    17075     psF64 bg_stdev = psMetadataLookupF64(&status, md, "bg_stdev");
    17076     if (!status) {
    17077         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item bg_stdev");
    17078         return false;
    17079     }
    17080 
    17081     return stackSumSkyfileRowAlloc(stack_id, uri, bg, bg_stdev);
    17082 }
    17083 psArray *stackSumSkyfileSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
    17084 {
    17085     psArray         *rowSet;
    17086     psArray         *returnSet;
    17087     psU64           i;
    17088 
    17089     rowSet = psDBSelectRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
    17090     if (!rowSet) {
    17091         return NULL;
    17092     }
    17093 
    17094     // convert psMetadata rows to row objects
    17095 
    17096     returnSet = psArrayAllocEmpty(rowSet->n);
    17097 
    17098     for (i = 0; i < rowSet->n; i++) {
    17099         stackSumSkyfileRow *object = stackSumSkyfileObjectFromMetadata(rowSet->data[i]);
    17100         psArrayAdd(returnSet, 0, object);
    17101         psFree(object);
    17102     }
    17103 
    17104     psFree(rowSet);
    17105 
    17106     return returnSet;
    17107 }
    17108 bool stackSumSkyfileDeleteObject(psDB *dbh, const stackSumSkyfileRow *object)
    17109 {
    17110     psMetadata *where = stackSumSkyfileMetadataFromObject(object);
    17111     long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, 0);
    17112     psFree(where);
    17113     if (count < 0) {
    17114         psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
    17115         return false;
    17116     }
    17117     if (count > 1) {
    17118         // XXX should this be a psAbort() instead?  It is possible that
    17119         // having an object match multiple rows was by design.
    17120         psError(PS_ERR_UNKNOWN, true, "stackSumSkyfileRow object matched more then one row.  Check your database schema");
    17121         return false;
    17122     }
    17123 
    17124     return true;
    17125 }
    17126 long long stackSumSkyfileDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
    17127 {
    17128     long long       deleted = 0;
    17129 
    17130     for (long long i = 0; i < objects->n; i++) {
    17131         stackSumSkyfileRow *object = objects->data[i];
    17132         psMetadata *where = stackSumSkyfileMetadataFromObject(object);
    17133         long long count = psDBDeleteRows(dbh, STACKSUMSKYFILE_TABLE_NAME, where, limit);
    17134         psFree(where);
    17135         if (count < 0) {
    17136             psError(PS_ERR_UNKNOWN, true, "failed to delete row from stackSumSkyfile");
    17137             return count;
    17138         }
    17139 
    17140         deleted += count;
    17141     }
    17142 
    17143     return deleted;
    17144 }
    17145 bool stackSumSkyfilePrintObjects(FILE *stream, psArray *objects, bool mdcf)
    17146 {
    17147     PS_ASSERT_PTR_NON_NULL(objects, false);
    17148 
    17149     psMetadata *output = psMetadataAlloc();
    17150     for (long i = 0; i < psArrayLength(objects); i++) {
    17151         psMetadata *md = stackSumSkyfileMetadataFromObject(objects->data[i]);
    17152         if (!psMetadataAddMetadata(
    17153             output,
    17154             PS_LIST_TAIL,
    17155             STACKSUMSKYFILE_TABLE_NAME,
    17156             PS_META_DUPLICATE_OK,
    17157             NULL,
    17158             md
    17159         )) {
    17160             psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
    17161             psFree(md);
    17162             psFree(output);
    17163             return false;
    17164         }
    17165         psFree(md);
    17166     }
    17167 
    17168     if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
    17169         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    17170         psFree(output);
    17171     }
    17172     psFree(output);
    17173 
    17174     return true;
    17175 }
    17176 bool stackSumSkyfilePrintObject(FILE *stream, stackSumSkyfileRow *object, bool mdcf)
    17177 {
    17178     PS_ASSERT_PTR_NON_NULL(object, false);
    17179 
    17180     psMetadata *md = stackSumSkyfileMetadataFromObject(object);
    17181 
    17182     if (!ippdbPrintMetadataRaw(stream, md, mdcf)) {
    17183         psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
    17184         psFree(md);
    17185     }
    17186 
    17187     psFree(md);
    17188 
    17189     return true;
    17190 }
Note: See TracChangeset for help on using the changeset viewer.