IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Nov 9, 2007, 11:27:51 AM (19 years ago)
Author:
jhoblitt
Message:

stub out caltool modes

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ippTools/src/caltool.h

    r15537 r15545  
    11/*
    2  * dettool.c
     2 * caltool.h
    33 *
    4  * Copyright (C) 2006  Joshua Hoblitt
     4 * Copyright (C) 2006-2007  Joshua Hoblitt
    55 *
    66 * This program is free software; you can redistribute it and/or modify it
     
    1818 */
    1919
    20 #ifdef HAVB_CONFIG_H
    21 #include <config.h>
    22 #endif
    23 
    24 #include <stdio.h>
    25 #include <string.h>
    26 #include <stdlib.h>
    27 #include <stdint.h>
    28 #include <inttypes.h>
    29 
    30 #include <ippdb.h>
     20#ifndef CALTOOL_H
     21#define CALTOOL_H 1
    3122
    3223#include "pxtools.h"
    33 #include "dettool.h"
    3424
    35 static bool pendingMode(pxConfig *config);
    36 static bool definebytagMode(pxConfig *config);
    37 static bool definebyqueryMode(pxConfig *config);
    38 static bool definebydetrunMode(pxConfig *config);
    39 static bool runsMode(pxConfig *config);
    40 static bool childlessrunMode(pxConfig *config);
    41 static bool inputMode(pxConfig *config);
    42 static bool rawMode(pxConfig *config);
    43 // processedimfile
    44 static bool toprocessedimfileMode(pxConfig *config);
    45 static bool addprocessedimfileMode(pxConfig *config);
    46 static bool processedimfileMode(pxConfig *config);
    47 static bool revertprocessedimfileMode(pxConfig *config);
    48 // processedexp
    49 static bool toprocessedexpMode(pxConfig *config);
    50 static bool addprocessedexpMode(pxConfig *config);
    51 static bool processedexpMode(pxConfig *config);
    52 static bool revertprocessedexpMode(pxConfig *config);
    53 // stackedimfile
    54 static bool tostackedMode(pxConfig *config);
    55 static bool addstackedMode(pxConfig *config);
    56 static bool stackedMode(pxConfig *config);
    57 static bool revertstackedMode(pxConfig *config);
    58 // normalizedstat
    59 static bool tonormalizedstatMode(pxConfig *config);
    60 static bool addnormalizedstatMode(pxConfig *config);
    61 static bool normalizedstatMode(pxConfig *config);
    62 static bool revertnormalizedstatMode(pxConfig *config);
    63 // normalizedimfile
    64 static bool tonormalizeMode(pxConfig *config);
    65 static bool addnormalizedimfileMode(pxConfig *config);
    66 static bool normalizedimfileMode(pxConfig *config);
    67 static bool revertnormalizedimfileMode(pxConfig *config);
    68 // normalizedexp
    69 static bool tonormalizedexpMode(pxConfig *config);
    70 static bool addnormalizedexpMode(pxConfig *config);
    71 static bool normalizedexpMode(pxConfig *config);
    72 static bool revertnormalizedexpMode(pxConfig *config);
    73 // residimfile
    74 static bool toresidimfileMode(pxConfig *config);
    75 static bool addresidimfileMode(pxConfig *config);
    76 static bool residimfileMode(pxConfig *config);
    77 static bool revertresidimfileMode(pxConfig *config);
    78 // residexp
    79 static bool toresidexpMode(pxConfig *config);
    80 static bool addresidexpMode(pxConfig *config);
    81 static bool residexpMode(pxConfig *config);
    82 static bool revertresidexpMode(pxConfig *config);
    83 static bool updateresidexpMode(pxConfig *config);
    84 // detrunsummary
    85 static bool todetrunsummaryMode(pxConfig *config);
    86 static bool adddetrunsummaryMode(pxConfig *config);
    87 static bool detrunsummaryMode(pxConfig *config);
    88 static bool revertdetrunsummaryMode(pxConfig *config);
    89 static bool updatedetrunMode(pxConfig *config);
    90 static bool rerunMode(pxConfig *config);
    91 // register
    92 static bool register_detrendMode(pxConfig *config);
    93 static bool register_detrend_imfileMode(pxConfig *config);
     25typedef enum {
     26    CALTOOL_MODE_NONE           = PXTOOL_MODE_NONE,
     27    CALTOOL_MODE_DBS,
     28    CALTOOL_MODE_ADDDB,
     29    CALTOOL_MODE_ADDRUN,
     30    CALTOOL_MODE_RUNS
     31} dettoolMode;
    9432
    95 static detNormalizedStatImfileRow *detStackedToDetNormalizedStatImfile(pxConfig *config, detStackedImfileRow *stackedImfile);
    96 static  detNormalizedImfileRow *detNormalizedStatToDetNormalizedmfile(pxConfig *config, detNormalizedStatImfileRow *statImfile);
    97 static detResidExpRow *mdToDetResidExp(pxConfig *config, psMetadata *row);
    98 static detRunSummaryRow *mdToDetRunSummary(pxConfig *config, psMetadata *row);
    99 //static psArray *validDetInputClassIds(pxConfig *config, const char *det_id);
    100 //static psArray *searchInputImfiles(pxConfig *config, const char *det_id);
    101 static detInputExpRow *rawDetrenTodetInputExpRow(rawExpRow *rawExp, psS64 det_id, psS32 iteration);
    102 static psArray *searchRawImfiles(pxConfig *config, psMetadata *where);
    103 static bool startNewIteration(pxConfig *config, psS64 det_id);
    104 static psS32 incrementIteration(pxConfig *config, psS64 det_id);
    105 static bool setDetRunState(pxConfig *config, psS64 det_id, const char *state);
    106 static bool isValidMode(pxConfig *config, const char *mode);
     33pxConfig *caltoolConfig(pxConfig *config, int argc, char **argv);
    10734
    108 # define MODECASE(caseName, func) \
    109     case caseName: \
    110     if (!func(config)) { \
    111         goto FAIL; \
    112     } \
    113     break;
    114 
    115 int main(int argc, char **argv)
    116 {
    117     psLibInit(NULL);
    118 
    119     pxConfig *config = dettoolConfig(NULL, argc, argv);
    120     if (!config) {
    121         psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
    122         goto FAIL;
    123     }
    124 
    125     switch (config->mode) {
    126         MODECASE(DETTOOL_MODE_PENDING,          pendingMode);
    127         MODECASE(DETTOOL_MODE_DEFINEBYTAG,      definebytagMode);
    128         MODECASE(DETTOOL_MODE_DEFINEBYQUERY,    definebyqueryMode);
    129         MODECASE(DETTOOL_MODE_DEFINEBYDETRUN,   definebydetrunMode);
    130         MODECASE(DETTOOL_MODE_RUNS,             runsMode);
    131         MODECASE(DETTOOL_MODE_CHILDLESSRUN,     childlessrunMode);
    132         MODECASE(DETTOOL_MODE_INPUT,            inputMode);
    133         MODECASE(DETTOOL_MODE_RAW,              rawMode);
    134         // imfile
    135         MODECASE(DETTOOL_MODE_TOPROCESSEDIMFILE,toprocessedimfileMode);
    136         MODECASE(DETTOOL_MODE_ADDPROCESSEDIMFILE,addprocessedimfileMode);
    137         MODECASE(DETTOOL_MODE_PROCESSEDIMFILE,  processedimfileMode);
    138         MODECASE(DETTOOL_MODE_REVERTPROCESSEDIMFILE, revertprocessedimfileMode);
    139         // exp
    140         MODECASE(DETTOOL_MODE_TOPROCESSEDEXP,   toprocessedexpMode);
    141         MODECASE(DETTOOL_MODE_ADDPROCESSEDEXP,  addprocessedexpMode);
    142         MODECASE(DETTOOL_MODE_PROCESSEDEXP,     processedexpMode);
    143         MODECASE(DETTOOL_MODE_REVERTPROCESSEDEXP, revertprocessedexpMode);
    144         // stacked
    145         MODECASE(DETTOOL_MODE_TOSTACKED,        tostackedMode);
    146         MODECASE(DETTOOL_MODE_ADDSTACKED,       addstackedMode);
    147         MODECASE(DETTOOL_MODE_STACKED,          stackedMode);
    148         MODECASE(DETTOOL_MODE_REVERTSTACKED,    revertstackedMode);
    149         // normalizedstat
    150         MODECASE(DETTOOL_MODE_TONORMALIZEDSTAT, tonormalizedstatMode);
    151         MODECASE(DETTOOL_MODE_ADDNORMALIZEDSTAT,addnormalizedstatMode);
    152         MODECASE(DETTOOL_MODE_NORMALIZEDSTAT,   normalizedstatMode);
    153         MODECASE(DETTOOL_MODE_REVERTNORMALIZEDSTAT, revertnormalizedstatMode);
    154         // normalizedimfile
    155         MODECASE(DETTOOL_MODE_TONORMALIZE,      tonormalizeMode);
    156         MODECASE(DETTOOL_MODE_ADDNORMALIZEDIMFILE,addnormalizedimfileMode);
    157         MODECASE(DETTOOL_MODE_NORMALIZEDIMFILE, normalizedimfileMode);
    158         MODECASE(DETTOOL_MODE_REVERTNORMALIZEDIMFILE, revertnormalizedimfileMode);
    159         // normalizedexp
    160         MODECASE(DETTOOL_MODE_TONORMALIZEDEXP,  tonormalizedexpMode);
    161         MODECASE(DETTOOL_MODE_ADDNORMALIZEDEXP, addnormalizedexpMode);
    162         MODECASE(DETTOOL_MODE_NORMALIZEDEXP,    normalizedexpMode);
    163         MODECASE(DETTOOL_MODE_REVERTNORMALIZEDEXP, revertnormalizedexpMode);
    164         // residimfile
    165         MODECASE(DETTOOL_MODE_TORESIDIMFILE,    toresidimfileMode);
    166         MODECASE(DETTOOL_MODE_ADDRESIDIMFILE,   addresidimfileMode);
    167         MODECASE(DETTOOL_MODE_RESIDIMFILE,      residimfileMode);
    168         MODECASE(DETTOOL_MODE_REVERTRESIDIMFILE,revertresidimfileMode);
    169         // residexp
    170         MODECASE(DETTOOL_MODE_TORESIDEXP,       toresidexpMode);
    171         MODECASE(DETTOOL_MODE_ADDRESIDEXP,      addresidexpMode);
    172         MODECASE(DETTOOL_MODE_RESIDEXP,         residexpMode);
    173         MODECASE(DETTOOL_MODE_REVERTRESIDEXP,   revertresidexpMode);
    174         MODECASE(DETTOOL_MODE_UPDATERESIDEXP,   updateresidexpMode);
    175         // detrunsummary
    176         MODECASE(DETTOOL_MODE_TODETRUNSUMMARY,  todetrunsummaryMode);
    177         MODECASE(DETTOOL_MODE_ADDDETRUNSUMMARY, adddetrunsummaryMode);
    178         MODECASE(DETTOOL_MODE_DETRUNSUMMARY,    detrunsummaryMode);
    179         MODECASE(DETTOOL_MODE_REVERTDETRUNSUMMARY, revertdetrunsummaryMode);
    180         MODECASE(DETTOOL_MODE_UPDATEDETRUN,     updatedetrunMode);
    181         MODECASE(DETTOOL_MODE_RERUN,            rerunMode);
    182         // register
    183         MODECASE(DETTOOL_MODE_REGISTER_DETREND, register_detrendMode);
    184         MODECASE(DETTOOL_MODE_REGISTER_DETREND_IMFILE, register_detrend_imfileMode);
    185         default:
    186             psAbort("invalid option (this should not happen)");
    187     }
    188 
    189     psFree(config);
    190     pmConfigDone();
    191     psLibFinalize();
    192 
    193     exit(EXIT_SUCCESS);
    194 
    195 FAIL:
    196     psErrorStackPrint(stderr, "\n");
    197     int exit_status = pxerrorGetExitStatus();
    198 
    199     psFree(config);
    200     pmConfigDone();
    201     psLibFinalize();
    202 
    203     exit(exit_status);
    204 }
    205 
    206 static bool pendingMode(pxConfig *config)
    207 {
    208     PS_ASSERT_PTR_NON_NULL(config, NULL);
    209 
    210      psString query = psStringCopy(
    211         "SELECT"
    212         "   rawExp.*"
    213         " FROM rawExp"
    214         " LEFT JOIN detInputExp"
    215         "   ON rawExp.exp_id = detInputExp.exp_id"
    216         " WHERE"
    217         "    detInputExp.exp_id IS NULL"
    218         "    AND rawExp.object != 'object'"
    219     );
    220 
    221     if (config->where) {
    222         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "rawExp");
    223         psStringAppend(&query, " AND %s", whereClause);
    224         psFree(whereClause);
    225     }
    226 
    227     if (!p_psDBRunQuery(config->dbh, query)) {
    228         psError(PS_ERR_UNKNOWN, false, "database error");
    229         psFree(query);
    230         return false;
    231     }
    232     psFree(query);
    233 
    234     psArray *output = p_psDBFetchResult(config->dbh);
    235     if (!output) {
    236         psError(PS_ERR_UNKNOWN, false, "database error");
    237         return false;
    238     }
    239     if (!psArrayLength(output)) {
    240         psTrace("dettool", PS_LOG_INFO, "no rows found");
    241         psFree(output);
    242         return true;
    243     }
    244 
    245     bool simple = false;
    246     {
    247         bool status = false;
    248         simple = psMetadataLookupBool(&status, config->args, "-simple");
    249         if (!status) {
    250             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    251             return false;
    252         }
    253     }
    254 
    255     // negative simple so the default is true
    256     if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
    257         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    258         psFree(output);
    259         return false;
    260     }
    261 
    262     psFree(output);
    263 
    264     return true;
    265 }
    266 
    267 static bool definebytagMode(pxConfig *config)
    268 {
    269     bool status     = false;
    270 
    271     PS_ASSERT_PTR_NON_NULL(config, false);
    272 
    273     // what type of detRun is this?
    274     // det_type is required
    275     psString det_type = psMetadataLookupStr(&status, config->args, "-det_type");
    276     if (!status) {
    277         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_type");
    278         return false;
    279     }
    280     if (!det_type) {
    281         psError(PS_ERR_UNKNOWN, true, "-det_type is required");
    282         return false;
    283     }
    284 
    285     // optional
    286     psString filelevel = psMetadataLookupStr(&status, config->args, "-filelevel");
    287     if (!status) {
    288         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filelevel");
    289         return false;
    290     }
    291 
    292     psString workdir = psMetadataLookupStr(&status, config->args, "-workdir");
    293     if (!status) {
    294         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -workdir");
    295         return false;
    296     }
    297     if (!workdir) {
    298         psError(PS_ERR_UNKNOWN, true, "-workdir is required");
    299         return false;
    300     }
    301 
    302     // optional
    303     psString mode = psMetadataLookupStr(&status, config->args, "-mode");
    304     if (!status) {
    305         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -mode");
    306         return false;
    307     }
    308     // check mode
    309     if (mode && !isValidMode(config, mode)) {
    310         psError(PS_ERR_UNKNOWN, false, "invalud mode");
    311         return false;
    312     }
    313     psString camera = psMetadataLookupStr(&status, config->args, "-inst");
    314     if (!status) {
    315         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -inst");
    316         return false;
    317     }
    318 
    319     psString telescope = psMetadataLookupStr(&status, config->args, "-telescope");
    320     if (!status) {
    321         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -telescope");
    322         return false;
    323     }
    324 
    325     psString exp_type = psMetadataLookupStr(&status, config->args, "-exp_type");
    326     if (!status) {
    327         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_type");
    328         return false;
    329     }
    330 
    331     psString filter = psMetadataLookupStr(&status, config->args, "-filter");
    332     if (!status) {
    333         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filter");
    334         return false;
    335     }
    336 
    337     psF32 airmass_min = psMetadataLookupF32(&status, config->args, "-airmass_min");
    338     if (!status) {
    339         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -airmass_min");
    340         return false;
    341     }
    342 
    343     psF32 airmass_max = psMetadataLookupF32(&status, config->args, "-airmass_max");
    344     if (!status) {
    345         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -airmass_max");
    346         return false;
    347     }
    348 
    349     psF32 exp_time_min = psMetadataLookupF32(&status, config->args, "-exp_time_min");
    350     if (!status) {
    351         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_time_min");
    352         return false;
    353     }
    354 
    355     psF32 exp_time_max = psMetadataLookupF32(&status, config->args, "-exp_time_max");
    356     if (!status) {
    357         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_time_max");
    358         return false;
    359     }
    360 
    361     psF32 ccd_temp_min = psMetadataLookupF32(&status, config->args, "-ccd_temp_min");
    362     if (!status) {
    363         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -ccd_temp_min");
    364         return false;
    365     }
    366 
    367     psF32 ccd_temp_max = psMetadataLookupF32(&status, config->args, "-ccd_temp_max");
    368     if (!status) {
    369         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -ccd_temp_max");
    370         return false;
    371     }
    372 
    373     psF64 posang_min = psMetadataLookupF32(&status, config->args, "-posang_min");
    374     if (!status) {
    375         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -posang_min");
    376         return false;
    377     }
    378 
    379     psF64 posang_max = psMetadataLookupF32(&status, config->args, "-posang_max");
    380     if (!status) {
    381         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -posang_max");
    382         return false;
    383     }
    384     psF64 solang_min = psMetadataLookupF32(&status, config->args, "-solang_min");
    385     if (!status) {
    386         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -solang_min");
    387         return false;
    388     }
    389 
    390     psF64 solang_max = psMetadataLookupF32(&status, config->args, "-solang_max");
    391     if (!status) {
    392         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -solang_max");
    393         return false;
    394     }
    395 
    396     psTime *registered = NULL;
    397     {
    398         psString registeredStr = psMetadataLookupStr(&status, config->args, "-registered");
    399         if (!status) {
    400             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -registered");
    401             return false;
    402         }
    403         // pass through NULL as this is an optional field
    404         if (registeredStr) {
    405             registered = psTimeFromISO(registeredStr, PS_TIME_UTC);
    406             if (!registered) {
    407                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", registeredStr);
    408                 return false;
    409             }
    410         } else {
    411             registered = NULL;
    412         }
    413     }
    414 
    415     psTime *time_begin = NULL;
    416     {
    417         psString time_beginStr = psMetadataLookupStr(&status, config->args, "-time_begin");
    418         if (!status) {
    419             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -time_begin");
    420             return false;
    421         }
    422         // pass through NULL as this is an optional field
    423         if (time_beginStr) {
    424             time_begin = psTimeFromISO(time_beginStr, PS_TIME_UTC);
    425             if (!time_begin) {
    426                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", time_beginStr);
    427                 return false;
    428             }
    429         } else {
    430             time_begin = NULL;
    431         }
    432     }
    433 
    434     psTime *time_end = NULL;
    435     {
    436         psString time_endStr = psMetadataLookupStr(&status, config->args, "-time_end");
    437         if (!status) {
    438             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -time_end");
    439             return false;
    440         }
    441         // pass through NULL as this is an optional field
    442         if (time_endStr) {
    443             time_end = psTimeFromISO(time_endStr, PS_TIME_UTC);
    444             if (!time_end) {
    445                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", time_endStr);
    446                 return false;
    447             }
    448         } else {
    449             time_end = NULL;
    450         }
    451     }
    452 
    453     psTime *use_begin = NULL;
    454     {
    455         psString use_beginStr = psMetadataLookupStr(&status, config->args, "-use_begin");
    456         if (!status) {
    457             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -use_begin");
    458             return false;
    459         }
    460         // pass through NULL as this is an optional field
    461         if (use_beginStr) {
    462             use_begin = psTimeFromISO(use_beginStr, PS_TIME_UTC);
    463             if (!use_begin) {
    464                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", use_beginStr);
    465                 return false;
    466             }
    467         } else {
    468             use_begin = NULL;
    469         }
    470     }
    471 
    472     psTime *use_end = NULL;
    473     {
    474         psString use_endStr = psMetadataLookupStr(&status, config->args, "-use_end");
    475         if (!status) {
    476             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -use_end");
    477             return false;
    478         }
    479         // pass through NULL as this is an optional field
    480         if (use_endStr) {
    481             use_end = psTimeFromISO(use_endStr, PS_TIME_UTC);
    482             if (!use_end) {
    483                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", use_endStr);
    484                 return false;
    485             }
    486         } else {
    487             use_end = NULL;
    488         }
    489     }
    490 
    491     psString reduction = psMetadataLookupStr(&status, config->args, "-reduction");
    492     if (!status) {
    493         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -reduction");
    494         return false;
    495     }
    496 
    497     psString label = psMetadataLookupStr(&status, config->args, "-label");
    498     if (!status) {
    499         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
    500         return false;
    501     }
    502 
    503     // we have to support multipe exp_ids
    504     psMetadataItem *item = psMetadataLookup(config->args, "-exp_id");
    505     if (!item) {
    506         // this shouldn't actually happen when using psArgs
    507         psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
    508         return false;
    509     }
    510     psMetadata *where = psMetadataAlloc();
    511 
    512     // make sure that -exp_id was parsed correctly
    513     // XXX this can be removed someday
    514     if (item->type == PS_DATA_METADATA_MULTI) {
    515         psListIterator *iter = psListIteratorAlloc(item->data.list, 0, false);
    516         psMetadataItem *mItem = NULL;
    517         while ((mItem = psListGetAndIncrement(iter))) {
    518             psString exp_id = mItem->data.V;
    519             // if exp_id is NULL then it means that -exp_id has not been
    520             // specified
    521             if (!exp_id) {
    522                 psError(PS_ERR_UNKNOWN, true,
    523                         "at least one -exp_id is required");
    524                 psFree(where);
    525                 return false;
    526             }
    527 
    528             if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id",
    529                  PS_META_DUPLICATE_OK, "==", exp_id)) {
    530                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    531                 psFree(iter);
    532                 psFree(where);
    533                 return false;
    534             }
    535         }
    536         psFree(iter);
    537     } else {
    538         psAbort(                "-exp_id was not parsed correctly (this should not happen");
    539     }
    540 
    541     if (psListLength(where->list) < 1) {
    542         psFree(where);
    543         where = NULL;
    544     }
    545 
    546     // check that the specified exp_ids actually exist
    547     psArray *detrendExps = rawExpSelectRowObjects(config->dbh, where, 0);
    548     psFree(where);
    549     if (!detrendExps) {
    550         psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
    551         return false;
    552     }
    553 
    554     // we should have one rawExp row per exp_id specified
    555     if (psListLength(item->data.list) != psArrayLength(detrendExps)) {
    556         psAbort(    "an -exp_id matched more then one rawExp (this should not happen");
    557 
    558     }
    559 
    560     // check to see if -filelevel was set on the command line
    561     if (!filelevel) {
    562         filelevel = psStringCopy(((rawExpRow *)detrendExps->data[0])->filelevel);
    563     }
    564 
    565     // start a transaction so we don't end up with childlessed det_ids
    566     if (!psDBTransaction(config->dbh)) {
    567         psError(PS_ERR_UNKNOWN, false, "database error");
    568         psFree(detrendExps);
    569         return false;
    570     }
    571 
    572     // the first iteration is always 0
    573     // XXX the camera name is set from the first inputExp
    574     // XXX det_id
    575     detRunInsert(config->dbh,
    576                  0,
    577                  0,
    578                  det_type,
    579                  mode,
    580                  "run",
    581                  filelevel,
    582                  workdir,
    583                  camera,
    584                  telescope,
    585                  exp_type,
    586                  reduction,
    587                  filter,
    588                  airmass_min,
    589                  airmass_max,
    590                  exp_time_min,
    591                  exp_time_max,
    592                  ccd_temp_min,
    593                  ccd_temp_max,
    594                  posang_min,
    595                  posang_max,
    596                  registered,
    597                  time_begin,
    598                  time_end,
    599                  use_begin,
    600                  use_end,
    601                  solang_min,
    602                  solang_max,
    603                  label,
    604                  0       // parent
    605         );
    606     psFree(registered);
    607     psFree(time_begin);
    608     psFree(time_end);
    609     psFree(use_begin);
    610     psFree(use_end);
    611     psS64 det_id = psDBLastInsertID(config->dbh);
    612 
    613     // create new detInputExp row(s) from the rawExp row(s)
    614     psArray *inputExps = psArrayAllocEmpty(psArrayLength(detrendExps));
    615     for (long i = 0; i < psArrayLength(detrendExps); i++) {
    616         detInputExpRow *inputExp = rawDetrenTodetInputExpRow(
    617             detrendExps->data[i],
    618             det_id,
    619             0 // the first iteration is explicitly 0
    620         );
    621         psArrayAdd(inputExps, 0, inputExp);
    622         psFree(inputExp);
    623     }
    624 
    625     psFree(detrendExps);
    626 
    627     // insert detInputExp objects into the database
    628     if (!detInputExpInsertObjects(config->dbh, inputExps)) {
    629         psError(PS_ERR_UNKNOWN, false, "database error");
    630         // rollback
    631         if (!psDBRollback(config->dbh)) {
    632             psError(PS_ERR_UNKNOWN, false, "database error");
    633         }
    634         psFree(inputExps);
    635         return false;
    636     }
    637     psFree(inputExps);
    638 
    639     // point of no return for det_id creation
    640     if (!psDBCommit(config->dbh)) {
    641         psError(PS_ERR_UNKNOWN, false, "database error");
    642         return false;
    643     }
    644 
    645     bool simple = false;
    646     {
    647         bool status = false;
    648         simple = psMetadataLookupBool(&status, config->args, "-simple");
    649         if (!status) {
    650             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    651             return false;
    652         }
    653     }
    654 
    655     // print the new det_id
    656     psArray *detRuns = NULL;
    657     {
    658         psMetadata *where = psMetadataAlloc();
    659         psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
    660         detRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
    661         psFree(where);
    662     }
    663     if (!detRuns) {
    664         psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
    665         return false;
    666     }
    667     // sanity check results
    668     if (psArrayLength(detRuns) != 1) {
    669         psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", det_id);
    670         return false;
    671     }
    672 
    673     if (!convertIdToStr(detRuns)) {
    674         psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
    675         psFree(detRuns);
    676         return false;
    677     }
    678 
    679     // negative simple so the default is true
    680     if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
    681         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    682         psFree(detRuns);
    683         return false;
    684     }
    685     psFree(detRuns);
    686 
    687     return true;
    688 }
    689 
    690 #if 0
    691 // This function is used to convert the det_id from an int, as it is used
    692 // internally, to be a string for external use.  The rational being that we may
    693 // want to change how det_id is generated in the future and don't want to
    694 // external programs to become depending on this value being an int.
    695 static bool convertDetIdToStr(psArray *mds)
    696 {
    697     PS_ASSERT_PTR_NON_NULL(mds, false);
    698 
    699     for (long i = 0; i < psArrayLength(mds); i++) {
    700         psMetadata *md = mds->data[i];
    701         bool status = false;
    702         psS32 det_id = psMetadataLookupS32(&status, md, "det_id");
    703         if (!status) {
    704             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for det_id");
    705             return false;
    706         }
    707         psMetadataRemoveKey(md, "det_id");
    708         psString det_idStr = psDBIntToString((psU64)det_id);
    709         psMetadataAddStr(mds->data[i], PS_LIST_HEAD, "det_id", 0, NULL, det_idStr);
    710         psFree(det_idStr);
    711     }
    712 
    713     return true;
    714 }
    715 #endif
    716 
    717 static bool definebyqueryMode(pxConfig *config)
    718 {
    719     bool status     = false;
    720 
    721     PS_ASSERT_PTR_NON_NULL(config, false);
    722 
    723     // what type of detRun is this?
    724     // det_type is required
    725     psString det_type = psMetadataLookupStr(&status, config->args, "-det_type");
    726     if (!status) {
    727         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_type");
    728         return false;
    729     }
    730     if (!det_type) {
    731         psError(PS_ERR_UNKNOWN, true, "-det_type is required");
    732         return false;
    733     }
    734 
    735     // optional
    736     psString filelevel = psMetadataLookupStr(&status, config->args, "-filelevel");
    737     if (!status) {
    738         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filelevel");
    739         return false;
    740     }
    741 
    742     psString workdir = psMetadataLookupStr(&status, config->args, "-workdir");
    743     if (!status) {
    744         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -workdir");
    745         return false;
    746     }
    747     if (!workdir) {
    748         psError(PS_ERR_UNKNOWN, true, "-workdir is required");
    749         return false;
    750     }
    751     psString camera = psMetadataLookupStr(&status, config->args, "-inst");
    752     if (!status) {
    753         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -inst");
    754         return false;
    755     }
    756     if (!camera) {
    757         psError(PS_ERR_UNKNOWN, false, "-inst is required");
    758         return false;
    759     }
    760 
    761     psMetadata *where = psMetadataAlloc();
    762     {
    763         bool status = false;
    764         psString exp_type = psMetadataLookupStr(&status, config->args, "-select_exp_type");
    765         if (!status) {
    766             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_exp_type");
    767             return false;
    768         }
    769         if (exp_type) {
    770             if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_type", 0, "==", exp_type)) {
    771                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_type");
    772                 psFree(where);
    773                 return false;
    774             }
    775         }
    776 
    777         // map -inst -> camera
    778         psString camera = psMetadataLookupStr(&status, config->args, "-select_inst");
    779         if (!status) {
    780             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_inst");
    781             return false;
    782         }
    783         if (camera) {
    784             if (!psMetadataAddStr(where, PS_LIST_TAIL, "camera", 0, "==", camera)) {
    785                 psError(PS_ERR_UNKNOWN, false, "failed to add item inst");
    786                 psFree(where);
    787                 return false;
    788             }
    789         }
    790 
    791         psString telescope = psMetadataLookupStr(&status, config->args, "-select_telescope");
    792         if (!status) {
    793             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_telescope");
    794             return false;
    795         }
    796         if (telescope) {
    797             if (!psMetadataAddStr(where, PS_LIST_TAIL, "telescope", 0, "==", telescope)) {
    798                 psError(PS_ERR_UNKNOWN, false, "failed to add item telescope");
    799                 psFree(where);
    800                 return false;
    801             }
    802         }
    803 
    804         psString filter = psMetadataLookupStr(&status, config->args, "-select_filter");
    805         if (!status) {
    806             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_filter");
    807             return false;
    808         }
    809         if (filter) {
    810             if (!psMetadataAddStr(where, PS_LIST_TAIL, "filter", 0, "==", filter)) {
    811                 psError(PS_ERR_UNKNOWN, false, "failed to add item filter");
    812                 psFree(where);
    813                 return false;
    814             }
    815         }
    816 
    817         psString uri = psMetadataLookupStr(&status, config->args, "-select_uri");
    818         if (!status) {
    819             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_uri");
    820             return false;
    821         }
    822         if (uri) {
    823             if (!psMetadataAddStr(where, PS_LIST_TAIL, "uri", 0, "==", uri)) {
    824                 psError(PS_ERR_UNKNOWN, false, "failed to add item uri");
    825                 psFree(where);
    826                 return false;
    827             }
    828         }
    829 
    830         psString dateobs_begin = psMetadataLookupStr(&status, config->args, "-select_dateobs_begin");
    831         if (!status) {
    832             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_dateobs_begin");
    833             psFree(where);
    834             return false;
    835         }
    836         if (dateobs_begin) {
    837             psTime *time = psTimeFromISO(dateobs_begin, PS_TIME_UTC);
    838             if (!time) {
    839                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", dateobs_begin);
    840                 psFree(time);
    841                 psFree(where);
    842                 return false;
    843             }
    844             if (!psMetadataAddTime(where, PS_LIST_TAIL, "dateobs", PS_META_DUPLICATE_OK, ">=", time)) {
    845                 psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
    846                 psFree(time);
    847                 psFree(where);
    848                 return false;
    849             }
    850             psFree(time);
    851         }
    852 
    853         psString dateobs_end = psMetadataLookupStr(&status, config->args, "-select_dateobs_end");
    854         if (!status) {
    855             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_dateobs_end");
    856             psFree(where);
    857             return false;
    858         }
    859         if (dateobs_end) {
    860             psTime *time = psTimeFromISO(dateobs_end, PS_TIME_UTC);
    861             if (!time) {
    862                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", dateobs_end);
    863                 psFree(time);
    864                 psFree(where);
    865                 return false;
    866             }
    867             if (!psMetadataAddTime(where, PS_LIST_TAIL, "dateobs", PS_META_DUPLICATE_OK, "<", time)) {
    868                 psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
    869                 psFree(time);
    870                 psFree(where);
    871                 return false;
    872             }
    873             psFree(time);
    874         }
    875 
    876         /** selection based on airmass range **/
    877         psF32 select_airmass_min = psMetadataLookupF32(&status, config->args, "-select_airmass_min");
    878         if (!status) {
    879             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_airmass_min");
    880             psFree(where);
    881             return false;
    882         }
    883         if (isfinite(select_airmass_min)) {
    884             if (!psMetadataAddF32(where, PS_LIST_TAIL, "airmass", PS_META_DUPLICATE_OK, ">=", select_airmass_min)) {
    885                 psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
    886                 psFree(where);
    887                 return false;
    888             }
    889         }
    890         psF32 select_airmass_max = psMetadataLookupF32(&status, config->args, "-select_airmass_max");
    891         if (!status) {
    892             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_airmass_max");
    893             psFree(where);
    894             return false;
    895         }
    896         if (isfinite(select_airmass_max)) {
    897             if (!psMetadataAddF32(where, PS_LIST_TAIL, "airmass", PS_META_DUPLICATE_OK, "<=", select_airmass_max)) {
    898                 psError(PS_ERR_UNKNOWN, false, "failed to add item airmass");
    899                 psFree(where);
    900                 return false;
    901             }
    902         }
    903 
    904         psF32 select_sat_pixel_frac_max = psMetadataLookupF32(&status, config->args, "-select_sat_pixel_frac_max");
    905         if (!status) {
    906             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_sat_pixel_frac_max");
    907             psFree(where);
    908             return false;
    909         }
    910         if (isfinite(select_sat_pixel_frac_max)) {
    911             if (!psMetadataAddF32(where, PS_LIST_TAIL, "sat_pixel_frac", PS_META_DUPLICATE_OK, "<=", select_sat_pixel_frac_max)) {
    912                 psError(PS_ERR_UNKNOWN, false, "failed to add item sat_pixel_frac");
    913                 psFree(where);
    914                 return false;
    915             }
    916         }
    917 
    918         /** selection based on exp_time range **/
    919         psF32 select_exp_time_min = psMetadataLookupF32(&status, config->args, "-select_exp_time_min");
    920         if (!status) {
    921             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_exp_time_min");
    922             psFree(where);
    923             return false;
    924         }
    925         if (isfinite(select_exp_time_min)) {
    926             if (!psMetadataAddF32(where, PS_LIST_TAIL, "exp_time", PS_META_DUPLICATE_OK, ">=", select_exp_time_min)) {
    927                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
    928                 psFree(where);
    929                 return false;
    930             }
    931         }
    932         psF32 select_exp_time_max = psMetadataLookupF32(&status, config->args, "-select_exp_time_max");
    933         if (!status) {
    934             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_exp_time_max");
    935             psFree(where);
    936             return false;
    937         }
    938         if (isfinite(select_exp_time_max)) {
    939             if (!psMetadataAddF32(where, PS_LIST_TAIL, "exp_time", PS_META_DUPLICATE_OK, "<=", select_exp_time_max)) {
    940                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_time");
    941                 psFree(where);
    942                 return false;
    943             }
    944         }
    945 
    946         /** selection based on ccd_temp range **/
    947         psF32 select_ccd_temp_min = psMetadataLookupF32(&status, config->args, "-select_ccd_temp_min");
    948         if (!status) {
    949             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_ccd_temp_min");
    950             psFree(where);
    951             return false;
    952         }
    953         if (isfinite(select_ccd_temp_min)) {
    954             if (!psMetadataAddF32(where, PS_LIST_TAIL, "ccd_temp", PS_META_DUPLICATE_OK, ">=", select_ccd_temp_min)) {
    955                 psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
    956                 psFree(where);
    957                 return false;
    958             }
    959         }
    960         psF32 select_ccd_temp_max = psMetadataLookupF32(&status, config->args, "-select_ccd_temp_max");
    961         if (!status) {
    962             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_ccd_temp_max");
    963             psFree(where);
    964             return false;
    965         }
    966         if (isfinite(select_ccd_temp_max)) {
    967             if (!psMetadataAddF32(where, PS_LIST_TAIL, "ccd_temp", PS_META_DUPLICATE_OK, "<=", select_ccd_temp_max)) {
    968                 psError(PS_ERR_UNKNOWN, false, "failed to add item ccd_temp");
    969                 psFree(where);
    970                 return false;
    971             }
    972         }
    973 
    974         /** selection based on posang **/
    975         psF32 select_posang_min = psMetadataLookupF32(&status, config->args, "-select_posang_min");
    976         if (!status) {
    977             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_posang_min");
    978             psFree(where);
    979             return false;
    980         }
    981         if (isfinite(select_posang_min)) {
    982             if (!psMetadataAddF32(where, PS_LIST_TAIL, "posang", PS_META_DUPLICATE_OK, ">=", select_posang_min)) {
    983                 psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
    984                 psFree(where);
    985                 return false;
    986             }
    987         }
    988         psF32 select_posang_max = psMetadataLookupF32(&status, config->args, "-select_posang_max");
    989         if (!status) {
    990             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_posang_max");
    991             psFree(where);
    992             return false;
    993         }
    994         if (isfinite(select_posang_max)) {
    995             if (!psMetadataAddF32(where, PS_LIST_TAIL, "posang", PS_META_DUPLICATE_OK, "<=", select_posang_max)) {
    996                 psError(PS_ERR_UNKNOWN, false, "failed to add item posang");
    997                 psFree(where);
    998                 return false;
    999             }
    1000         }
    1001 
    1002         /** selection based on solang **/
    1003         psF32 select_solang_min = psMetadataLookupF32(&status, config->args, "-select_solang_min");
    1004         if (!status) {
    1005             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_solang_min");
    1006             psFree(where);
    1007             return false;
    1008         }
    1009         if (isfinite(select_solang_min)) {
    1010             if (!psMetadataAddF32(where, PS_LIST_TAIL, "solang", PS_META_DUPLICATE_OK, ">=", select_solang_min)) {
    1011                 psError(PS_ERR_UNKNOWN, false, "failed to add item solang");
    1012                 psFree(where);
    1013                 return false;
    1014             }
    1015         }
    1016         psF32 select_solang_max = psMetadataLookupF32(&status, config->args, "-select_solang_max");
    1017         if (!status) {
    1018             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -select_solang_max");
    1019             psFree(where);
    1020             return false;
    1021         }
    1022         if (isfinite(select_solang_max)) {
    1023             if (!psMetadataAddF32(where, PS_LIST_TAIL, "solang", PS_META_DUPLICATE_OK, "<=", select_solang_max)) {
    1024                 psError(PS_ERR_UNKNOWN, false, "failed to add item solang");
    1025                 psFree(where);
    1026                 return false;
    1027             }
    1028         }
    1029 
    1030     }
    1031 
    1032     if (!psListLength(where->list)) {
    1033         psFree(where);
    1034         where = NULL;
    1035     }
    1036 
    1037     // there is some namespace overlap between the names of the fields we'd
    1038     // like to search by to setup a detrun and the names of the fields we'd
    1039     // like to assign values to so I've seperated them but prepending set- to
    1040     // the assigned values
    1041 
    1042     // optional
    1043     psString mode = psMetadataLookupStr(&status, config->args, "-mode");
    1044     if (!status) {
    1045         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -mode");
    1046         return false;
    1047     }
    1048     // check mode
    1049     if (mode && !isValidMode(config, mode)) {
    1050         psError(PS_ERR_UNKNOWN, false, "invalud mode");
    1051         return false;
    1052     }
    1053 
    1054     psString telescope = psMetadataLookupStr(&status, config->args, "-telescope");
    1055     if (!status) {
    1056         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -telescope");
    1057         return false;
    1058     }
    1059 
    1060     psString filter = psMetadataLookupStr(&status, config->args, "-filter");
    1061     if (!status) {
    1062         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filter");
    1063         return false;
    1064     }
    1065 
    1066     psF32 airmass_min = psMetadataLookupF32(&status, config->args, "-airmass_min");
    1067     if (!status) {
    1068         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -airmass_min");
    1069         return false;
    1070     }
    1071 
    1072     psF32 airmass_max = psMetadataLookupF32(&status, config->args, "-airmass_max");
    1073     if (!status) {
    1074         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -airmass_max");
    1075         return false;
    1076     }
    1077 
    1078     psF32 exp_time_min = psMetadataLookupF32(&status, config->args, "-exp_time_min");
    1079     if (!status) {
    1080         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_time_min");
    1081         return false;
    1082     }
    1083 
    1084     psF32 exp_time_max = psMetadataLookupF32(&status, config->args, "-exp_time_max");
    1085     if (!status) {
    1086         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_time_max");
    1087         return false;
    1088     }
    1089 
    1090     psF32 ccd_temp_min = psMetadataLookupF32(&status, config->args, "-ccd_temp_min");
    1091     if (!status) {
    1092         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -ccd_temp_min");
    1093         return false;
    1094     }
    1095 
    1096     psF32 ccd_temp_max = psMetadataLookupF32(&status, config->args, "-ccd_temp_max");
    1097     if (!status) {
    1098         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -ccd_temp_max");
    1099         return false;
    1100     }
    1101 
    1102     psF64 posang_min = psMetadataLookupF32(&status, config->args, "-posang_min");
    1103     if (!status) {
    1104         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -posang_min");
    1105         return false;
    1106     }
    1107 
    1108     psF64 posang_max = psMetadataLookupF32(&status, config->args, "-posang_max");
    1109     if (!status) {
    1110         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -posang_max");
    1111         return false;
    1112     }
    1113 
    1114     psF64 solang_min = psMetadataLookupF32(&status, config->args, "-solang_min");
    1115     if (!status) {
    1116         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -solang_min");
    1117         return false;
    1118     }
    1119 
    1120     psF64 solang_max = psMetadataLookupF32(&status, config->args, "-solang_max");
    1121     if (!status) {
    1122         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -solang_max");
    1123         return false;
    1124     }
    1125 
    1126     psTime *registered = NULL;
    1127     {
    1128         psString registeredStr = psMetadataLookupStr(&status, config->args, "-registered");
    1129         if (!status) {
    1130             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -registered");
    1131             return false;
    1132         }
    1133         // pass through NULL as this is an optional field
    1134         if (registeredStr) {
    1135             registered = psTimeFromISO(registeredStr, PS_TIME_UTC);
    1136             if (!registered) {
    1137                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", registeredStr);
    1138                 return false;
    1139             }
    1140         } else {
    1141             registered = NULL;
    1142         }
    1143     }
    1144 
    1145     psTime *time_begin = NULL;
    1146     {
    1147         psString time_beginStr = psMetadataLookupStr(&status, config->args, "-time_begin");
    1148         if (!status) {
    1149             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -time_begin");
    1150             return false;
    1151         }
    1152         // pass through NULL as this is an optional field
    1153         if (time_beginStr) {
    1154             time_begin = psTimeFromISO(time_beginStr, PS_TIME_UTC);
    1155             if (!time_begin) {
    1156                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", time_beginStr);
    1157                 return false;
    1158             }
    1159         } else {
    1160             time_begin = NULL;
    1161         }
    1162     }
    1163 
    1164     psTime *time_end = NULL;
    1165     {
    1166         psString time_endStr = psMetadataLookupStr(&status, config->args, "-time_end");
    1167         if (!status) {
    1168             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -time_end");
    1169             return false;
    1170         }
    1171         // pass through NULL as this is an optional field
    1172         if (time_endStr) {
    1173             time_end = psTimeFromISO(time_endStr, PS_TIME_UTC);
    1174             if (!time_end) {
    1175                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", time_endStr);
    1176                 return false;
    1177             }
    1178         } else {
    1179             time_end = NULL;
    1180         }
    1181     }
    1182 
    1183     psTime *use_begin = NULL;
    1184     {
    1185         psString use_beginStr = psMetadataLookupStr(&status, config->args, "-use_begin");
    1186         if (!status) {
    1187             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -use_begin");
    1188             return false;
    1189         }
    1190         // pass through NULL as this is an optional field
    1191         if (use_beginStr) {
    1192             use_begin = psTimeFromISO(use_beginStr, PS_TIME_UTC);
    1193             if (!use_begin) {
    1194                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", use_beginStr);
    1195                 return false;
    1196             }
    1197         } else {
    1198             use_begin = NULL;
    1199         }
    1200     }
    1201 
    1202     psTime *use_end = NULL;
    1203     {
    1204         psString use_endStr = psMetadataLookupStr(&status, config->args, "-use_end");
    1205         if (!status) {
    1206             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -use_end");
    1207             return false;
    1208         }
    1209         // pass through NULL as this is an optional field
    1210         if (use_endStr) {
    1211             use_end = psTimeFromISO(use_endStr, PS_TIME_UTC);
    1212             if (!use_end) {
    1213                 psError(PS_ERR_UNKNOWN, false, "error in time format %s", use_endStr);
    1214                 return false;
    1215             }
    1216         } else {
    1217             use_end = NULL;
    1218         }
    1219     }
    1220 
    1221     psString reduction = psMetadataLookupStr(&status, config->args, "-reduction");
    1222     if (!status) {
    1223         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -reduction");
    1224         return false;
    1225     }
    1226 
    1227     psString label = psMetadataLookupStr(&status, config->args, "-label");
    1228     if (!status) {
    1229         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
    1230         return false;
    1231     }
    1232 
    1233     bool simple = psMetadataLookupBool(&status, config->args, "-simple");
    1234     if (!status) {
    1235         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    1236         psFree(registered);
    1237         psFree(use_begin);
    1238         psFree(use_end);
    1239         return false;
    1240     }
    1241 
    1242     bool pretend = psMetadataLookupBool(&status, config->args, "-pretend");
    1243     if (!status) {
    1244         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -pretend");
    1245         psFree(registered);
    1246         psFree(use_begin);
    1247         psFree(use_end);
    1248         return false;
    1249     }
    1250 
    1251     // search for rawExps with the specified options
    1252     psArray *detrendExps = rawExpSelectRowObjects(config->dbh, where, 0);
    1253     psFree(where);
    1254     // make sure that we found at least one rawExp
    1255     if (!detrendExps) {
    1256         psError(PS_ERR_UNKNOWN, false, "database error");
    1257         psFree(registered);
    1258         psFree(use_begin);
    1259         psFree(use_end);
    1260         return false;
    1261     }
    1262     if (!psArrayLength(detrendExps)) {
    1263         psTrace("dettool", PS_LOG_INFO, "no rows found");
    1264         psFree(detrendExps);
    1265         psFree(registered);
    1266         psFree(use_begin);
    1267         psFree(use_end);
    1268         return true;
    1269     }
    1270 
    1271     // check to see if -filelevel was set on the command line
    1272     if (!filelevel) {
    1273         filelevel = psStringCopy(((rawExpRow *)detrendExps->data[0])->filelevel);
    1274     }
    1275 
    1276     if (pretend) {
    1277         // negative simple so the default is true
    1278         if (!rawExpPrintObjects(stdout, detrendExps, !simple)) {
    1279             psError(PS_ERR_UNKNOWN, false, "failed to print array");
    1280             psFree(detrendExps);
    1281             psFree(registered);
    1282             psFree(use_begin);
    1283             psFree(use_end);
    1284             return false;
    1285         }
    1286         psFree(detrendExps);
    1287         psFree(registered);
    1288         psFree(use_begin);
    1289         psFree(use_end);
    1290         return true;
    1291     }
    1292 
    1293     // start a transaction so we don't end up with childlessed det_ids
    1294     if (!psDBTransaction(config->dbh)) {
    1295         psError(PS_ERR_UNKNOWN, false, "database error");
    1296         psFree(detrendExps);
    1297         psFree(registered);
    1298         psFree(use_begin);
    1299         psFree(use_end);
    1300         return false;
    1301     }
    1302 
    1303     // the first iteration is always 0
    1304     // XXX det_id
    1305     detRunInsert(config->dbh,
    1306                  0,      // det_id
    1307                  0,      // iteration
    1308                  det_type,
    1309                  mode,
    1310                  "run",  // state
    1311                  filelevel,
    1312                  workdir,
    1313                  camera,
    1314                  telescope,
    1315                  "NA",
    1316                  reduction,
    1317                  filter,
    1318                  airmass_min,
    1319                  airmass_max,
    1320                  exp_time_min,
    1321                  exp_time_max,
    1322                  ccd_temp_min,
    1323                  ccd_temp_max,
    1324                  posang_min,
    1325                  posang_max,
    1326                  registered,
    1327                  time_begin,
    1328                  time_end,
    1329                  use_begin,
    1330                  use_end,
    1331                  solang_min,
    1332                  solang_max,
    1333                  label,
    1334                  0       // parent
    1335         );
    1336     psFree(registered);
    1337     psFree(time_begin);
    1338     psFree(time_end);
    1339     psFree(use_end);
    1340     psFree(use_begin);
    1341     psFree(use_end);
    1342     psS64 det_id = psDBLastInsertID(config->dbh);
    1343 
    1344     // create new detInputExp row(s) from the rawExp row(s)
    1345     psArray *inputExps = psArrayAllocEmpty(psArrayLength(detrendExps));
    1346     for (long i = 0; i < psArrayLength(detrendExps); i++) {
    1347         detInputExpRow *inputExp = rawDetrenTodetInputExpRow(
    1348             detrendExps->data[i],
    1349             det_id,
    1350             0 // the first iteration is explicitly 0
    1351         );
    1352         psArrayAdd(inputExps, 0, inputExp);
    1353         psFree(inputExp);
    1354     }
    1355 
    1356     psFree(detrendExps);
    1357 
    1358     // insert detInputExp objects into the database
    1359     if (!detInputExpInsertObjects(config->dbh, inputExps)) {
    1360         psError(PS_ERR_UNKNOWN, false, "database error");
    1361         // rollback
    1362         if (!psDBRollback(config->dbh)) {
    1363             psError(PS_ERR_UNKNOWN, false, "database error");
    1364         }
    1365         psFree(inputExps);
    1366         return false;
    1367     }
    1368     psFree(inputExps);
    1369 
    1370     // point of no return for det_id creation
    1371     if (!psDBCommit(config->dbh)) {
    1372         psError(PS_ERR_UNKNOWN, false, "database error");
    1373         return false;
    1374     }
    1375 
    1376 
    1377     // print the new det_id
    1378     psArray *detRuns = NULL;
    1379     {
    1380         psMetadata *where = psMetadataAlloc();
    1381         psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
    1382         detRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
    1383         psFree(where);
    1384     }
    1385     if (!detRuns) {
    1386         psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
    1387         return false;
    1388     }
    1389     // sanity check results
    1390     if (psArrayLength(detRuns) != 1) {
    1391         psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", det_id);
    1392         return false;
    1393     }
    1394 
    1395     // convert det_id to a string externaly
    1396     if (!convertIdToStr(detRuns)) {
    1397         psError(PS_ERR_UNKNOWN, false, "failed to convert det_id to a string");
    1398         psFree(detRuns);
    1399         return false;
    1400     }
    1401 
    1402     // negative simple so the default is true
    1403     if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
    1404         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    1405         psFree(detRuns);
    1406         return false;
    1407     }
    1408     psFree(detRuns);
    1409 
    1410     return true;
    1411 }
    1412 
    1413 static bool definebydetrunMode(pxConfig *config)
    1414 {
    1415     bool status     = false;
    1416 
    1417     PS_ASSERT_PTR_NON_NULL(config, false);
    1418 
    1419     // det_id is the only required input
    1420     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    1421     if (!status) {
    1422         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    1423         return false;
    1424     }
    1425     if (!det_id) {
    1426         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    1427         return false;
    1428     }
    1429 
    1430     // lookup the detRun that we will be basing this one on
    1431     psArray *detRuns = NULL;
    1432     {
    1433         psMetadata *where = psMetadataAlloc();
    1434         psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", (psS64)atoll(det_id));
    1435         detRuns = detRunSelectRowObjects(config->dbh, where, 0);
    1436         psFree(where);
    1437     }
    1438     if (!detRuns) {
    1439         psError(PS_ERR_UNKNOWN, false, "database error");
    1440         return false;
    1441     }
    1442     // sanity check the result... we should have only found one det_id
    1443     if (psArrayLength(detRuns) != 1) {
    1444         psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", (psS64)atoll(det_id));
    1445         return false;                   // unreachable
    1446     }
    1447 
    1448     // pull the detRun object out the result array
    1449     detRunRow *detRun = psMemIncrRefCounter(detRuns->data[0]);
    1450 
    1451     // discard the resultarray
    1452     psFree(detRuns);
    1453 
    1454     // set the det_id to 0/NULL so the database can assign it
    1455     detRun->det_id = 0;
    1456 
    1457     // reset the iteration to 0
    1458     detRun->iteration = 0;
    1459 
    1460     // reset the state to "run"
    1461     psFree(detRun->state);
    1462     detRun->state = psStringCopy("run");
    1463 
    1464     // walk through the optional values and update the detRun as required
    1465     psString det_type = psMetadataLookupStr(&status, config->args, "-set_det_type");
    1466     if (!status) {
    1467         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_det_type");
    1468         return false;
    1469     }
    1470     if (det_type) {
    1471         psFree(detRun->det_type);
    1472         detRun->det_type = psStringCopy(det_type);
    1473     }
    1474 
    1475     psString mode = psMetadataLookupStr(&status, config->args, "-set_mode");
    1476     if (!status) {
    1477         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_mode");
    1478         return false;
    1479     }
    1480     // check mode
    1481     if (mode && !isValidMode(config, mode)) {
    1482         psError(PS_ERR_UNKNOWN, false, "invalud mode");
    1483         return false;
    1484     }
    1485     if (mode) {
    1486         psFree(detRun->mode);
    1487         detRun->mode = psStringCopy(mode);
    1488     }
    1489 
    1490     psString camera = psMetadataLookupStr(&status, config->args, "-set_inst");
    1491     if (!status) {
    1492         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_inst");
    1493         return false;
    1494     }
    1495     if (camera) {
    1496         psFree(detRun->camera);
    1497         detRun->camera = psStringCopy(camera);
    1498     }
    1499 
    1500     psString telescope = psMetadataLookupStr(&status, config->args, "-set_telescope");
    1501     if (!status) {
    1502         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_telescope");
    1503         return false;
    1504     }
    1505     if (telescope) {
    1506         psFree(detRun->telescope);
    1507         detRun->telescope = psStringCopy(telescope);
    1508     }
    1509 
    1510     psString exp_type = psMetadataLookupStr(&status, config->args, "-set_exp_type");
    1511     if (!status) {
    1512         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_exp_type");
    1513         return false;
    1514     }
    1515     if (exp_type) {
    1516         psFree(detRun->exp_type);
    1517         detRun->exp_type = psStringCopy(exp_type);
    1518     }
    1519 
    1520     psString filelevel = psMetadataLookupStr(&status, config->args, "-set_filelevel");
    1521     if (!status) {
    1522         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_filelevel");
    1523         return false;
    1524     }
    1525     if (filelevel) {
    1526         psFree(detRun->filelevel);
    1527         detRun->filelevel = psStringCopy(filelevel);
    1528     }
    1529 
    1530     psString workdir = psMetadataLookupStr(&status, config->args, "-set_workdir");
    1531     if (!status) {
    1532         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_workdir");
    1533         return false;
    1534     }
    1535     if (workdir) {
    1536         psFree(detRun->workdir);
    1537         detRun->workdir = psStringCopy(workdir);
    1538     }
    1539 
    1540     psString filter = psMetadataLookupStr(&status, config->args, "-set_filter");
    1541     if (!status) {
    1542         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_filter");
    1543         return false;
    1544     }
    1545     if (filter) {
    1546         psFree(detRun->filter);
    1547         detRun->filter = psStringCopy(filter);
    1548     }
    1549 
    1550     psF32 airmass_min = psMetadataLookupF32(&status, config->args, "-set_airmass_min");
    1551     if (!status) {
    1552         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_airmass_min");
    1553         return false;
    1554     }
    1555     if (!isnan(airmass_min)) {
    1556         detRun->airmass_min = airmass_min;
    1557     }
    1558 
    1559     psF32 airmass_max = psMetadataLookupF32(&status, config->args, "-set_airmass_max");
    1560     if (!status) {
    1561         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_airmass_max");
    1562         return false;
    1563     }
    1564     if (!isnan(airmass_max)) {
    1565         detRun->airmass_max = airmass_max;
    1566     }
    1567 
    1568     psF32 exp_time_min = psMetadataLookupF32(&status, config->args, "-set_exp_time_min");
    1569     if (!status) {
    1570         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_exp_time_min");
    1571         return false;
    1572     }
    1573     if (!isnan(exp_time_min)) {
    1574         detRun->exp_time_min = exp_time_min;
    1575     }
    1576 
    1577     psF32 exp_time_max = psMetadataLookupF32(&status, config->args, "-set_exp_time_max");
    1578     if (!status) {
    1579         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_exp_time_max");
    1580         return false;
    1581     }
    1582     if (!isnan(exp_time_max)) {
    1583         detRun->exp_time_max = exp_time_max;
    1584     }
    1585 
    1586     psF32 ccd_temp_min = psMetadataLookupF32(&status, config->args, "-set_ccd_temp_min");
    1587     if (!status) {
    1588         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_ccd_temp_min");
    1589         return false;
    1590     }
    1591     if (!isnan(ccd_temp_min)) {
    1592         detRun->ccd_temp_min = ccd_temp_min;
    1593     }
    1594 
    1595     psF32 ccd_temp_max = psMetadataLookupF32(&status, config->args, "-set_ccd_temp_max");
    1596     if (!status) {
    1597         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_ccd_temp_max");
    1598         return false;
    1599     }
    1600     if (!isnan(ccd_temp_max)) {
    1601         detRun->ccd_temp_max = ccd_temp_max;
    1602     }
    1603 
    1604     psF64 posang_min = psMetadataLookupF32(&status, config->args, "-set_posang_min");
    1605     if (!status) {
    1606         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_posang_min");
    1607         return false;
    1608     }
    1609     if (!isnan(posang_min)) {
    1610         detRun->posang_min = posang_min;
    1611     }
    1612 
    1613     psF64 posang_max = psMetadataLookupF32(&status, config->args, "-set_posang_max");
    1614     if (!status) {
    1615         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_posang_max");
    1616         return false;
    1617     }
    1618     if (!isnan(posang_max)) {
    1619         detRun->posang_max = posang_max;
    1620     }
    1621 
    1622     psF64 solang_min = psMetadataLookupF32(&status, config->args, "-set_solang_min");
    1623     if (!status) {
    1624         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_solang_min");
    1625         return false;
    1626     }
    1627     if (!isnan(solang_min)) {
    1628         detRun->solang_min = solang_min;
    1629     }
    1630 
    1631     psF64 solang_max = psMetadataLookupF32(&status, config->args, "-set_solang_max");
    1632     if (!status) {
    1633         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_solang_max");
    1634         return false;
    1635     }
    1636     if (!isnan(solang_max)) {
    1637         detRun->solang_max = solang_max;
    1638     }
    1639 
    1640     psString label = psMetadataLookupStr(&status, config->args, "-set_label");
    1641     if (!status) {
    1642         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_label");
    1643         return false;
    1644     }
    1645     if (label) {
    1646         detRun->label = label;
    1647     }
    1648 
    1649     psTime *registered = NULL;
    1650     {
    1651         psString registeredStr = psMetadataLookupStr(&status, config->args, "-set_registered");
    1652         if (!status) {
    1653             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_registered");
    1654             return false;
    1655         }
    1656         // pass through NULL as this is an optional field
    1657         if (registeredStr) {
    1658             psFree(detRun->registered);
    1659             registered = psTimeFromISO(registeredStr, PS_TIME_UTC);
    1660             detRun->registered = psMemIncrRefCounter(registered);
    1661             psFree(registered);
    1662         }
    1663     }
    1664 
    1665     psTime *time_begin = NULL;
    1666     {
    1667         psString time_beginStr = psMetadataLookupStr(&status, config->args, "-set_time_begin");
    1668         if (!status) {
    1669             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_time_begin");
    1670             return false;
    1671         }
    1672         // pass through NULL as this is an optional field
    1673         if (time_beginStr) {
    1674             psFree(detRun->time_begin);
    1675             time_begin = psTimeFromISO(time_beginStr, PS_TIME_UTC);
    1676             detRun->time_begin = psMemIncrRefCounter(time_begin);
    1677             psFree(time_begin);
    1678         }
    1679     }
    1680 
    1681     psTime *time_end = NULL;
    1682     {
    1683         psString time_endStr = psMetadataLookupStr(&status, config->args, "-set_time_end");
    1684         if (!status) {
    1685             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_time_end");
    1686             return false;
    1687         }
    1688         // pass through NULL as this is an optional field
    1689         if (time_endStr) {
    1690             psFree(detRun->time_end);
    1691             time_end = psTimeFromISO(time_endStr, PS_TIME_UTC);
    1692             detRun->time_end = psMemIncrRefCounter(time_end);
    1693             psFree(time_end);
    1694         }
    1695     }
    1696 
    1697     psTime *use_begin = NULL;
    1698     {
    1699         psString use_beginStr = psMetadataLookupStr(&status, config->args, "-set_use_begin");
    1700         if (!status) {
    1701             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_use_begin");
    1702             return false;
    1703         }
    1704         // pass through NULL as this is an optional field
    1705         if (use_beginStr) {
    1706             psFree(detRun->use_begin);
    1707             use_begin = psTimeFromISO(use_beginStr, PS_TIME_UTC);
    1708             detRun->use_begin = psMemIncrRefCounter(use_begin);
    1709             psFree(use_begin);
    1710         }
    1711     }
    1712 
    1713     psTime *use_end = NULL;
    1714     {
    1715         psString use_endStr = psMetadataLookupStr(&status, config->args, "-set_use_end");
    1716         if (!status) {
    1717             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_use_end");
    1718             return false;
    1719         }
    1720         // pass through NULL as this is an optional field
    1721         if (use_endStr) {
    1722             psFree(detRun->use_end);
    1723             use_end = psTimeFromISO(use_endStr, PS_TIME_UTC);
    1724             detRun->use_end = psMemIncrRefCounter(use_end);
    1725             psFree(use_end);
    1726         }
    1727     }
    1728 
    1729     psString reduction = psMetadataLookupStr(&status, config->args, "-set_reduction");
    1730     if (!status) {
    1731         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -set_reduction");
    1732         return false;
    1733     }
    1734     if (reduction) {
    1735         psFree(detRun->reduction);
    1736         detRun->reduction = psStringCopy(reduction);
    1737     }
    1738 
    1739     // create a metadata to restrict detInputExp's be in in the specified range
    1740 
    1741     psMetadata *time_filter = psMetadataAlloc();
    1742 
    1743     {
    1744         psString timeStr = psMetadataLookupStr(&status, config->args, "-filter_input_begin");
    1745         if (!status) {
    1746             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filter_input_begin");
    1747             return false;
    1748         }
    1749         // pass through NULL as this is an optional field
    1750         if (timeStr) {
    1751             psTime *time = psTimeFromISO(timeStr, PS_TIME_UTC);
    1752             if (!psMetadataAddTime(time_filter, PS_LIST_TAIL, "dateobs", PS_META_DUPLICATE_OK, ">=", time)) {
    1753                 psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
    1754                 psFree(time);
    1755                 psFree(time_filter);
    1756                 return false;
    1757             }
    1758             psFree(time);
    1759         }
    1760     }
    1761 
    1762     {
    1763         psString timeStr = psMetadataLookupStr(&status, config->args, "-filter_input_end");
    1764         if (!status) {
    1765             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filter_input_end");
    1766             return false;
    1767         }
    1768         // pass through NULL as this is an optional field
    1769         if (timeStr) {
    1770             psTime *time = psTimeFromISO(timeStr, PS_TIME_UTC);
    1771             if (!psMetadataAddTime(time_filter, PS_LIST_TAIL, "dateobs", PS_META_DUPLICATE_OK, "<", time)) {
    1772                 psError(PS_ERR_UNKNOWN, false, "failed to add item dateobs");
    1773                 psFree(time);
    1774                 psFree(time_filter);
    1775                 return false;
    1776             }
    1777             psFree(time);
    1778         }
    1779     }
    1780 
    1781 
    1782     // start a transaction so we don't end up with childlessed det_ids
    1783     if (!psDBTransaction(config->dbh)) {
    1784         psError(PS_ERR_UNKNOWN, false, "database error");
    1785         psFree(time_filter);
    1786         psFree(detRun);
    1787         return false;
    1788     }
    1789 
    1790     if (!detRunInsertObject(config->dbh, detRun)) {
    1791         psError(PS_ERR_UNKNOWN, false, "database error");
    1792         // rollback
    1793         if (!psDBRollback(config->dbh)) {
    1794             psError(PS_ERR_UNKNOWN, false, "database error");
    1795         }
    1796         psFree(time_filter);
    1797         psFree(detRun);
    1798         return false;
    1799     }
    1800     psFree(detRun);
    1801 
    1802     // get the det_id
    1803     psS64 newDet_id = psDBLastInsertID(config->dbh);
    1804 
    1805     psString query = psStringCopy(
    1806         "INSERT INTO detInputExp"
    1807         "   SELECT"
    1808         "       %d,"
    1809         "       0,"
    1810         "       detResidExp.exp_id,"
    1811         "       detResidExp.accept"
    1812         "   FROM detResidExp"
    1813         "   JOIN rawExp"
    1814         "       USING(exp_id)"
    1815         "   WHERE det_id = %d"
    1816     );
    1817 
    1818     if (time_filter->list->n) {
    1819         psString whereClause = psDBGenerateWhereConditionSQL(time_filter, "rawExp");
    1820         psStringAppend(&query, " AND %s", whereClause);
    1821         psFree(whereClause);
    1822     }
    1823     psFree(time_filter);
    1824 
    1825     if (!p_psDBRunQuery(config->dbh, query, (psS64)newDet_id, (psS64)atoll(det_id))) {
    1826         psError(PS_ERR_UNKNOWN, false, "database error");
    1827         psFree(query);
    1828         return false;
    1829     }
    1830     psFree(query);
    1831 
    1832     // point of no return for det_id creation
    1833     if (!psDBCommit(config->dbh)) {
    1834         psError(PS_ERR_UNKNOWN, false, "database error");
    1835         return false;
    1836     }
    1837 
    1838     bool simple = false;
    1839     {
    1840         bool status = false;
    1841         simple = psMetadataLookupBool(&status, config->args, "-simple");
    1842         if (!status) {
    1843             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    1844             return false;
    1845         }
    1846     }
    1847 
    1848     // print the new det_id
    1849     psArray *newDetRuns = NULL;
    1850     {
    1851         psMetadata *where = psMetadataAlloc();
    1852         psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", newDet_id);
    1853         newDetRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
    1854         psFree(where);
    1855     }
    1856     if (!newDetRuns) {
    1857         psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
    1858         return false;
    1859     }
    1860     // sanity check the result... we should have only found one det_id
    1861     if (psArrayLength(newDetRuns) != 1) {
    1862         psAbort("found more then one detRun matching det_id %" PRId64 " (this should not happen)", newDet_id);
    1863         return false;                   // unreachable
    1864     }
    1865 
    1866     // convert det_id to a string externaly
    1867     if (!convertIdToStr(detRuns)) {
    1868         psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
    1869         psFree(detRuns);
    1870         return false;
    1871     }
    1872 
    1873     // negative simple so the default is true
    1874     if (!ippdbPrintMetadatas(stdout, newDetRuns, "detRun", !simple)) {
    1875         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    1876         psFree(newDetRuns);
    1877         return false;
    1878     }
    1879     psFree(newDetRuns);
    1880 
    1881     return true;
    1882 }
    1883 
    1884 static bool runsMode(pxConfig *config)
    1885 {
    1886     PS_ASSERT_PTR_NON_NULL(config, false);
    1887 
    1888     // XXX fix the hard coding of the table name
    1889     psArray *runs = psDBSelectRows(config->dbh, "detRun", config->where, 0);
    1890     if (!runs) {
    1891         psError(PS_ERR_UNKNOWN, false, "database error");
    1892         return false;
    1893     }
    1894 
    1895     if (!psArrayLength(runs)) {
    1896         psTrace("dettool", PS_LOG_INFO, "no rows found");
    1897         psFree(runs);
    1898         return true;
    1899     }
    1900 
    1901     // convert det_id to a string externaly
    1902     if (!convertIdToStr(runs)) {
    1903         psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
    1904         psFree(runs);
    1905         return false;
    1906     }
    1907 
    1908     bool simple = false;
    1909     {
    1910         bool status = false;
    1911         simple = psMetadataLookupBool(&status, config->args, "-simple");
    1912         if (!status) {
    1913             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    1914             return false;
    1915         }
    1916     }
    1917 
    1918     // negative simple so the default is true
    1919     if (!ippdbPrintMetadatas(stdout, runs, "detRun", !simple)) {
    1920         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    1921         psFree(runs);
    1922         return false;
    1923     }
    1924 
    1925     psFree(runs);
    1926 
    1927     return true;
    1928 }
    1929 
    1930 static bool childlessrunMode(pxConfig *config)
    1931 {
    1932     PS_ASSERT_PTR_NON_NULL(config, false);
    1933 
    1934     bool status = false;
    1935     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    1936     if (!status) {
    1937         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    1938         return false;
    1939     }
    1940 
    1941     psString query = psStringCopy(
    1942             "SELECT DISTINCT\n"
    1943             "   detRun.*\n"
    1944             " FROM detRun\n"
    1945             " LEFT JOIN detRun as foo\n"
    1946             "   ON foo.parent = detRun.det_id\n"
    1947             " WHERE\n"
    1948             "   detRun.state = 'stop'\n"
    1949             "   AND detRun.mode = 'master'\n"
    1950             "   AND foo.det_id IS NULL\n"
    1951         );
    1952 
    1953     if (config->where) {
    1954         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detRun");
    1955         psStringAppend(&query, " AND %s", whereClause);
    1956         psFree(whereClause);
    1957     }
    1958 
    1959     // treat limit == 0 as "no limit"
    1960     if (limit) {
    1961         psString limitString = psDBGenerateLimitSQL(limit);
    1962         psStringAppend(&query, " %s", limitString);
    1963         psFree(limitString);
    1964     }
    1965 
    1966     if (!p_psDBRunQuery(config->dbh, query)) {
    1967         psError(PS_ERR_UNKNOWN, false, "database error");
    1968         psFree(query);
    1969         return false;
    1970     }
    1971     psFree(query);
    1972 
    1973     psArray *output = p_psDBFetchResult(config->dbh);
    1974     if (!output) {
    1975         psError(PS_ERR_UNKNOWN, false, "database error");
    1976         return false;
    1977     }
    1978     if (!psArrayLength(output)) {
    1979         psTrace("dettool", PS_LOG_INFO, "no rows found");
    1980         psFree(output);
    1981         return true;
    1982     }
    1983 
    1984     // convert det_id to a string externaly
    1985     if (!convertIdToStr(output)) {
    1986         psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
    1987         psFree(output);
    1988         return false;
    1989     }
    1990 
    1991     bool simple = false;
    1992     {
    1993         bool status = false;
    1994         simple = psMetadataLookupBool(&status, config->args, "-simple");
    1995         if (!status) {
    1996             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    1997             psFree(output);
    1998             return false;
    1999         }
    2000     }
    2001 
    2002     // negative simple so the default is true
    2003     if (!ippdbPrintMetadatas(stdout, output, "detRun", !simple)) {
    2004         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2005         psFree(output);
    2006         return false;
    2007     }
    2008 
    2009     psFree(output);
    2010 
    2011     return true;
    2012 }
    2013 
    2014 static detInputExpRow *rawDetrenTodetInputExpRow(rawExpRow *rawExp, psS64 det_id, psS32 iteration)
    2015 {
    2016     PS_ASSERT_PTR_NON_NULL(rawExp, NULL);
    2017 
    2018     return detInputExpRowAlloc(
    2019         det_id,
    2020         iteration,
    2021         rawExp->exp_id,
    2022         true            // use
    2023     );
    2024 }
    2025 
    2026 static bool inputMode(pxConfig *config)
    2027 {
    2028     PS_ASSERT_PTR_NON_NULL(config, false);
    2029 
    2030     // select detInputExp.*
    2031     // select rawExp.*
    2032     // by:
    2033     // exp_id
    2034 
    2035     psString query = psStringCopy(
    2036         "SELECT DISTINCT *"
    2037         " FROM detInputExp"
    2038         " JOIN rawExp"
    2039         " USING(exp_id)"
    2040         );
    2041 
    2042     if (config->where) {
    2043         psString whereClause = psDBGenerateWhereSQL(config->where, "detInputExp");
    2044         psStringAppend(&query, " %s", whereClause);
    2045         psFree(whereClause);
    2046     }
    2047 
    2048     if (!p_psDBRunQuery(config->dbh, query)) {
    2049         psError(PS_ERR_UNKNOWN, false, "database error");
    2050         psFree(query);
    2051         return false;
    2052     }
    2053     psFree(query);
    2054 
    2055     psArray *output = p_psDBFetchResult(config->dbh);
    2056     if (!output) {
    2057         psError(PS_ERR_UNKNOWN, false, "database error");
    2058         return false;
    2059     }
    2060     if (!psArrayLength(output)) {
    2061         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2062         psFree(output);
    2063         return true;
    2064     }
    2065 
    2066     bool simple = false;
    2067     {
    2068         bool status = false;
    2069         simple = psMetadataLookupBool(&status, config->args, "-simple");
    2070         if (!status) {
    2071             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2072             return false;
    2073         }
    2074     }
    2075 
    2076     // negative simple so the default is true
    2077     if (!ippdbPrintMetadatas(stdout, output, "detInputExp", !simple)) {
    2078         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2079         psFree(output);
    2080         return false;
    2081     }
    2082 
    2083     psFree(output);
    2084 
    2085     return true;
    2086 }
    2087 
    2088 static bool rawMode(pxConfig *config)
    2089 {
    2090     PS_ASSERT_PTR_NON_NULL(config, false);
    2091 
    2092     psString query = pxDataGet("dettool_raw.sql");
    2093     if (!query) {
    2094         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    2095         return false;
    2096     }
    2097 
    2098     if (config->where) {
    2099         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "rawImfile");
    2100         psStringAppend(&query, " AND %s", whereClause);
    2101         psFree(whereClause);
    2102     }
    2103 
    2104     if (!p_psDBRunQuery(config->dbh, query)) {
    2105         psError(PS_ERR_UNKNOWN, false, "database error");
    2106         psFree(query);
    2107         return false;
    2108     }
    2109     psFree(query);
    2110 
    2111     psArray *output = p_psDBFetchResult(config->dbh);
    2112     if (!output) {
    2113         psError(PS_ERR_UNKNOWN, false, "database error");
    2114         return false;
    2115     }
    2116     if (!psArrayLength(output)) {
    2117         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2118         psFree(output);
    2119         return true;
    2120     }
    2121 
    2122     bool simple = false;
    2123     {
    2124         bool status = false;
    2125         simple = psMetadataLookupBool(&status, config->args, "-simple");
    2126         if (!status) {
    2127             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2128             return false;
    2129         }
    2130     }
    2131 
    2132     // negative simple so the default is true
    2133     if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
    2134         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2135         psFree(output);
    2136         return false;
    2137     }
    2138 
    2139     psFree(output);
    2140 
    2141     return true;
    2142 }
    2143 
    2144 static bool toprocessedimfileMode(pxConfig *config)
    2145 {
    2146     PS_ASSERT_PTR_NON_NULL(config, false);
    2147 
    2148     bool status = false;
    2149     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    2150     if (!status) {
    2151         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    2152         return false;
    2153     }
    2154 
    2155     psString query = pxDataGet("dettool_toprocessedimfile.sql");
    2156     if (!query) {
    2157         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    2158         return false;
    2159     }
    2160 
    2161     if (config->where) {
    2162         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "rawImfile");
    2163         psStringAppend(&query, " AND %s", whereClause);
    2164         psFree(whereClause);
    2165     }
    2166 
    2167     // treat limit == 0 as "no limit"
    2168     if (limit) {
    2169         psString limitString = psDBGenerateLimitSQL(limit);
    2170         psStringAppend(&query, " %s", limitString);
    2171         psFree(limitString);
    2172     }
    2173 
    2174     if (!p_psDBRunQuery(config->dbh, query)) {
    2175         psError(PS_ERR_UNKNOWN, false, "database error");
    2176         psFree(query);
    2177         return false;
    2178     }
    2179     psFree(query);
    2180 
    2181     psArray *output = p_psDBFetchResult(config->dbh);
    2182     if (!output) {
    2183         psError(PS_ERR_UNKNOWN, false, "database error");
    2184         return false;
    2185     }
    2186     if (!psArrayLength(output)) {
    2187         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2188         psFree(output);
    2189         return true;
    2190     }
    2191 
    2192     bool simple = false;
    2193     {
    2194         bool status = false;
    2195         simple = psMetadataLookupBool(&status, config->args, "-simple");
    2196         if (!status) {
    2197             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2198             return false;
    2199         }
    2200     }
    2201 
    2202     // negative simple so the default is true
    2203     if (!ippdbPrintMetadatas(stdout, output, "detPendingProcessedImfile", !simple)) {
    2204         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2205         psFree(output);
    2206         return false;
    2207     }
    2208 
    2209     psFree(output);
    2210 
    2211     return true;
    2212 }
    2213 
    2214 static psArray *searchRawImfiles(pxConfig *config, psMetadata *where)
    2215 {
    2216     PS_ASSERT_PTR_NON_NULL(config, NULL);
    2217 
    2218     // use the default where if "where" is NULL
    2219     if (!where) {
    2220         where = config->where;
    2221     }
    2222 
    2223     // select exp_ids from detInputExp matching det_idp
    2224     // where query should be pre-generated
    2225     psArray *detInputExp =
    2226         detInputExpSelectRowObjects(config->dbh, where, 0);
    2227     if (!detInputExp) {
    2228         psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
    2229         return NULL;
    2230     }
    2231 
    2232     // generate where query with just the exp_ids
    2233     psMetadata *where_exp_ids = psMetadataAlloc();
    2234     for (long i = 0; i < psArrayLength(detInputExp); i++) {
    2235         detInputExpRow *row = detInputExp->data[i];
    2236         if (!psMetadataAddS64(where_exp_ids, PS_LIST_TAIL, "exp_id",
    2237                 PS_META_DUPLICATE_OK, "==", row->exp_id)
    2238         ) {
    2239             psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    2240             psFree(detInputExp);
    2241             psFree(where_exp_ids);
    2242             return NULL;
    2243         }
    2244     }
    2245     psFree(detInputExp);
    2246 
    2247     // select rawImfiles with matching exp_ids
    2248     psArray *rawImfiles =
    2249         rawImfileSelectRowObjects(config->dbh, where_exp_ids, 0);
    2250     psFree(where_exp_ids);
    2251     if (!rawImfiles) {
    2252         psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found");
    2253         return NULL;
    2254     }
    2255 
    2256     return rawImfiles;
    2257 }
    2258 
    2259 static bool addprocessedimfileMode(pxConfig *config)
    2260 {
    2261     PS_ASSERT_PTR_NON_NULL(config, false);
    2262 
    2263     // det_id, exp_id, class_id, uri, recipe, -bg, -bg_stdev
    2264     // are required
    2265     bool status = false;
    2266     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    2267     if (!status) {
    2268         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    2269         return false;
    2270     }
    2271     if (!det_id) {
    2272         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    2273         return false;
    2274     }
    2275     psString exp_id = psMetadataLookupStr(&status, config->args, "-exp_id");
    2276     if (!status) {
    2277         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_id");
    2278         return false;
    2279     }
    2280     if (!exp_id) {
    2281         psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
    2282         return false;
    2283     }
    2284     psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
    2285     if (!status) {
    2286         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
    2287         return false;
    2288     }
    2289     if (!class_id) {
    2290         psError(PS_ERR_UNKNOWN, true, "-class_id is required");
    2291         return false;
    2292     }
    2293     psString uri    = psMetadataLookupStr(&status, config->args, "-uri");
    2294     if (!status) {
    2295         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
    2296         return false;
    2297     }
    2298     if (!uri) {
    2299         psError(PS_ERR_UNKNOWN, true, "-uri is required");
    2300         return false;
    2301     }
    2302     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    2303     if (!status) {
    2304         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    2305         return false;
    2306     }
    2307     if (!recipe) {
    2308         psError(PS_ERR_UNKNOWN, true, "-recip is required");
    2309         return false;
    2310     }
    2311     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    2312     if (!status) {
    2313         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    2314         return false;
    2315     }
    2316     //if (isnan(bg)) {
    2317     //psError(PS_ERR_UNKNOWN, true, "-bg is required");
    2318     //return false;
    2319     //}
    2320     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    2321     if (!status) {
    2322         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    2323         return false;
    2324     }
    2325     //if (isnan(bg_stdev)) {
    2326     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    2327     //  return false;
    2328     //}
    2329     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    2330     if (!status) {
    2331         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    2332         return false;
    2333     }
    2334 
    2335     // optional
    2336     psF64 fringe_0 = psMetadataLookupF64(&status, config->args, "-fringe_0");
    2337     if (!status) {
    2338         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_0");
    2339         return false;
    2340     }
    2341     psF64 fringe_1 = psMetadataLookupF64(&status, config->args, "-fringe_1");
    2342     if (!status) {
    2343         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_1");
    2344         return false;
    2345     }
    2346     psF64 fringe_2 = psMetadataLookupF64(&status, config->args, "-fringe_2");
    2347     if (!status) {
    2348         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_2");
    2349         return false;
    2350     }
    2351 
    2352     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    2353     if (!status) {
    2354         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    2355         return false;
    2356     }
    2357     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    2358     if (!status) {
    2359         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    2360         return false;
    2361     }
    2362     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    2363     if (!status) {
    2364         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    2365         return false;
    2366     }
    2367     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    2368     if (!status) {
    2369         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    2370         return false;
    2371     }
    2372     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    2373     if (!status) {
    2374         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    2375         return false;
    2376     }
    2377 
    2378     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    2379     if (!status) {
    2380         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    2381         return false;
    2382     }
    2383 
    2384     // find the matching rawImfile by exp_id/class_id
    2385     psMetadata *where = psMetadataAlloc();
    2386     if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id", 0, "==", exp_id)) {
    2387         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    2388         psFree(where);
    2389         return false;
    2390     }
    2391     if (!psMetadataAddStr(where, PS_LIST_TAIL, "class_id", 0, "==", class_id)) {
    2392         psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
    2393         psFree(where);
    2394         return false;
    2395     }
    2396 
    2397     // default values
    2398     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    2399     if (!status) {
    2400         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    2401         return false;
    2402     }
    2403 
    2404     psArray *rawImfiles = rawImfileSelectRowObjects(config->dbh, where, 0);
    2405     psFree(where);
    2406     if (!rawImfiles) {
    2407         psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found ");
    2408         return false;
    2409     }
    2410 
    2411 
    2412     // create a new detProcessedImfile object
    2413     detProcessedImfileRow *detRow = detProcessedImfileRowAlloc(
    2414         (psS64)atoll(det_id),
    2415         (psS64)atoll(exp_id),
    2416         class_id,
    2417         uri,
    2418         recipe,
    2419         bg,
    2420         bg_stdev,
    2421         bg_mean_stdev,
    2422         fringe_0,
    2423         fringe_1,
    2424         fringe_2,
    2425         user_1,
    2426         user_2,
    2427         user_3,
    2428         user_4,
    2429         user_5,
    2430         path_base,
    2431         code
    2432     );
    2433     psFree(rawImfiles);
    2434 
    2435     // insert the new row into the detProcessedImfile table
    2436     if (!detProcessedImfileInsertObject(config->dbh, detRow)) {
    2437         psError(PS_ERR_UNKNOWN, false, "database error");
    2438         psFree(detRow);
    2439         return false;
    2440     }
    2441 
    2442     psFree(detRow);
    2443 
    2444     return true;
    2445 }
    2446 
    2447 static bool toprocessedexpMode(pxConfig *config)
    2448 {
    2449     PS_ASSERT_PTR_NON_NULL(config, false);
    2450 
    2451     bool status = false;
    2452     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    2453     if (!status) {
    2454         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    2455         return false;
    2456     }
    2457 
    2458     psString query = pxDataGet("dettool_toprocessedexp.sql");
    2459     if (!query) {
    2460         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    2461         return false;
    2462     }
    2463 
    2464     // XXX does it make sense to accept any search params?
    2465 #if 0
    2466     if (config->where) {
    2467         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    2468         psStringAppend(&query, " AND %s", whereClause);
    2469         psFree(whereClause);
    2470     }
    2471 #endif
    2472 
    2473     // treat limit == 0 as "no limit"
    2474     if (limit) {
    2475         psString limitString = psDBGenerateLimitSQL(limit);
    2476         psStringAppend(&query, " %s", limitString);
    2477         psFree(limitString);
    2478     }
    2479 
    2480     if (!p_psDBRunQuery(config->dbh, query)) {
    2481         psError(PS_ERR_UNKNOWN, false, "database error");
    2482         psFree(query);
    2483         return false;
    2484     }
    2485     psFree(query);
    2486 
    2487     psArray *output = p_psDBFetchResult(config->dbh);
    2488     if (!output) {
    2489         psError(PS_ERR_UNKNOWN, false, "database error");
    2490         return false;
    2491     }
    2492     if (!psArrayLength(output)) {
    2493         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2494         psFree(output);
    2495         return true;
    2496     }
    2497 
    2498     bool simple = false;
    2499     {
    2500         bool status = false;
    2501         simple = psMetadataLookupBool(&status, config->args, "-simple");
    2502         if (!status) {
    2503             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2504             return false;
    2505         }
    2506     }
    2507 
    2508     // negative simple so the default is true
    2509     if (!ippdbPrintMetadatas(stdout, output, "detPendingProcessedExp", !simple)) {
    2510         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2511         psFree(output);
    2512         return false;
    2513     }
    2514 
    2515     psFree(output);
    2516 
    2517     return true;
    2518 }
    2519 
    2520 static bool addprocessedexpMode(pxConfig *config)
    2521 {
    2522     PS_ASSERT_PTR_NON_NULL(config, false);
    2523 
    2524     // det_id, exp_id, recip, -bg, -bg_stdev
    2525     // are required
    2526     bool status = false;
    2527     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    2528     if (!status) {
    2529         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    2530         return false;
    2531     }
    2532     if (!det_id) {
    2533         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    2534         return false;
    2535     }
    2536     psString exp_id = psMetadataLookupStr(&status, config->args, "-exp_id");
    2537     if (!status) {
    2538         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_id");
    2539         return false;
    2540     }
    2541     if (!exp_id) {
    2542         psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
    2543         return false;
    2544     }
    2545     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    2546     if (!status) {
    2547         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    2548         return false;
    2549     }
    2550     if (!recipe) {
    2551         psError(PS_ERR_UNKNOWN, true, "-recip is required");
    2552         return false;
    2553     }
    2554     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    2555     if (!status) {
    2556         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    2557         return false;
    2558     }
    2559     //if (isnan(bg)) {
    2560     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    2561     //  return false;
    2562     //}
    2563     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    2564     if (!status) {
    2565         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    2566         return false;
    2567     }
    2568     //if (isnan(bg_stdev)) {
    2569     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    2570     //  return false;
    2571     //}
    2572     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    2573     if (!status) {
    2574         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    2575         return false;
    2576     }
    2577     // optional
    2578     psF64 fringe_0 = psMetadataLookupF64(&status, config->args, "-fringe_0");
    2579     if (!status) {
    2580         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_0");
    2581         return false;
    2582     }
    2583     psF64 fringe_1 = psMetadataLookupF64(&status, config->args, "-fringe_1");
    2584     if (!status) {
    2585         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_1");
    2586         return false;
    2587     }
    2588     psF64 fringe_2 = psMetadataLookupF64(&status, config->args, "-fringe_2");
    2589     if (!status) {
    2590         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_2");
    2591         return false;
    2592     }
    2593 
    2594     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    2595     if (!status) {
    2596         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    2597         return false;
    2598     }
    2599     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    2600     if (!status) {
    2601         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    2602         return false;
    2603     }
    2604     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    2605     if (!status) {
    2606         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    2607         return false;
    2608     }
    2609     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    2610     if (!status) {
    2611         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    2612         return false;
    2613     }
    2614     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    2615     if (!status) {
    2616         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    2617         return false;
    2618     }
    2619 
    2620     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    2621     if (!status) {
    2622         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    2623         return false;
    2624     }
    2625 
    2626     // default values
    2627     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    2628     if (!status) {
    2629         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    2630         return false;
    2631     }
    2632 
    2633     psString query = psStringCopy(
    2634         " SELECT DISTINCT"
    2635         "    detProcessedImfile.det_id,"
    2636         "    detRun.iteration,"
    2637         "    detRun.det_type,"
    2638         "    detProcessedImfile.exp_id"
    2639         " FROM detRun"
    2640         " JOIN detInputExp"
    2641         "    ON detRun.det_id = detInputExp.det_id"
    2642         "    AND detRun.iteration = detInputExp.iteration"
    2643         " JOIN rawExp"
    2644         "    ON detInputExp.exp_id = rawExp.exp_id"
    2645         " JOIN detProcessedImfile"
    2646         "    ON detInputExp.det_id = detProcessedImfile.det_id"
    2647         "    AND detInputExp.exp_id = detProcessedImfile.exp_id"
    2648         " LEFT JOIN detProcessedExp"
    2649         "    ON detInputExp.det_id = detProcessedExp.det_id"
    2650         "    AND detProcessedImfile.exp_id= detProcessedExp.exp_id"
    2651         " LEFT JOIN rawImfile"
    2652         "    ON detInputExp.exp_id = rawImfile.exp_id"
    2653         "    AND detProcessedImfile.class_id = rawImfile.class_id"
    2654         " WHERE"
    2655         "   detRun.state = 'run'"
    2656         "   AND detRun.mode = 'master'"
    2657         "   AND detProcessedExp.det_id IS NULL"
    2658         "   AND detProcessedExp.exp_id IS NULL"
    2659         "   AND detInputExp.include = 1"
    2660         "   AND detRun.det_id = %s"
    2661         "   AND detProcessedImfile.exp_id = '%s'"
    2662         " GROUP BY"
    2663         "    detProcessedImfile.class_id,"
    2664         "    rawImfile.class_id,"
    2665         "    detRun.det_id"
    2666         " HAVING"
    2667         "    COUNT(detProcessedImfile.class_id) = COUNT(rawImfile.class_id)"
    2668         );
    2669 
    2670     if (!p_psDBRunQuery(config->dbh, query, det_id, exp_id)) {
    2671         psError(PS_ERR_UNKNOWN, false, "database error");
    2672         psFree(query);
    2673         return false;
    2674     }
    2675     psFree(query);
    2676 
    2677     psArray *output = p_psDBFetchResult(config->dbh);
    2678     if (!output) {
    2679         psError(PS_ERR_UNKNOWN, false, "database error");
    2680         return false;
    2681     }
    2682     if (!psArrayLength(output)) {
    2683         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2684         psFree(output);
    2685         return true;
    2686     }
    2687     psFree(output);
    2688 
    2689     // create a new detProcessedImfile object
    2690     detProcessedExpRow *detRow = detProcessedExpRowAlloc(
    2691         (psS64)atoll(det_id),
    2692         (psS64)atoll(exp_id),
    2693         recipe,
    2694         bg,
    2695         bg_stdev,
    2696         bg_mean_stdev,
    2697         fringe_0,
    2698         fringe_1,
    2699         fringe_2,
    2700         user_1,
    2701         user_2,
    2702         user_3,
    2703         user_4,
    2704         user_5,
    2705         path_base,
    2706         code
    2707     );
    2708 
    2709     // insert the new row into the detProcessedImfile table
    2710     if (!detProcessedExpInsertObject(config->dbh, detRow)) {
    2711         psError(PS_ERR_UNKNOWN, false, "database error");
    2712         psFree(detRow);
    2713         return false;
    2714     }
    2715 
    2716     psFree(detRow);
    2717 
    2718     return true;
    2719 }
    2720 
    2721 static bool processedexpMode(pxConfig *config)
    2722 {
    2723     PS_ASSERT_PTR_NON_NULL(config, false);
    2724 
    2725     bool status = false;
    2726     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    2727     if (!status) {
    2728         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    2729         return false;
    2730     }
    2731 
    2732     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    2733     if (!status) {
    2734         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    2735         return false;
    2736     }
    2737 
    2738     psString query = psStringCopy("SELECT * FROM detProcessedExp");
    2739 
    2740     if (config->where) {
    2741         psString whereClause = psDBGenerateWhereSQL(config->where, NULL);
    2742         psStringAppend(&query, " %s", whereClause);
    2743         psFree(whereClause);
    2744     }
    2745 
    2746     if (faulted) {
    2747         // list only faulted rows
    2748         psStringAppend(&query, " %s", "AND detProcessedExp.fault != 0");
    2749     } else {
    2750         // don't list faulted rows
    2751         psStringAppend(&query, " %s", "AND detProcessedExp.fault = 0");
    2752     }
    2753 
    2754     // treat limit == 0 as "no limit"
    2755     if (limit) {
    2756         psString limitString = psDBGenerateLimitSQL(limit);
    2757         psStringAppend(&query, " %s", limitString);
    2758         psFree(limitString);
    2759     }
    2760 
    2761     if (!p_psDBRunQuery(config->dbh, query)) {
    2762         psError(PS_ERR_UNKNOWN, false, "database error");
    2763         psFree(query);
    2764         return false;
    2765     }
    2766     psFree(query);
    2767 
    2768     psArray *output = p_psDBFetchResult(config->dbh);
    2769     if (!output) {
    2770         psError(PS_ERR_UNKNOWN, false, "database error");
    2771         return false;
    2772     }
    2773     if (!psArrayLength(output)) {
    2774         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2775         psFree(output);
    2776         return true;
    2777     }
    2778 
    2779     bool simple = false;
    2780     {
    2781         bool status = false;
    2782         simple = psMetadataLookupBool(&status, config->args, "-simple");
    2783         if (!status) {
    2784             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2785             return false;
    2786         }
    2787     }
    2788 
    2789     // negative simple so the default is true
    2790     if (!ippdbPrintMetadatas(stdout, output, "detProcessedExp", !simple)) {
    2791         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2792         psFree(output);
    2793         return false;
    2794     }
    2795 
    2796     psFree(output);
    2797 
    2798     return true;
    2799 }
    2800 
    2801 
    2802 static bool revertprocessedexpMode(pxConfig *config)
    2803 {
    2804     PS_ASSERT_PTR_NON_NULL(config, false);
    2805 
    2806     psString query = pxDataGet("dettool_revertprocessedexp.sql");
    2807     if (!query) {
    2808         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    2809         return false;
    2810     }
    2811 
    2812     if (config->where) {
    2813         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detProcessedExp");
    2814         psStringAppend(&query, " AND %s", whereClause);
    2815         psFree(whereClause);
    2816     }
    2817 
    2818     if (!p_psDBRunQuery(config->dbh, query)) {
    2819         psError(PS_ERR_UNKNOWN, false, "database error");
    2820         psFree(query);
    2821         return false;
    2822     }
    2823     psFree(query);
    2824 
    2825     if (psDBAffectedRows(config->dbh) < 1) {
    2826         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    2827         return false;
    2828     }
    2829 
    2830     return true;
    2831 }
    2832 
    2833 
    2834 static bool tostackedMode(pxConfig *config)
    2835 {
    2836     PS_ASSERT_PTR_NON_NULL(config, false);
    2837 
    2838     bool status = false;
    2839     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    2840     if (!status) {
    2841         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    2842         return false;
    2843     }
    2844 
    2845     psString query = pxDataGet("dettool_tostacked.sql");
    2846     if (!query) {
    2847         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    2848         return false;
    2849     }
    2850 
    2851     // XXX does it make sense to accept any search params?
    2852 #if 0
    2853     if (config->where) {
    2854         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    2855         psStringAppend(&query, " AND %s", whereClause);
    2856         psFree(whereClause);
    2857     }
    2858 #endif
    2859 
    2860     // treat limit == 0 as "no limit"
    2861     if (limit) {
    2862         psString limitString = psDBGenerateLimitSQL(limit);
    2863         psStringAppend(&query, " %s", limitString);
    2864         psFree(limitString);
    2865     }
    2866 
    2867     if (!p_psDBRunQuery(config->dbh, query)) {
    2868         psError(PS_ERR_UNKNOWN, false, "database error");
    2869         psFree(query);
    2870         return false;
    2871     }
    2872     psFree(query);
    2873 
    2874     psArray *output = p_psDBFetchResult(config->dbh);
    2875     if (!output) {
    2876         psError(PS_ERR_UNKNOWN, false, "database error");
    2877         return false;
    2878     }
    2879     if (!psArrayLength(output)) {
    2880         psTrace("dettool", PS_LOG_INFO, "no rows found");
    2881         psFree(output);
    2882         return true;
    2883     }
    2884 
    2885     bool simple = false;
    2886     {
    2887         bool status = false;
    2888         simple = psMetadataLookupBool(&status, config->args, "-simple");
    2889         if (!status) {
    2890             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2891             return false;
    2892         }
    2893     }
    2894 
    2895     // negative simple so the default is true
    2896     if (!ippdbPrintMetadatas(stdout, output, "detPendingStackedImfile", !simple)) {
    2897         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    2898         psFree(output);
    2899         return false;
    2900     }
    2901 
    2902     psFree(output);
    2903 
    2904     return true;
    2905 }
    2906 
    2907 static bool processedimfileMode(pxConfig *config)
    2908 {
    2909     PS_ASSERT_PTR_NON_NULL(config, false);
    2910 
    2911     char *value = NULL;
    2912     bool status = false;
    2913 
    2914     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    2915     if (!status) {
    2916         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    2917         return false;
    2918     }
    2919 
    2920     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    2921     if (!status) {
    2922         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    2923         return false;
    2924     }
    2925 
    2926     psString query = psStringCopy(
    2927         " SELECT DISTINCT"
    2928         "   detRun.det_type,"
    2929         "   rawExp.exp_time,"
    2930         "   detProcessedImfile.*"
    2931         " FROM detProcessedImfile"
    2932         " JOIN detRun"
    2933         "   USING(det_id)"
    2934         " JOIN detInputExp"
    2935         "   ON detRun.det_id = detInputExp.det_id"
    2936         "   AND detRun.iteration = detInputExp.iteration"
    2937         "   AND detProcessedImfile.exp_id = detInputExp.exp_id"
    2938         " JOIN rawExp"
    2939         "   ON rawExp.exp_id = detProcessedImfile.exp_id"
    2940         " WHERE"
    2941     );
    2942     // NOTE the above WHERE is completed with the following line:
    2943 
    2944     // add the two required restrictions: detRun.state and detRun.mode
    2945     if ((value = psMetadataLookupStr(&status, config->args, "-select_state"))) {
    2946         psStringAppend(&query, " detRun.state = '%s'", value);
    2947     } else {
    2948         psStringAppend(&query, " detRun.state = 'run'");
    2949     }
    2950     if ((value = psMetadataLookupStr(&status, config->args, "-select_mode"))) {
    2951         psStringAppend(&query, " AND detRun.mode = '%s'", value);
    2952     } else {
    2953         psStringAppend(&query, " AND detRun.mode = 'master'");
    2954     }
    2955 
    2956     if (config->where) {
    2957         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detProcessedImfile");
    2958         psStringAppend(&query, " AND %s", whereClause);
    2959         psFree(whereClause);
    2960     }
    2961 
    2962     {
    2963         bool status = false;
    2964         bool included = psMetadataLookupBool(&status, config->args, "-included");
    2965         if (!status) {
    2966             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    2967             return false;
    2968         }
    2969         // restrict search to included imfiles
    2970         if (included) {
    2971             psStringAppend(&query, " AND detInputExp.include = 1");
    2972         }
    2973     }
    2974 
    2975     if (faulted) {
    2976         // list only faulted rows
    2977         psStringAppend(&query, " %s", "AND detProcessedImfile.fault != 0");
    2978     } else {
    2979         // don't list faulted rows
    2980         psStringAppend(&query, " %s", "AND detProcessedImfile.fault = 0");
    2981     }
    2982 
    2983     // treat limit == 0 as "no limit"
    2984     if (limit) {
    2985         psString limitString = psDBGenerateLimitSQL(limit);
    2986         psStringAppend(&query, " %s", limitString);
    2987         psFree(limitString);
    2988     }
    2989 
    2990     if (!p_psDBRunQuery(config->dbh, query)) {
    2991         psError(PS_ERR_UNKNOWN, false, "database error");
    2992         psFree(query);
    2993         return false;
    2994     }
    2995     psFree(query);
    2996 
    2997     psArray *output = p_psDBFetchResult(config->dbh);
    2998     if (!output) {
    2999         psError(PS_ERR_UNKNOWN, false, "database error");
    3000         return false;
    3001     }
    3002     if (!psArrayLength(output)) {
    3003         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3004         psFree(output);
    3005         return true;
    3006     }
    3007 
    3008     bool simple = false;
    3009     {
    3010         bool status = false;
    3011         simple = psMetadataLookupBool(&status, config->args, "-simple");
    3012         if (!status) {
    3013             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    3014             return false;
    3015         }
    3016     }
    3017 
    3018     // negative simple so the default is true
    3019     if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
    3020         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    3021         psFree(output);
    3022         return false;
    3023     }
    3024 
    3025     psFree(output);
    3026 
    3027     return true;
    3028 }
    3029 
    3030 
    3031 static bool revertprocessedimfileMode(pxConfig *config)
    3032 {
    3033     PS_ASSERT_PTR_NON_NULL(config, false);
    3034 
    3035     psString query = pxDataGet("dettool_revertprocessedimfile.sql");
    3036     if (!query) {
    3037         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    3038         return false;
    3039     }
    3040 
    3041     if (config->where) {
    3042         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detProcessedImfile");
    3043         psStringAppend(&query, " AND %s", whereClause);
    3044         psFree(whereClause);
    3045     }
    3046 
    3047     if (!p_psDBRunQuery(config->dbh, query)) {
    3048         psError(PS_ERR_UNKNOWN, false, "database error");
    3049         psFree(query);
    3050         return false;
    3051     }
    3052     psFree(query);
    3053 
    3054     if (psDBAffectedRows(config->dbh) < 1) {
    3055         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    3056         return false;
    3057     }
    3058 
    3059     return true;
    3060 }
    3061 
    3062 
    3063 static bool addstackedMode(pxConfig *config)
    3064 {
    3065     PS_ASSERT_PTR_NON_NULL(config, false);
    3066 
    3067     // det_id, class_id, uri, & recipe are required
    3068     bool status = false;
    3069     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    3070     if (!status) {
    3071         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    3072         return false;
    3073     }
    3074     if (!det_id) {
    3075         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    3076         return false;
    3077     }
    3078     psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
    3079     if (!status) {
    3080         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
    3081         return false;
    3082     }
    3083     if (!class_id) {
    3084         psError(PS_ERR_UNKNOWN, true, "-class_id is required");
    3085         return false;
    3086     }
    3087     psString uri    = psMetadataLookupStr(&status, config->args, "-uri");
    3088     if (!status) {
    3089         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
    3090         return false;
    3091     }
    3092     if (!uri) {
    3093         psError(PS_ERR_UNKNOWN, true, "-uri is required");
    3094         return false;
    3095     }
    3096     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    3097     if (!status) {
    3098         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    3099         return false;
    3100     }
    3101     if (!recipe) {
    3102         psError(PS_ERR_UNKNOWN, true, "-recip is required");
    3103         return false;
    3104     }
    3105     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    3106     if (!status) {
    3107         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    3108         return false;
    3109     }
    3110     //if (isnan(bg)) {
    3111     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    3112     //  return false;
    3113     //}
    3114     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    3115     if (!status) {
    3116         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    3117         return false;
    3118     }
    3119     //if (isnan(bg_stdev)) {
    3120     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    3121     //  return false;
    3122     //}
    3123     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    3124     if (!status) {
    3125         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    3126         return false;
    3127     }
    3128 
    3129     // optional values
    3130     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    3131     if (!status) {
    3132         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    3133         return false;
    3134     }
    3135     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    3136     if (!status) {
    3137         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    3138         return false;
    3139     }
    3140     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    3141     if (!status) {
    3142         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    3143         return false;
    3144     }
    3145     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    3146     if (!status) {
    3147         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    3148         return false;
    3149     }
    3150     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    3151     if (!status) {
    3152         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    3153         return false;
    3154     }
    3155 
    3156     // default values
    3157     psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    3158     if (!status) {
    3159         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");
    3160         return false;
    3161     }
    3162 
    3163     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    3164     if (!status) {
    3165         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    3166         return false;
    3167     }
    3168 
    3169     // correlate the class_id against the input exposure(s)
    3170 
    3171     // we have to generate our own where clause as we want to search only by the
    3172     // det_id
    3173     psMetadata *where = psMetadataAlloc();
    3174     if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==",
    3175             (psS64)atoll(det_id))) {
    3176         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    3177         psFree(where);
    3178         return false;
    3179     }
    3180 
    3181     psArray *rawImfiles = searchRawImfiles(config, where);
    3182     psFree(where);
    3183 
    3184     bool valid_class_id = false;
    3185     if (rawImfiles) {
    3186         for (long i = 0; i < psArrayLength(rawImfiles); i++) {
    3187             if (strcmp(class_id, ((rawImfileRow *)rawImfiles->data[i])->class_id) == 0) {
    3188                 valid_class_id = true;
    3189                 break;
    3190             }
    3191         }
    3192         psFree(rawImfiles);
    3193     }
    3194 
    3195     if (!valid_class_id) {
    3196         psError(PS_ERR_UNKNOWN, true,
    3197             "class_id can not be correlated with the input exposures");
    3198         return false;
    3199     }
    3200 
    3201     // create a new detStackedImfile object
    3202     detStackedImfileRow *stackedImfile = detStackedImfileRowAlloc(
    3203             (psS64)atoll(det_id),
    3204             iteration,
    3205             class_id,
    3206             uri,
    3207             recipe,
    3208             bg,
    3209             bg_stdev,
    3210             bg_mean_stdev,
    3211             user_1,
    3212             user_2,
    3213             user_3,
    3214             user_4,
    3215             user_5,
    3216             code
    3217         );
    3218 
    3219     // insert the new row into the detProcessedImfile table
    3220     if (!detStackedImfileInsertObject(config->dbh, stackedImfile)) {
    3221         psError(PS_ERR_UNKNOWN, false, "database error");
    3222         psFree(stackedImfile);
    3223         return false;
    3224     }
    3225 
    3226     psFree(stackedImfile);
    3227 
    3228     return true;
    3229 }
    3230 
    3231 static bool stackedMode(pxConfig *config)
    3232 {
    3233     PS_ASSERT_PTR_NON_NULL(config, false);
    3234 
    3235     bool status = false;
    3236     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    3237     if (!status) {
    3238         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    3239         return false;
    3240     }
    3241 
    3242     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    3243     if (!status) {
    3244         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    3245         return false;
    3246     }
    3247 
    3248     // select detStackedImfile.*
    3249     // by:
    3250     // where det_id, iteration, class_id is not in detNormalizedImfile
    3251 
    3252     psString query = psStringCopy(
    3253         "SELECT"
    3254         "   detStackedImfile.*"
    3255         " FROM detStackedImfile"
    3256         " JOIN detRun"
    3257         "   USING(det_id, iteration)"
    3258         " WHERE"
    3259         "   detRun.state = 'run'"
    3260         "   AND detRun.mode = 'master'"
    3261     );
    3262 
    3263     if (config->where) {
    3264         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detStackedImfile");
    3265         psStringAppend(&query, " AND %s", whereClause);
    3266         psFree(whereClause);
    3267     }
    3268 
    3269     if (faulted) {
    3270         // list only faulted rows
    3271         psStringAppend(&query, " %s", "AND detStackedImfile.fault != 0");
    3272     } else {
    3273         // don't list faulted rows
    3274         psStringAppend(&query, " %s", "AND detStackedImfile.fault = 0");
    3275     }
    3276 
    3277     // treat limit == 0 as "no limit"
    3278     if (limit) {
    3279         psString limitString = psDBGenerateLimitSQL(limit);
    3280         psStringAppend(&query, " %s", limitString);
    3281         psFree(limitString);
    3282     }
    3283 
    3284     if (!p_psDBRunQuery(config->dbh, query)) {
    3285         psError(PS_ERR_UNKNOWN, false, "database error");
    3286         psFree(query);
    3287         return false;
    3288     }
    3289     psFree(query);
    3290 
    3291     psArray *output = p_psDBFetchResult(config->dbh);
    3292     if (!output) {
    3293         psError(PS_ERR_UNKNOWN, false, "database error");
    3294         return false;
    3295     }
    3296     if (!psArrayLength(output)) {
    3297         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3298         psFree(output);
    3299         return true;
    3300     }
    3301 
    3302     bool simple = false;
    3303     {
    3304         bool status = false;
    3305         simple = psMetadataLookupBool(&status, config->args, "-simple");
    3306         if (!status) {
    3307             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    3308             psFree(output);
    3309             return false;
    3310         }
    3311     }
    3312 
    3313     // negative simple so the default is true
    3314     if (!ippdbPrintMetadatas(stdout, output, "rawImfile", !simple)) {
    3315         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    3316         psFree(output);
    3317         return false;
    3318     }
    3319 
    3320     psFree(output);
    3321 
    3322     return true;
    3323 }
    3324 
    3325 
    3326 static bool revertstackedMode(pxConfig *config)
    3327 {
    3328     PS_ASSERT_PTR_NON_NULL(config, false);
    3329 
    3330     psString query = pxDataGet("dettool_revertstacked.sql");
    3331     if (!query) {
    3332         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    3333         return false;
    3334     }
    3335 
    3336     if (config->where) {
    3337         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detStackedImfile");
    3338         psStringAppend(&query, " AND %s", whereClause);
    3339         psFree(whereClause);
    3340     }
    3341 
    3342     if (!p_psDBRunQuery(config->dbh, query)) {
    3343         psError(PS_ERR_UNKNOWN, false, "database error");
    3344         psFree(query);
    3345         return false;
    3346     }
    3347     psFree(query);
    3348 
    3349     if (psDBAffectedRows(config->dbh) < 1) {
    3350         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    3351         return false;
    3352     }
    3353 
    3354     return true;
    3355 }
    3356 
    3357 static bool tonormalizedstatMode(pxConfig *config)
    3358 {
    3359     PS_ASSERT_PTR_NON_NULL(config, false);
    3360 
    3361     bool status = false;
    3362     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    3363     if (!status) {
    3364         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    3365         return false;
    3366     }
    3367 
    3368     psString query = pxDataGet("dettool_tonormalizedstat.sql");
    3369     if (!query) {
    3370         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    3371         return false;
    3372     }
    3373 
    3374     // XXX does it make sense to accept any search params?
    3375 #if 0
    3376     if (config->where) {
    3377         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    3378         psStringAppend(&query, " AND %s", whereClause);
    3379         psFree(whereClause);
    3380     }
    3381 #endif
    3382 
    3383     // treat limit == 0 as "no limit"
    3384     if (limit) {
    3385         psString limitString = psDBGenerateLimitSQL(limit);
    3386         psStringAppend(&query, " %s", limitString);
    3387         psFree(limitString);
    3388     }
    3389 
    3390     if (!p_psDBRunQuery(config->dbh, query)) {
    3391         psError(PS_ERR_UNKNOWN, false, "database error");
    3392         psFree(query);
    3393         return false;
    3394     }
    3395     psFree(query);
    3396 
    3397     psArray *output = p_psDBFetchResult(config->dbh);
    3398     if (!output) {
    3399         psError(PS_ERR_UNKNOWN, false, "database error");
    3400         return false;
    3401     }
    3402     if (!psArrayLength(output)) {
    3403         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3404         psFree(output);
    3405         return true;
    3406     }
    3407 
    3408     bool simple = false;
    3409     {
    3410         bool status = false;
    3411         simple = psMetadataLookupBool(&status, config->args, "-simple");
    3412         if (!status) {
    3413             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    3414             return false;
    3415         }
    3416     }
    3417 
    3418     // negative simple so the default is true
    3419     if (!ippdbPrintMetadatas(stdout, output, "detPendingNormStatImfile", !simple)) {
    3420         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    3421         psFree(output);
    3422         return false;
    3423     }
    3424 
    3425     psFree(output);
    3426 
    3427     return true;
    3428 }
    3429 
    3430 static bool addnormalizedstatMode(pxConfig *config)
    3431 {
    3432     PS_ASSERT_PTR_NON_NULL(config, NULL);
    3433 
    3434     // select * from detStackedImfile
    3435     // by det_id, iteration, class_id
    3436     // where det_id, iteration, class_id is not in detNormalizedStatImfile
    3437     psString query = psStringCopy(
    3438         "SELECT DISTINCT"
    3439         "   detStackedImfile.*"
    3440         " FROM detStackedImfile"
    3441         " LEFT JOIN detNormalizedStatImfile"
    3442         "   USING(det_id, iteration, class_id)"
    3443         " WHERE"
    3444         "  detNormalizedStatImfile.det_id IS NULL"
    3445         "  AND detNormalizedStatImfile.iteration IS NULL"
    3446         "  AND detNormalizedStatImfile.class_id IS NULL"
    3447         );
    3448 
    3449     if (config->where) {
    3450         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detStackedImfile");
    3451         psStringAppend(&query, " AND %s", whereClause);
    3452         psFree(whereClause);
    3453     }
    3454 
    3455     if (!p_psDBRunQuery(config->dbh, query)) {
    3456         psError(PS_ERR_UNKNOWN, false, "database error");
    3457         psFree(query);
    3458         return false;
    3459     }
    3460     psFree(query);
    3461 
    3462     psArray *output = p_psDBFetchResult(config->dbh);
    3463     if (!output) {
    3464         psError(PS_ERR_UNKNOWN, false, "database error");
    3465         return false;
    3466     }
    3467     if (!psArrayLength(output)) {
    3468         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3469         psFree(output);
    3470         return true;
    3471     }
    3472 
    3473     // start a transaction so it's all rows or nothing
    3474     if (!psDBTransaction(config->dbh)) {
    3475         psError(PS_ERR_UNKNOWN, false, "database error");
    3476         psFree(output);
    3477         return false;
    3478     }
    3479 
    3480     for (long i = 0; i < psArrayLength(output); i++) {
    3481         psMetadata *row = output->data[i];
    3482         // convert metadata into a detStackedImfile object
    3483         detStackedImfileRow *stackedImfile = detStackedImfileObjectFromMetadata(row);
    3484         // convert detStackedImfile object into a detNormalizedStat object
    3485         detNormalizedStatImfileRow *stat = detStackedToDetNormalizedStatImfile(config, stackedImfile);
    3486         psFree(stackedImfile);
    3487         if (!stat) {
    3488             if (!psDBRollback(config->dbh)) {
    3489                 psError(PS_ERR_UNKNOWN, false, "database error");
    3490             }
    3491             psError(PS_ERR_UNKNOWN, false, "failed to convert detStackedImfile to detNormalizedStatImfile");
    3492             psFree(output);
    3493             return false;
    3494         }
    3495         // insert detNormlized Stat object into the database
    3496         if (!detNormalizedStatImfileInsertObject(config->dbh, stat)) {
    3497             if (!psDBRollback(config->dbh)) {
    3498                 psError(PS_ERR_UNKNOWN, false, "database error");
    3499             }
    3500             psError(PS_ERR_UNKNOWN, false, "database error");
    3501             psFree(stat);
    3502             psFree(output);
    3503         }
    3504         psFree(stat);
    3505     }
    3506 
    3507     psFree(output);
    3508 
    3509     if (!psDBCommit(config->dbh)) {
    3510         psError(PS_ERR_UNKNOWN, false, "database error");
    3511         return false;
    3512     }
    3513 
    3514     return true;
    3515 }
    3516 
    3517 static detNormalizedStatImfileRow *detStackedToDetNormalizedStatImfile(pxConfig *config, detStackedImfileRow *stackedImfile)
    3518 {
    3519     PS_ASSERT_PTR_NON_NULL(config, NULL);
    3520     PS_ASSERT_PTR_NON_NULL(stackedImfile, NULL);
    3521 
    3522     bool status = false;
    3523     psF32 norm = psMetadataLookupF32(&status, config->args, "-norm");
    3524     if (!status) {
    3525         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -norm");
    3526         return false;
    3527     }
    3528     //if (isnan(norm)) {
    3529     //  psError(PS_ERR_UNKNOWN, true, "-norm is required");
    3530     //  return false;
    3531     //}
    3532 
    3533     // default values
    3534     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    3535     if (!status) {
    3536         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    3537         return false;
    3538     }
    3539 
    3540     return detNormalizedStatImfileRowAlloc(
    3541         stackedImfile->det_id,
    3542         stackedImfile->iteration,
    3543         stackedImfile->class_id,
    3544         norm,
    3545         code
    3546     );
    3547 }
    3548 
    3549 
    3550 static bool normalizedstatMode(pxConfig *config)
    3551 {
    3552     PS_ASSERT_PTR_NON_NULL(config, false);
    3553 
    3554     bool status = false;
    3555     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    3556     if (!status) {
    3557         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    3558         return false;
    3559     }
    3560 
    3561     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    3562     if (!status) {
    3563         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    3564         return false;
    3565     }
    3566 
    3567     psString query = pxDataGet("dettool_normalizedstat.sql");
    3568     if (!query) {
    3569         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    3570         return false;
    3571     }
    3572 
    3573     if (faulted) {
    3574         // list only faulted rows
    3575         psStringAppend(&query, " %s", "WHERE fault != 0");
    3576     } else {
    3577         // don't list faulted rows
    3578         psStringAppend(&query, " %s", "WHERE fault = 0");
    3579     }
    3580 
    3581     if (config->where) {
    3582         psString whereClause = psDBGenerateWhereConditionSQL(config->where, NULL);
    3583         psStringAppend(&query, " AND %s", whereClause);
    3584         psFree(whereClause);
    3585     }
    3586 
    3587     // treat limit == 0 as "no limit"
    3588     if (limit) {
    3589         psString limitString = psDBGenerateLimitSQL(limit);
    3590         psStringAppend(&query, " %s", limitString);
    3591         psFree(limitString);
    3592     }
    3593 
    3594     if (!p_psDBRunQuery(config->dbh, query)) {
    3595         psError(PS_ERR_UNKNOWN, false, "database error");
    3596         psFree(query);
    3597         return false;
    3598     }
    3599     psFree(query);
    3600 
    3601     psArray *output = p_psDBFetchResult(config->dbh);
    3602     if (!output) {
    3603         psError(PS_ERR_UNKNOWN, false, "database error");
    3604         return false;
    3605     }
    3606     if (!psArrayLength(output)) {
    3607         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3608         psFree(output);
    3609         return true;
    3610     }
    3611 
    3612     bool simple = false;
    3613     {
    3614         bool status = false;
    3615         simple = psMetadataLookupBool(&status, config->args, "-simple");
    3616         if (!status) {
    3617             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    3618             psFree(output);
    3619             return false;
    3620         }
    3621     }
    3622 
    3623     // negative simple so the default is true
    3624     if (!ippdbPrintMetadatas(stdout, output, "detNormalizedStatImfile", !simple)) {
    3625         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    3626         psFree(output);
    3627         return false;
    3628     }
    3629 
    3630     psFree(output);
    3631 
    3632     return true;
    3633 }
    3634 
    3635 static bool revertnormalizedstatMode(pxConfig *config)
    3636 {
    3637     PS_ASSERT_PTR_NON_NULL(config, false);
    3638 
    3639     psString query = pxDataGet("dettool_revertnormalizedstat.sql");
    3640     if (!query) {
    3641         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    3642         return false;
    3643     }
    3644 
    3645     if (config->where) {
    3646         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detNormalizedStatImfile");
    3647         psStringAppend(&query, " AND %s", whereClause);
    3648         psFree(whereClause);
    3649     }
    3650 
    3651     if (!p_psDBRunQuery(config->dbh, query)) {
    3652         psError(PS_ERR_UNKNOWN, false, "database error");
    3653         psFree(query);
    3654         return false;
    3655     }
    3656     psFree(query);
    3657 
    3658     if (psDBAffectedRows(config->dbh) < 1) {
    3659         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    3660         return false;
    3661     }
    3662 
    3663     return true;
    3664 }
    3665 
    3666 static bool tonormalizeMode(pxConfig *config)
    3667 {
    3668     PS_ASSERT_PTR_NON_NULL(config, false);
    3669 
    3670     bool status = false;
    3671     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    3672     if (!status) {
    3673         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    3674         return false;
    3675     }
    3676 
    3677     // select detNormalizedStatImfile.*
    3678     // by:
    3679     // where det_id, iteration, class_id is not in detNormalizedImfile
    3680 
    3681     psString query = psStringCopy(
    3682         "SELECT DISTINCT"
    3683         "   detRun.det_type,"
    3684         "   detRun.workdir,"
    3685         "   rawExp.camera,"
    3686         "   detStackedImfile.uri,"
    3687         "   detNormalizedStatImfile.*"
    3688         " FROM detRun"
    3689         " JOIN detStackedImfile"
    3690         "   USING(det_id, iteration)"
    3691         " JOIN detInputExp"
    3692         "   USING(det_id, iteration)"
    3693         " JOIN rawExp"
    3694         "   ON detInputExp.exp_id = rawExp.exp_id"
    3695         " JOIN detNormalizedStatImfile"
    3696         "   ON detStackedImfile.det_id = detNormalizedStatImfile.det_id"
    3697         "   AND detStackedImfile.iteration = detNormalizedStatImfile.iteration"
    3698         "   AND detStackedImfile.class_id = detNormalizedStatImfile.class_id"
    3699         " LEFT JOIN detNormalizedImfile"
    3700         "   ON detNormalizedStatImfile.det_id = detNormalizedImfile.det_id"
    3701         "   AND detNormalizedStatImfile.iteration = detNormalizedImfile.iteration"
    3702         "   AND detNormalizedStatImfile.class_id = detNormalizedImfile.class_id"
    3703         " WHERE"
    3704         "   detNormalizedImfile.det_id IS NULL"
    3705         "   AND detNormalizedImfile.iteration IS NULL"
    3706         "   AND detNormalizedImfile.class_id IS NULL"
    3707         "   AND detNormalizedStatImfile.fault = 0"
    3708         );
    3709 
    3710     // XXX does it make sense to accept any search params?
    3711 #if 0
    3712     if (config->where) {
    3713         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    3714         psStringAppend(&query, " AND %s", whereClause);
    3715         psFree(whereClause);
    3716     }
    3717 #endif
    3718 
    3719     // treat limit == 0 as "no limit"
    3720     if (limit) {
    3721         psString limitString = psDBGenerateLimitSQL(limit);
    3722         psStringAppend(&query, " %s", limitString);
    3723         psFree(limitString);
    3724     }
    3725 
    3726     if (!p_psDBRunQuery(config->dbh, query)) {
    3727         psError(PS_ERR_UNKNOWN, false, "database error");
    3728         psFree(query);
    3729         return false;
    3730     }
    3731     psFree(query);
    3732 
    3733     psArray *output = p_psDBFetchResult(config->dbh);
    3734     if (!output) {
    3735         psError(PS_ERR_UNKNOWN, false, "database error");
    3736         return false;
    3737     }
    3738     if (!psArrayLength(output)) {
    3739         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3740         psFree(output);
    3741         return true;
    3742     }
    3743 
    3744     bool simple = false;
    3745     {
    3746         bool status = false;
    3747         simple = psMetadataLookupBool(&status, config->args, "-simple");
    3748         if (!status) {
    3749             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    3750             psFree(output);
    3751             return false;
    3752         }
    3753     }
    3754 
    3755     // negative simple so the default is true
    3756     if (!ippdbPrintMetadatas(stdout, output, "detPendingNormImfile", !simple)) {
    3757         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    3758         psFree(output);
    3759         return false;
    3760     }
    3761 
    3762     psFree(output);
    3763 
    3764     return true;
    3765 }
    3766 
    3767 #if 0
    3768 // XXX this function was left in commented as this method may be useful in the
    3769 // future
    3770 static psArray *validDetInputClassIds(pxConfig *config, const char *det_id)
    3771 {
    3772     PS_ASSERT_PTR_NON_NULL(config, NULL);
    3773     // det_id is input as a string because the fact that it is an integer
    3774     // is just a database impliementation detail.
    3775     PS_ASSERT_PTR_NON_NULL(det_id, NULL);
    3776 
    3777     psArray *rawImfiles = searchInputImfiles(config, det_id);
    3778     if (!rawImfiles) {
    3779         return NULL;
    3780     }
    3781 
    3782     psArray *valid_class_ids = NULL;
    3783     {
    3784         // All this jumping through hoops is so that we end up with unique
    3785         // values. PP thinks that making multiple passes through this array and
    3786         // deleting matched elements would end up being more expensive then a
    3787         // double sort and stagger scheme. JH thinks it would be cheaper to
    3788         // just do a unqiue sort and delete.  So this is really just a cheap
    3789         // hack to avoid implimenting a unique sort function but at least it's
    3790         // stable.
    3791         psHash *hash = psHashAlloc(psArrayLength(rawImfiles));
    3792         for (long i = 0; i < psArrayLength(rawImfiles); i++) {
    3793             psHashAdd(hash,
    3794                 ((rawImfileRow *)rawImfiles->data[i])->class_id,
    3795                 ((rawImfileRow *)rawImfiles->data[i])->class_id
    3796             );
    3797         }
    3798         valid_class_ids = psHashToArray(hash);
    3799         psFree(hash);
    3800     }
    3801     psFree(rawImfiles);
    3802 
    3803     return valid_class_ids;
    3804 }
    3805 
    3806 static psArray *searchInputImfiles(pxConfig *config, const char *det_id)
    3807 {
    3808     PS_ASSERT_PTR_NON_NULL(config, NULL);
    3809     // det_id is input as a string because the fact that it is an integer
    3810     // is just a database impliementation detail.
    3811     PS_ASSERT_PTR_NON_NULL(det_id, NULL);
    3812 
    3813     psArray *inputExps = NULL;
    3814     {
    3815         psMetadata *where = psMetadataAlloc();
    3816         if (!psMetadataAddS32(where, PS_LIST_TAIL, "det_id", 0, "==",
    3817                 (psS32)atoi(det_id))) {
    3818             psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    3819             psFree(where);
    3820             return NULL;
    3821         }
    3822         inputExps = detInputExpSelectRowObjects(config->dbh, where, 0);
    3823         psFree(where);
    3824     }
    3825     if (!inputExps) {
    3826         psError(PS_ERR_UNKNOWN, false, "no detInputExp rows found");
    3827         return NULL;
    3828     }
    3829 
    3830     // find rawImfiles associated with detInputExps
    3831     psArray *rawImfiles = NULL;
    3832     {
    3833         psMetadata *where = psMetadataAlloc();
    3834         for (long i = 0; i < psArrayLength(inputExps); i++) {
    3835             if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id",
    3836                     PS_META_DUPLICATE_OK, "==",
    3837                     ((detInputExpRow *)inputExps->data[i])->exp_id)) {
    3838                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    3839                 psFree(inputExps);
    3840                 psFree(where);
    3841                 return NULL;
    3842             }
    3843         }
    3844         psFree(inputExps);
    3845         rawImfiles = rawImfileSelectRowObjects(config->dbh, where, 0);
    3846         // XXX this really should be sorted for uniqueness
    3847         psFree(where);
    3848     }
    3849     if (!rawImfiles) {
    3850         psError(PS_ERR_UNKNOWN, false, "no rawImfile rows found");
    3851         return NULL;
    3852     }
    3853 
    3854     return rawImfiles;
    3855 }
    3856 #endif
    3857 
    3858 static bool addnormalizedimfileMode(pxConfig *config)
    3859 {
    3860     PS_ASSERT_PTR_NON_NULL(config, false);
    3861 
    3862     // make sure that there is a respondoing entry in detNormalizedStatImfile
    3863     // select * from detNormalizedStatImfile
    3864     // by det_id, iteration, class_id
    3865     // where det_id, iteration, class_id is not in detNormalizedImfile
    3866     psString query = psStringCopy(
    3867         "SELECT"
    3868         "   detNormalizedStatImfile.*"
    3869         " FROM detNormalizedStatImfile"
    3870         " LEFT JOIN detNormalizedImfile"
    3871         "   USING(det_id, iteration, class_id)"
    3872         " WHERE"
    3873         "  detNormalizedImfile.det_id IS NULL"
    3874         "  AND detNormalizedImfile.iteration IS NULL"
    3875         "  AND detNormalizedImfile.class_id IS NULL"
    3876         );
    3877 
    3878     {
    3879         // build a query to search by det_id, iteration, class_id
    3880         psMetadata *where = psMetadataAlloc();
    3881         bool status = false;
    3882         psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    3883         if (!status) {
    3884             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    3885             psFree(where);
    3886             psFree(query);
    3887             return false;
    3888         }
    3889         if (det_id) {
    3890             if (!psMetadataAddStr(where, PS_LIST_TAIL, "det_id", 0, "==", det_id)) {
    3891                 psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    3892                 psFree(where);
    3893                 psFree(query);
    3894                 return false;
    3895             }
    3896         }
    3897         psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    3898         if (!status) {
    3899             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");
    3900             psFree(where);
    3901             psFree(query);
    3902             return false;
    3903         }
    3904         // always set iteration
    3905         if (!psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", iteration)) {
    3906             psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
    3907             psFree(where);
    3908             psFree(query);
    3909             return false;
    3910         }
    3911         psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
    3912         if (!status) {
    3913             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
    3914             psFree(where);
    3915             psFree(query);
    3916             return false;
    3917         }
    3918         if (class_id) {
    3919             if (!psMetadataAddStr(where, PS_LIST_TAIL, "class_id", 0, "==", class_id)) {
    3920                 psError(PS_ERR_UNKNOWN, false, "failed to add item class_id");
    3921                 psFree(where);
    3922                 psFree(query);
    3923                 return false;
    3924             }
    3925         }
    3926 
    3927         // there's not
    3928         psString whereClause = psDBGenerateWhereConditionSQL(where, "detNormalizedStatImfile");
    3929         psFree(where);
    3930         if (whereClause) {
    3931             psStringAppend(&query, " AND %s", whereClause);
    3932             psFree(whereClause);
    3933         }
    3934     }
    3935 
    3936     if (!p_psDBRunQuery(config->dbh, query)) {
    3937         psError(PS_ERR_UNKNOWN, false, "database error");
    3938         psFree(query);
    3939         return false;
    3940     }
    3941     psFree(query);
    3942 
    3943     psArray *output = p_psDBFetchResult(config->dbh);
    3944     if (!output) {
    3945         psError(PS_ERR_UNKNOWN, false, "database error");
    3946         return false;
    3947     }
    3948     if (!psArrayLength(output)) {
    3949         psTrace("dettool", PS_LOG_INFO, "no rows found");
    3950         psFree(output);
    3951         return true;
    3952     }
    3953 
    3954     // start a transaction so it's all rows or nothing
    3955     if (!psDBTransaction(config->dbh)) {
    3956         psError(PS_ERR_UNKNOWN, false, "database error");
    3957         psFree(output);
    3958         return false;
    3959     }
    3960 
    3961     for (long i = 0; i < psArrayLength(output); i++) {
    3962         psMetadata *row = output->data[i];
    3963         // convert metadata into a detNormalizedStatImfile object
    3964         detNormalizedStatImfileRow *statImfile = detNormalizedStatImfileObjectFromMetadata(row);
    3965         // convert detNormalizedStatImfile object into a detNormalizedImfile
    3966         detNormalizedImfileRow *normalizedImfile  = detNormalizedStatToDetNormalizedmfile(config, statImfile);
    3967         psFree(statImfile);
    3968         if (!normalizedImfile) {
    3969             if (!psDBRollback(config->dbh)) {
    3970                 psError(PS_ERR_UNKNOWN, false, "database error");
    3971             }
    3972             psError(PS_ERR_UNKNOWN, false, "failed to convert detStackedImfile to detNormalizedStatImfile");
    3973             psFree(output);
    3974             return false;
    3975         }
    3976         // insert detNormlized Stat object into the database
    3977         if (!detNormalizedImfileInsertObject(config->dbh, normalizedImfile)) {
    3978             if (!psDBRollback(config->dbh)) {
    3979                 psError(PS_ERR_UNKNOWN, false, "database error");
    3980             }
    3981             psError(PS_ERR_UNKNOWN, false, "database error");
    3982             psFree(normalizedImfile);
    3983             psFree(output);
    3984         }
    3985         psFree(normalizedImfile);
    3986     }
    3987 
    3988     psFree(output);
    3989 
    3990     if (!psDBCommit(config->dbh)) {
    3991         psError(PS_ERR_UNKNOWN, false, "database error");
    3992         return false;
    3993     }
    3994 
    3995     return true;
    3996 }
    3997 
    3998 
    3999 static bool normalizedimfileMode(pxConfig *config)
    4000 {
    4001     PS_ASSERT_PTR_NON_NULL(config, false);
    4002 
    4003     bool status = false;
    4004     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    4005     if (!status) {
    4006         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    4007         return false;
    4008     }
    4009 
    4010     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    4011     if (!status) {
    4012         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    4013         return false;
    4014     }
    4015 
    4016     psString query = psStringCopy(
    4017         "SELECT"
    4018         " detNormalizedImfile.*"
    4019         " FROM detNormalizedImfile"
    4020         " JOIN detRun"
    4021         "   USING(det_id, iteration)"
    4022         " WHERE"
    4023         "   detRun.state = 'run'"
    4024         "   AND detRun.mode = 'master'"
    4025     );
    4026 
    4027     if (config->where) {
    4028         bool status;
    4029         int iteration = psMetadataLookupS32 (&status, config->where, "iteration");
    4030         if (status) {
    4031             psMetadataRemoveKey (config->where, "iteration");
    4032             psMetadataAddS32 (config->where, PS_LIST_TAIL, "iteration", 0, "==", iteration);
    4033         }
    4034         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detNormalizedImfile");
    4035         psStringAppend(&query, " AND %s", whereClause);
    4036         psFree(whereClause);
    4037     }
    4038 
    4039     if (faulted) {
    4040         // list only faulted rows
    4041         psStringAppend(&query, " %s", "AND detNormalizedImfile.fault != 0");
    4042     } else {
    4043         // don't list faulted rows
    4044         psStringAppend(&query, " %s", "AND detNormalizedImfile.fault = 0");
    4045     }
    4046 
    4047     // treat limit == 0 as "no limit"
    4048     if (limit) {
    4049         psString limitString = psDBGenerateLimitSQL(limit);
    4050         psStringAppend(&query, " %s", limitString);
    4051         psFree(limitString);
    4052     }
    4053 
    4054     if (!p_psDBRunQuery(config->dbh, query)) {
    4055         psError(PS_ERR_UNKNOWN, false, "database error");
    4056         psFree(query);
    4057         return false;
    4058     }
    4059     psFree(query);
    4060 
    4061     psArray *output = p_psDBFetchResult(config->dbh);
    4062     if (!output) {
    4063         psError(PS_ERR_UNKNOWN, false, "database error");
    4064         return false;
    4065     }
    4066     if (!psArrayLength(output)) {
    4067         psTrace("dettool", PS_LOG_INFO, "no rows found");
    4068         psFree(output);
    4069         return true;
    4070     }
    4071 
    4072     bool simple = false;
    4073     {
    4074         bool status = false;
    4075         simple = psMetadataLookupBool(&status, config->args, "-simple");
    4076         if (!status) {
    4077             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    4078             return false;
    4079         }
    4080     }
    4081 
    4082     // negative simple so the default is true
    4083     if (!ippdbPrintMetadatas(stdout, output, "detNormalizedImfile", !simple)) {
    4084         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    4085         psFree(output);
    4086         return false;
    4087     }
    4088 
    4089     psFree(output);
    4090 
    4091     return true;
    4092 }
    4093 
    4094 
    4095 static bool revertnormalizedimfileMode(pxConfig *config)
    4096 {
    4097     PS_ASSERT_PTR_NON_NULL(config, false);
    4098 
    4099     psString query = pxDataGet("dettool_revertnormalizedimfile.sql");
    4100     if (!query) {
    4101         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    4102         return false;
    4103     }
    4104 
    4105     if (config->where) {
    4106         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detNormalizedImfile");
    4107         psStringAppend(&query, " AND %s", whereClause);
    4108         psFree(whereClause);
    4109     }
    4110 
    4111     if (!p_psDBRunQuery(config->dbh, query)) {
    4112         psError(PS_ERR_UNKNOWN, false, "database error");
    4113         psFree(query);
    4114         return false;
    4115     }
    4116     psFree(query);
    4117 
    4118     if (psDBAffectedRows(config->dbh) < 1) {
    4119         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    4120         return false;
    4121     }
    4122 
    4123     return true;
    4124 }
    4125 
    4126 
    4127 static bool tonormalizedexpMode(pxConfig *config)
    4128 {
    4129     PS_ASSERT_PTR_NON_NULL(config, false);
    4130 
    4131     bool status = false;
    4132     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    4133     if (!status) {
    4134         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    4135         return false;
    4136     }
    4137 
    4138     psString query = pxDataGet("dettool_tonormalizedexp.sql");
    4139     if (!query) {
    4140         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    4141         return false;
    4142     }
    4143 
    4144     // XXX does it make sense to accept any search params?
    4145 #if 0
    4146     if (config->where) {
    4147         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    4148         psStringAppend(&query, " AND %s", whereClause);
    4149         psFree(whereClause);
    4150     }
    4151 #endif
    4152 
    4153     // treat limit == 0 as "no limit"
    4154     if (limit) {
    4155         psString limitString = psDBGenerateLimitSQL(limit);
    4156         psStringAppend(&query, " %s", limitString);
    4157         psFree(limitString);
    4158     }
    4159 
    4160     if (!p_psDBRunQuery(config->dbh, query)) {
    4161         psError(PS_ERR_UNKNOWN, false, "database error");
    4162         psFree(query);
    4163         return false;
    4164     }
    4165     psFree(query);
    4166 
    4167     psArray *output = p_psDBFetchResult(config->dbh);
    4168     if (!output) {
    4169         psError(PS_ERR_UNKNOWN, false, "database error");
    4170         return false;
    4171     }
    4172     if (!psArrayLength(output)) {
    4173         psTrace("dettool", PS_LOG_INFO, "no rows found");
    4174         psFree(output);
    4175         return true;
    4176     }
    4177 
    4178     bool simple = false;
    4179     {
    4180         bool status = false;
    4181         simple = psMetadataLookupBool(&status, config->args, "-simple");
    4182         if (!status) {
    4183             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    4184             return false;
    4185         }
    4186     }
    4187 
    4188     // negative simple so the default is true
    4189     if (!ippdbPrintMetadatas(stdout, output, "detPendingNormExp", !simple)) {
    4190         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    4191         psFree(output);
    4192         return false;
    4193     }
    4194 
    4195     psFree(output);
    4196 
    4197     return true;
    4198 }
    4199 
    4200 static bool addnormalizedexpMode(pxConfig *config)
    4201 {
    4202     PS_ASSERT_PTR_NON_NULL(config, false);
    4203 
    4204     // det_id, recip, -bg, -bg_stdev
    4205     // are required
    4206     bool status = false;
    4207     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    4208     if (!status) {
    4209         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    4210         return false;
    4211     }
    4212     if (!det_id) {
    4213         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    4214         return false;
    4215     }
    4216     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    4217     if (!status) {
    4218         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    4219         return false;
    4220     }
    4221     if (!recipe) {
    4222         psError(PS_ERR_UNKNOWN, true, "-recip is required");
    4223         return false;
    4224     }
    4225     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    4226     if (!status) {
    4227         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    4228         return false;
    4229     }
    4230     //if (isnan(bg)) {
    4231     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    4232     //  return false;
    4233     //}
    4234     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    4235     if (!status) {
    4236         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    4237         return false;
    4238     }
    4239     //if (isnan(bg_stdev)) {
    4240     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    4241     //  return false;
    4242     //}
    4243     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    4244     if (!status) {
    4245         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    4246         return false;
    4247     }
    4248 
    4249     // optional
    4250     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    4251     if (!status) {
    4252         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    4253         return false;
    4254     }
    4255     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    4256     if (!status) {
    4257         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    4258         return false;
    4259     }
    4260     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    4261     if (!status) {
    4262         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    4263         return false;
    4264     }
    4265     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    4266     if (!status) {
    4267         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    4268         return false;
    4269     }
    4270     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    4271     if (!status) {
    4272         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    4273         return false;
    4274     }
    4275 
    4276     // iteration has a default value
    4277     psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    4278     if (!status) {
    4279         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");        return false;
    4280     }
    4281 
    4282     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    4283     if (!status) {
    4284         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    4285         return false;
    4286     }
    4287 
    4288     // optional
    4289     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    4290     if (!status) {
    4291         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    4292         return false;
    4293     }
    4294 
    4295     psString query = pxDataGet("dettool_tonormalizedexp.sql");
    4296     if (!query) {
    4297         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    4298         return false;
    4299     }
    4300 
    4301     psStringAppend(&query,
    4302             "WHERE det_id = %s"
    4303             " AND iteration = %d", det_id, iteration);
    4304 
    4305 
    4306     if (!p_psDBRunQuery(config->dbh, query)) {
    4307         psError(PS_ERR_UNKNOWN, false, "database error");
    4308         psFree(query);
    4309         return false;
    4310     }
    4311     psFree(query);
    4312 
    4313     psArray *output = p_psDBFetchResult(config->dbh);
    4314     if (!output) {
    4315         psError(PS_ERR_UNKNOWN, false, "database error");
    4316         return false;
    4317     }
    4318     if (!psArrayLength(output)) {
    4319         psTrace("dettool", PS_LOG_INFO, "no rows found");
    4320         psFree(output);
    4321         return true;
    4322     }
    4323     psFree(output);
    4324 
    4325     // create a new detProcessedImfile object
    4326     detNormalizedExpRow *detRow = detNormalizedExpRowAlloc(
    4327         (psS32)atoll(det_id),
    4328         iteration,
    4329         recipe,
    4330         bg,
    4331         bg_stdev,
    4332         bg_mean_stdev,
    4333         user_1,
    4334         user_2,
    4335         user_3,
    4336         user_4,
    4337         user_5,
    4338         path_base,
    4339         code
    4340     );
    4341 
    4342     // insert the new row into the detProcessedImfile table
    4343     if (!detNormalizedExpInsertObject(config->dbh, detRow)) {
    4344         psError(PS_ERR_UNKNOWN, false, "database error");
    4345         psFree(detRow);
    4346         return false;
    4347     }
    4348 
    4349     psFree(detRow);
    4350 
    4351     return true;
    4352 }
    4353 
    4354 
    4355 static bool normalizedexpMode(pxConfig *config)
    4356 {
    4357     PS_ASSERT_PTR_NON_NULL(config, false);
    4358 
    4359     bool status = false;
    4360     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    4361     if (!status) {
    4362         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    4363         return false;
    4364     }
    4365 
    4366     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    4367     if (!status) {
    4368         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    4369         return false;
    4370     }
    4371 
    4372     psString query = psStringCopy(
    4373         "SELECT"
    4374         "   detNormalizedExp.*"
    4375         " FROM detNormalizedExp"
    4376         " JOIN detRun"
    4377         "   USING(det_id, iteration)"
    4378         " WHERE"
    4379         "   detRun.state = 'run'"
    4380         "   AND detRun.mode = 'master'"
    4381     );
    4382 
    4383     if (config->where) {
    4384         psString whereClause = psDBGenerateWhereSQL(config->where, NULL);
    4385         psStringAppend(&query, " %s", whereClause);
    4386         psFree(whereClause);
    4387     }
    4388 
    4389     if (faulted) {
    4390         // list only faulted rows
    4391         psStringAppend(&query, " %s", "AND detNormalizedExp.fault != 0");
    4392     } else {
    4393         // don't list faulted rows
    4394         psStringAppend(&query, " %s", "AND detNormalizedExp.fault = 0");
    4395     }
    4396 
    4397     // treat limit == 0 as "no limit"
    4398     if (limit) {
    4399         psString limitString = psDBGenerateLimitSQL(limit);
    4400         psStringAppend(&query, " %s", limitString);
    4401         psFree(limitString);
    4402     }
    4403 
    4404     if (!p_psDBRunQuery(config->dbh, query)) {
    4405         psError(PS_ERR_UNKNOWN, false, "database error");
    4406         psFree(query);
    4407         return false;
    4408     }
    4409     psFree(query);
    4410 
    4411     psArray *output = p_psDBFetchResult(config->dbh);
    4412     if (!output) {
    4413         psError(PS_ERR_UNKNOWN, false, "database error");
    4414         return false;
    4415     }
    4416     if (!psArrayLength(output)) {
    4417         psTrace("dettool", PS_LOG_INFO, "no rows found");
    4418         psFree(output);
    4419         return true;
    4420     }
    4421 
    4422     bool simple = false;
    4423     {
    4424         bool status = false;
    4425         simple = psMetadataLookupBool(&status, config->args, "-simple");
    4426         if (!status) {
    4427             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    4428             return false;
    4429         }
    4430     }
    4431 
    4432     // negative simple so the default is true
    4433     if (!ippdbPrintMetadatas(stdout, output, "detNormalizedExp", !simple)) {
    4434         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    4435         psFree(output);
    4436         return false;
    4437     }
    4438 
    4439     psFree(output);
    4440 
    4441     return true;
    4442 }
    4443 
    4444 
    4445 static  detNormalizedImfileRow *detNormalizedStatToDetNormalizedmfile(pxConfig *config, detNormalizedStatImfileRow *statImfile)
    4446 {
    4447     PS_ASSERT_PTR_NON_NULL(config, NULL);
    4448     PS_ASSERT_PTR_NON_NULL(statImfile, NULL);
    4449 
    4450     bool status = false;
    4451     psString uri = psMetadataLookupStr(&status, config->args, "-uri");
    4452     if (!status) {
    4453         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
    4454         return false;
    4455     }
    4456     if (!uri) {
    4457         psError(PS_ERR_UNKNOWN, true, "-uri is required");
    4458         return false;
    4459     }
    4460     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    4461     if (!status) {
    4462         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    4463         return false;
    4464     }
    4465     //if (isnan(bg)) {
    4466     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    4467     //  return false;
    4468     //}
    4469     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    4470     if (!status) {
    4471         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    4472         return false;
    4473     }
    4474     //if (isnan(bg_stdev)) {
    4475     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    4476     //  return false;
    4477     //}
    4478     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    4479     if (!status) {
    4480         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    4481         return false;
    4482     }
    4483     // optional
    4484     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    4485     if (!status) {
    4486         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    4487         return false;
    4488     }
    4489     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    4490     if (!status) {
    4491         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    4492         return false;
    4493     }
    4494     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    4495     if (!status) {
    4496         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    4497         return false;
    4498     }
    4499     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    4500     if (!status) {
    4501         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    4502         return false;
    4503     }
    4504     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    4505     if (!status) {
    4506         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    4507         return false;
    4508     }
    4509 
    4510     // optional
    4511     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    4512     if (!status) {
    4513         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    4514         return false;
    4515     }
    4516 
    4517     // default values
    4518     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    4519     if (!status) {
    4520         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    4521         return false;
    4522     }
    4523 
    4524     return detNormalizedImfileRowAlloc(
    4525         statImfile->det_id,
    4526         statImfile->iteration,
    4527         statImfile->class_id,
    4528         uri,
    4529         bg,
    4530         bg_stdev,
    4531         bg_mean_stdev,
    4532         user_1,
    4533         user_2,
    4534         user_3,
    4535         user_4,
    4536         user_5,
    4537         path_base,
    4538         code
    4539     );
    4540 }
    4541 
    4542 
    4543 static bool revertnormalizedexpMode(pxConfig *config)
    4544 {
    4545     PS_ASSERT_PTR_NON_NULL(config, false);
    4546 
    4547     psString query = pxDataGet("dettool_revertnormalizedexp.sql");
    4548     if (!query) {
    4549         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    4550         return false;
    4551     }
    4552 
    4553     if (config->where) {
    4554         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detNormalizedExp");
    4555         psStringAppend(&query, " AND %s", whereClause);
    4556         psFree(whereClause);
    4557     }
    4558 
    4559     if (!p_psDBRunQuery(config->dbh, query)) {
    4560         psError(PS_ERR_UNKNOWN, false, "database error");
    4561         psFree(query);
    4562         return false;
    4563     }
    4564     psFree(query);
    4565 
    4566     if (psDBAffectedRows(config->dbh) < 1) {
    4567         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    4568         return false;
    4569     }
    4570 
    4571     return true;
    4572 }
    4573 
    4574 
    4575 static bool toresidimfileMode(pxConfig *config)
    4576 {
    4577     PS_ASSERT_PTR_NON_NULL(config, false);
    4578 
    4579     bool status = false;
    4580     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    4581     if (!status) {
    4582         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    4583         return false;
    4584     }
    4585 
    4586     psString query = pxDataGet("dettool_toresidimfile.sql");
    4587     if (!query) {
    4588         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    4589         return false;
    4590     }
    4591 
    4592     // XXX does it make sense to accept any search params?
    4593 #if 0
    4594     if (config->where) {
    4595         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    4596         psStringAppend(&query, " AND %s", whereClause);
    4597         psFree(whereClause);
    4598     }
    4599 #endif
    4600 
    4601     // treat limit == 0 as "no limit"
    4602     if (limit) {
    4603         psString limitString = psDBGenerateLimitSQL(limit);
    4604         psStringAppend(&query, " %s", limitString);
    4605         psFree(limitString);
    4606     }
    4607 
    4608     if (!p_psDBRunQuery(config->dbh, query)) {
    4609         psError(PS_ERR_UNKNOWN, false, "database error");
    4610         psFree(query);
    4611         return false;
    4612     }
    4613     psFree(query);
    4614 
    4615     psArray *output = p_psDBFetchResult(config->dbh);
    4616     if (!output) {
    4617         psError(PS_ERR_UNKNOWN, false, "database error");
    4618         return false;
    4619     }
    4620     if (!psArrayLength(output)) {
    4621         psTrace("dettool", PS_LOG_INFO, "no rows found");
    4622         psFree(output);
    4623         return true;
    4624     }
    4625 
    4626     bool simple = false;
    4627     {
    4628         bool status = false;
    4629         simple = psMetadataLookupBool(&status, config->args, "-simple");
    4630         if (!status) {
    4631             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    4632             psFree(output);
    4633             return false;
    4634         }
    4635     }
    4636 
    4637     // negative simple so the default is true
    4638     if (!ippdbPrintMetadatas(stdout, output, "detPendingResidImfile", !simple)) {
    4639         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    4640         psFree(output);
    4641         return false;
    4642     }
    4643 
    4644     psFree(output);
    4645 
    4646     return true;
    4647 }
    4648 
    4649 
    4650 static bool addresidimfileMode(pxConfig *config)
    4651 {
    4652     PS_ASSERT_PTR_NON_NULL(config, false);
    4653 
    4654     bool status = false;
    4655     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    4656     if (!status) {
    4657         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    4658         return false;
    4659     }
    4660     if (!det_id) {
    4661         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    4662         return false;
    4663     }
    4664 
    4665     // defaults to 0
    4666     psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    4667     if (!status) {
    4668         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");
    4669         return false;
    4670     }
    4671 
    4672     psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
    4673     if (!status) {
    4674         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
    4675         return false;
    4676     }
    4677     if (!class_id) {
    4678         psError(PS_ERR_UNKNOWN, false, "-class_id is required");
    4679         return false;
    4680     }
    4681 
    4682     psString exp_id = psMetadataLookupStr(&status, config->args, "-exp_id");
    4683     if (!status) {
    4684         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_id");
    4685         return false;
    4686     }
    4687     if (!exp_id) {
    4688         psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
    4689         return false;
    4690     }
    4691     psString uri = psMetadataLookupStr(&status, config->args, "-uri");
    4692     if (!status) {
    4693         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
    4694         return false;
    4695     }
    4696     if (!uri) {
    4697         psError(PS_ERR_UNKNOWN, true, "-uri is required");
    4698         return false;
    4699     }
    4700     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    4701     if (!status) {
    4702         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    4703         return false;
    4704     }
    4705     if (!recipe) {
    4706         psError(PS_ERR_UNKNOWN, true, "-recip is required");
    4707         return false;
    4708     }
    4709     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    4710     if (!status) {
    4711         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    4712         return false;
    4713     }
    4714     //if (isnan(bg)) {
    4715     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    4716     //  return false;
    4717     //}
    4718     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    4719     if (!status) {
    4720         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    4721         return false;
    4722     }
    4723     //if (isnan(bg_stdev)) {
    4724     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    4725     //  return false;
    4726     //}
    4727     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    4728     if (!status) {
    4729         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    4730         return false;
    4731     }
    4732     psF64 bg_skewness = psMetadataLookupF64(&status, config->args, "-bg_skewness");
    4733     if (!status) {
    4734         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_skewness");
    4735         return false;
    4736     }
    4737     psF64 bg_kurtosis = psMetadataLookupF64(&status, config->args, "-bg_kurtosis");
    4738     if (!status) {
    4739         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_kurtosis");
    4740         return false;
    4741     }
    4742     psF64 bin_stdev = psMetadataLookupF64(&status, config->args, "-bin_stdev");
    4743     if (!status) {
    4744         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bin_stdev");
    4745         return false;
    4746     }
    4747 
    4748     // optional
    4749     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    4750     if (!status) {
    4751         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    4752         return false;
    4753     }
    4754 
    4755     // optional
    4756     psF64 fringe_0 = psMetadataLookupF64(&status, config->args, "-fringe_0");
    4757     if (!status) {
    4758         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_0");
    4759         return false;
    4760     }
    4761     psF64 fringe_1 = psMetadataLookupF64(&status, config->args, "-fringe_1");
    4762     if (!status) {
    4763         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_1");
    4764         return false;
    4765     }
    4766     psF64 fringe_2 = psMetadataLookupF64(&status, config->args, "-fringe_2");
    4767     if (!status) {
    4768         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_2");
    4769         return false;
    4770     }
    4771 
    4772     psF64 fringe_resid_0 = psMetadataLookupF64(&status, config->args, "-fringe_resid_0");
    4773     if (!status) {
    4774         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_resid_0");
    4775         return false;
    4776     }
    4777     psF64 fringe_resid_1 = psMetadataLookupF64(&status, config->args, "-fringe_resid_1");
    4778     if (!status) {
    4779         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_resid_1");
    4780         return false;
    4781     }
    4782     psF64 fringe_resid_2 = psMetadataLookupF64(&status, config->args, "-fringe_resid_2");
    4783     if (!status) {
    4784         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_resid_2");
    4785         return false;
    4786     }
    4787 
    4788     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    4789     if (!status) {
    4790         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    4791         return false;
    4792     }
    4793     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    4794     if (!status) {
    4795         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    4796         return false;
    4797     }
    4798     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    4799     if (!status) {
    4800         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    4801         return false;
    4802     }
    4803     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    4804     if (!status) {
    4805         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    4806         return false;
    4807     }
    4808     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    4809     if (!status) {
    4810         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    4811         return false;
    4812     }
    4813 
    4814     // default values
    4815     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    4816     if (!status) {
    4817         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    4818         return false;
    4819     }
    4820 
    4821     // create a new detResidImfileRow and insert it
    4822     if (!detResidImfileInsert(
    4823             config->dbh,
    4824             (psS64)atoll(det_id),
    4825             iteration,
    4826             (psS64)atoll(exp_id),
    4827             class_id,
    4828             uri,
    4829             recipe,
    4830             bg,
    4831             bg_stdev,
    4832             bg_mean_stdev,
    4833             bg_skewness,
    4834             bg_kurtosis,
    4835             bin_stdev,
    4836             fringe_0,
    4837             fringe_1,
    4838             fringe_2,
    4839             fringe_resid_0,
    4840             fringe_resid_1,
    4841             fringe_resid_2,
    4842             user_1,
    4843             user_2,
    4844             user_3,
    4845             user_4,
    4846             user_5,
    4847             path_base,
    4848             code
    4849     )) {
    4850         psError(PS_ERR_UNKNOWN, false, "database error");
    4851         return false;
    4852     }
    4853 
    4854     return true;
    4855 }
    4856 
    4857 static bool residimfileMode(pxConfig *config)
    4858 {
    4859     PS_ASSERT_PTR_NON_NULL(config, false);
    4860 
    4861     char *value = NULL;
    4862     bool status = false;
    4863     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    4864     if (!status) {
    4865         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    4866         return false;
    4867     }
    4868 
    4869     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    4870     if (!status) {
    4871         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    4872         return false;
    4873     }
    4874 
    4875     // select detResidImfile.*
    4876     // by:
    4877     // where det_id, iteration, exp_id is not in detResidExp;
    4878 
    4879     psString query = psStringCopy(
    4880         "SELECT"
    4881         "   detRun.det_type,"
    4882         "   detRun.mode,"
    4883         "   detResidImfile.*,"
    4884         "   rawExp.exp_time"
    4885         " FROM detResidImfile"
    4886         " JOIN detRun"
    4887         "   USING(det_id, iteration)"
    4888         " JOIN rawExp"
    4889         "   USING(exp_id)"
    4890         " WHERE"
    4891     );
    4892     // NOTE the above WHERE is completed with the following line:
    4893 
    4894     // add the two required restrictions: detRun.state and detRun.mode
    4895     if ((value = psMetadataLookupStr(&status, config->args, "-select_state"))) {
    4896         psStringAppend(&query, " detRun.state = '%s'", value);
    4897     } else {
    4898         psStringAppend(&query, " detRun.state = 'run'");
    4899     }
    4900 
    4901     if (config->where) {
    4902         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detResidImfile");
    4903         psStringAppend(&query, " AND %s", whereClause);
    4904         psFree(whereClause);
    4905     }
    4906 
    4907     if (faulted) {
    4908         // list only faulted rows
    4909         psStringAppend(&query, " %s", "AND detResidImfile.fault != 0");
    4910     } else {
    4911         // don't list faulted rows
    4912         psStringAppend(&query, " %s", "AND detResidImfile.fault = 0");
    4913     }
    4914 
    4915     // treat limit == 0 as "no limit"
    4916     if (limit) {
    4917         psString limitString = psDBGenerateLimitSQL(limit);
    4918         psStringAppend(&query, " %s", limitString);
    4919         psFree(limitString);
    4920     }
    4921 
    4922     if (!p_psDBRunQuery(config->dbh, query)) {
    4923         psError(PS_ERR_UNKNOWN, false, "database error");
    4924         psFree(query);
    4925         return false;
    4926     }
    4927     psFree(query);
    4928 
    4929     psArray *output = p_psDBFetchResult(config->dbh);
    4930     if (!output) {
    4931         psError(PS_ERR_UNKNOWN, false, "database error");
    4932         return false;
    4933     }
    4934     if (!psArrayLength(output)) {
    4935         psTrace("dettool", PS_LOG_INFO, "no rows found");
    4936         psFree(output);
    4937         return true;
    4938     }
    4939 
    4940     bool simple = false;
    4941     {
    4942         bool status = false;
    4943         simple = psMetadataLookupBool(&status, config->args, "-simple");
    4944         if (!status) {
    4945             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    4946             psFree(output);
    4947             return false;
    4948         }
    4949     }
    4950 
    4951     // negative simple so the default is true
    4952     if (!ippdbPrintMetadatas(stdout, output, "rawResidImfile", !simple)) {
    4953         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    4954         psFree(output);
    4955         return false;
    4956     }
    4957 
    4958     psFree(output);
    4959 
    4960     return true;
    4961 }
    4962 
    4963 
    4964 static bool revertresidimfileMode(pxConfig *config)
    4965 {
    4966     PS_ASSERT_PTR_NON_NULL(config, false);
    4967 
    4968     psString query = pxDataGet("dettool_revertresidimfile.sql");
    4969     if (!query) {
    4970         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    4971         return false;
    4972     }
    4973 
    4974     if (config->where) {
    4975         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detResidImfile");
    4976         psStringAppend(&query, " AND %s", whereClause);
    4977         psFree(whereClause);
    4978     }
    4979 
    4980     if (!p_psDBRunQuery(config->dbh, query)) {
    4981         psError(PS_ERR_UNKNOWN, false, "database error");
    4982         psFree(query);
    4983         return false;
    4984     }
    4985     psFree(query);
    4986 
    4987     if (psDBAffectedRows(config->dbh) < 1) {
    4988         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    4989         return false;
    4990     }
    4991 
    4992     return true;
    4993 }
    4994 
    4995 
    4996 static bool toresidexpMode(pxConfig *config)
    4997 {
    4998     PS_ASSERT_PTR_NON_NULL(config, false);
    4999 
    5000     /*
    5001 which returns a list of exposures for which all component class ids
    5002 have had residuals created.  This list includes the detrend id,
    5003 iteration, exposure id, detrend type, and whether the exposure was
    5004 included in the stack for this iteration.
    5005     */
    5006 
    5007 
    5008     bool status = false;
    5009     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    5010     if (!status) {
    5011         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    5012         return false;
    5013     }
    5014 
    5015     psString query = pxDataGet("dettool_toresidexp.sql");
    5016     if (!query) {
    5017         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    5018         return false;
    5019     }
    5020 
    5021     // XXX does it make sense to accept any search params?
    5022 #if 0
    5023     if (config->where) {
    5024         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    5025         psStringAppend(&query, " AND %s", whereClause);
    5026         psFree(whereClause);
    5027     }
    5028 #endif
    5029 
    5030     // treat limit == 0 as "no limit"
    5031     if (limit) {
    5032         psString limitString = psDBGenerateLimitSQL(limit);
    5033         psStringAppend(&query, " %s", limitString);
    5034         psFree(limitString);
    5035     }
    5036 
    5037     if (!p_psDBRunQuery(config->dbh, query)) {
    5038         psError(PS_ERR_UNKNOWN, false, "database error");
    5039         psFree(query);
    5040         return false;
    5041     }
    5042     psFree(query);
    5043 
    5044     psArray *output = p_psDBFetchResult(config->dbh);
    5045     if (!output) {
    5046         psError(PS_ERR_UNKNOWN, false, "database error");
    5047         return false;
    5048     }
    5049     if (!psArrayLength(output)) {
    5050         psTrace("dettool", PS_LOG_INFO, "no rows found");
    5051         psFree(output);
    5052         return true;
    5053     }
    5054 
    5055     bool simple = false;
    5056     {
    5057         bool status = false;
    5058         simple = psMetadataLookupBool(&status, config->args, "-simple");
    5059         if (!status) {
    5060             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    5061             return false;
    5062         }
    5063     }
    5064 
    5065     // negative simple so the default is true
    5066     if (!ippdbPrintMetadatas(stdout, output, "detPendingResidExp", !simple)) {
    5067         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    5068         psFree(output);
    5069         return false;
    5070     }
    5071 
    5072     psFree(output);
    5073 
    5074     return true;
    5075 }
    5076 
    5077 static bool addresidexpMode(pxConfig *config)
    5078 {
    5079     PS_ASSERT_PTR_NON_NULL(config, false);
    5080 
    5081     /*
    5082 which returns a list of exposures for which all component class ids
    5083 have had residuals created.  This list includes the detrend id,
    5084 iteration, exposure id, detrend type, and whether the exposure was
    5085 included in the stack for this iteration.
    5086     */
    5087 
    5088 
    5089     // select detRun.det_id
    5090     // select detRun.iteration
    5091     // select detRun.det_type
    5092     // select detInputExp.exp_id
    5093     // select detInputExp.include
    5094     // by:
    5095     // find the current iteration bassed on det_id
    5096     // find all exp_ids in the current det_id/iteration from detInputExp
    5097     // compare to detInputExp.imfiles to derResidImfile by class_id
    5098     // and:
    5099     // detResidImfile.{det_id, iteration, exp_id} is not in detResidExp
    5100 
    5101     psString query = pxDataGet("dettool_toresidexp.sql");
    5102     if (!query) {
    5103         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    5104         return false;
    5105     }
    5106 
    5107     {
    5108         // build a query to search by det_id, iteration, exp_id
    5109         psMetadata *where = psMetadataAlloc();
    5110         bool status = false;
    5111         psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    5112         if (!status) {
    5113             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    5114             return false;
    5115         }
    5116         if (!det_id) {
    5117             psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    5118             return false;
    5119         }
    5120         if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", (psS64)atoll(det_id))) {
    5121             psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    5122             psFree(where);
    5123             psFree(query);
    5124             return false;
    5125         }
    5126 
    5127         psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    5128         if (!status) {
    5129             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");
    5130             psFree(where);
    5131             psFree(query);
    5132             return false;
    5133         }
    5134         // always set iteration
    5135         if (!psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", iteration)) {
    5136             psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
    5137             psFree(where);
    5138             psFree(query);
    5139             return false;
    5140         }
    5141         psString exp_id = psMetadataLookupStr(&status, config->args, "-exp_id");
    5142         if (!status) {
    5143             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_id");
    5144             psFree(where);
    5145             psFree(query);
    5146             return false;
    5147         }
    5148         if (exp_id) {
    5149             if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id", 0, "==", exp_id)) {
    5150                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    5151                 psFree(where);
    5152                 psFree(query);
    5153                 return false;
    5154             }
    5155         }
    5156 
    5157         // there's not
    5158         psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
    5159         psFree(where);
    5160         if (whereClause) {
    5161             psStringAppend(&query, " WHERE %s", whereClause);
    5162             psFree(whereClause);
    5163         }
    5164     }
    5165 
    5166     if (!p_psDBRunQuery(config->dbh, query)) {
    5167         psError(PS_ERR_UNKNOWN, false, "database error");
    5168         psFree(query);
    5169         return false;
    5170     }
    5171     psFree(query);
    5172 
    5173     psArray *output = p_psDBFetchResult(config->dbh);
    5174     if (!output) {
    5175         psError(PS_ERR_UNKNOWN, false, "database error");
    5176         return false;
    5177     }
    5178     if (!psArrayLength(output)) {
    5179         // XXX check psError here
    5180         psError(PS_ERR_UNKNOWN, false, "no detResidImfile rows found");
    5181         psFree(output);
    5182         return false;
    5183     }
    5184 
    5185     // start a transaction so it's all rows or nothing
    5186     if (!psDBTransaction(config->dbh)) {
    5187         psError(PS_ERR_UNKNOWN, false, "database error");
    5188         psFree(output);
    5189         return false;
    5190     }
    5191 
    5192     for (long i = 0; i < psArrayLength(output); i++) {
    5193         psMetadata *row = output->data[i];
    5194         // convert metadata into a detResidExp object
    5195         detResidExpRow *residExp = mdToDetResidExp(config, row);
    5196         if (!residExp) {
    5197             if (!psDBRollback(config->dbh)) {
    5198                 psError(PS_ERR_UNKNOWN, false, "database error");
    5199             }
    5200             psError(PS_ERR_UNKNOWN, false, "failed to convert metadata to detResidExp");
    5201             psFree(output);
    5202             return false;
    5203         }
    5204         // insert detResidExp object into the database
    5205         if (!detResidExpInsertObject(config->dbh, residExp)) {
    5206             if (!psDBRollback(config->dbh)) {
    5207                 psError(PS_ERR_UNKNOWN, false, "database error");
    5208             }
    5209             psError(PS_ERR_UNKNOWN, false, "database error");
    5210             psFree(residExp);
    5211             psFree(output);
    5212         }
    5213         psFree(residExp);
    5214     }
    5215 
    5216     psFree(output);
    5217 
    5218     if (!psDBCommit(config->dbh)) {
    5219         psError(PS_ERR_UNKNOWN, false, "database error");
    5220         return false;
    5221     }
    5222 
    5223     return true;
    5224 }
    5225 
    5226 static detResidExpRow *mdToDetResidExp(pxConfig *config, psMetadata *row)
    5227 {
    5228     PS_ASSERT_PTR_NON_NULL(config, NULL);
    5229     PS_ASSERT_PTR_NON_NULL(row, NULL);
    5230 
    5231     bool status = false;
    5232     // values from row
    5233     psS64 det_id = psMetadataLookupS64(&status, row, "det_id");
    5234     if (!status) {
    5235         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for det_id");
    5236         return false;
    5237     }
    5238     //if (isnan(det_id)) {
    5239     //  psError(PS_ERR_UNKNOWN, true, "det_id is required");
    5240     //  return false;
    5241     //}
    5242     psS32 iteration = psMetadataLookupS32(&status, row, "iteration");
    5243     if (!status) {
    5244         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for iteration");
    5245         return false;
    5246     }
    5247     psS64 exp_id = psMetadataLookupS64(&status, row, "exp_id");
    5248     if (!status) {
    5249         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for exp_id");
    5250         return false;
    5251     }
    5252     if (!exp_id) {
    5253         psError(PS_ERR_UNKNOWN, true, "exp_id is required");
    5254         return false;
    5255     }
    5256 
    5257     // values from config
    5258     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    5259     if (!status) {
    5260         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    5261         return false;
    5262     }
    5263     if (!recipe) {
    5264         psError(PS_ERR_UNKNOWN, true, "-recip is required");
    5265         return false;
    5266     }
    5267     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    5268     if (!status) {
    5269         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    5270         return false;
    5271     }
    5272     //if (isnan(bg)) {
    5273     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    5274     //  return false;
    5275     //}
    5276     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    5277     if (!status) {
    5278         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    5279         return false;
    5280     }
    5281     //if (isnan(bg_stdev)) {
    5282     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    5283     //  return false;
    5284     //}
    5285     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    5286     if (!status) {
    5287         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    5288         return false;
    5289     }
    5290     psF64 bg_skewness = psMetadataLookupF64(&status, config->args, "-bg_skewness");
    5291     if (!status) {
    5292         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_skewness");
    5293         return false;
    5294     }
    5295     psF64 bg_kurtosis = psMetadataLookupF64(&status, config->args, "-bg_kurtosis");
    5296     if (!status) {
    5297         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_kurtosis");
    5298         return false;
    5299     }
    5300     psF64 bin_stdev = psMetadataLookupF64(&status, config->args, "-bin_stdev");
    5301     if (!status) {
    5302         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bin_stdev");
    5303         return false;
    5304     }
    5305     // optional
    5306     psF64 fringe_0 = psMetadataLookupF64(&status, config->args, "-fringe_0");
    5307     if (!status) {
    5308         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_0");
    5309         return false;
    5310     }
    5311     psF64 fringe_1 = psMetadataLookupF64(&status, config->args, "-fringe_1");
    5312     if (!status) {
    5313         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_1");
    5314         return false;
    5315     }
    5316     psF64 fringe_2 = psMetadataLookupF64(&status, config->args, "-fringe_2");
    5317     if (!status) {
    5318         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_2");
    5319         return false;
    5320     }
    5321 
    5322     psF64 fringe_resid_0 = psMetadataLookupF64(&status, config->args, "-fringe_resid_0");
    5323     if (!status) {
    5324         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_resid_0");
    5325         return false;
    5326     }
    5327     psF64 fringe_resid_1 = psMetadataLookupF64(&status, config->args, "-fringe_resid_1");
    5328     if (!status) {
    5329         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_resid_1");
    5330         return false;
    5331     }
    5332     psF64 fringe_resid_2 = psMetadataLookupF64(&status, config->args, "-fringe_resid_2");
    5333     if (!status) {
    5334         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -fringe_resid_2");
    5335         return false;
    5336     }
    5337 
    5338     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    5339     if (!status) {
    5340         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    5341         return false;
    5342     }
    5343     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    5344     if (!status) {
    5345         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    5346         return false;
    5347     }
    5348     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    5349     if (!status) {
    5350         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    5351         return false;
    5352     }
    5353     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    5354     if (!status) {
    5355         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    5356         return false;
    5357     }
    5358     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    5359     if (!status) {
    5360         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    5361         return false;
    5362     }
    5363 
    5364     // optional
    5365     bool reject = psMetadataLookupBool(&status, config->args, "-reject");
    5366     if (!status) {
    5367         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -reject");
    5368         return false;
    5369     }
    5370     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    5371     if (!status) {
    5372         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    5373         return false;
    5374     }
    5375     if (!path_base) {
    5376         psError(PS_ERR_UNKNOWN, true, "-path_base is required");
    5377         return false;
    5378     }
    5379 
    5380     // default values
    5381     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    5382     if (!status) {
    5383         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    5384         return false;
    5385     }
    5386 
    5387     // create a new detResidImfileRow and insert it
    5388     return detResidExpRowAlloc(
    5389             det_id,
    5390             iteration,
    5391             exp_id,
    5392             recipe,
    5393             bg,
    5394             bg_stdev,
    5395             bg_mean_stdev,
    5396             bg_skewness,
    5397             bg_kurtosis,
    5398             bin_stdev,
    5399             fringe_0,
    5400             fringe_1,
    5401             fringe_2,
    5402             fringe_resid_0,
    5403             fringe_resid_1,
    5404             fringe_resid_2,
    5405             user_1,
    5406             user_2,
    5407             user_3,
    5408             user_4,
    5409             user_5,
    5410             path_base,
    5411             !reject,
    5412             code
    5413         );
    5414 }
    5415 
    5416 static bool residexpMode(pxConfig *config)
    5417 {
    5418     PS_ASSERT_PTR_NON_NULL(config, false);
    5419 
    5420     bool status = false;
    5421     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    5422     if (!status) {
    5423         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    5424         return false;
    5425     }
    5426 
    5427     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    5428     if (!status) {
    5429         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    5430         return false;
    5431     }
    5432 
    5433     psString query = psStringCopy(
    5434         "SELECT"
    5435         "   detRun.mode,"
    5436         "   detResidExp.*,"
    5437         "   detInputExp.include"
    5438         " FROM detRun"
    5439         " JOIN detInputExp"
    5440         "   USING(det_id, iteration)"
    5441         " JOIN detResidExp"
    5442         "   USING(det_id, iteration, exp_id)"
    5443         " WHERE"
    5444         "   detRun.state = 'run'"
    5445     );
    5446 
    5447     if (config->where) {
    5448         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detResidExp");
    5449         psStringAppend(&query, " AND %s", whereClause);
    5450         psFree(whereClause);
    5451     }
    5452 
    5453     if (faulted) {
    5454         // list only faulted rows
    5455         psStringAppend(&query, " %s", "AND detResidExp.fault != 0");
    5456     } else {
    5457         // don't list faulted rows
    5458         psStringAppend(&query, " %s", "AND detResidExp.fault = 0");
    5459     }
    5460 
    5461     // treat limit == 0 as "no limit"
    5462     if (limit) {
    5463         psString limitString = psDBGenerateLimitSQL(limit);
    5464         psStringAppend(&query, " %s", limitString);
    5465         psFree(limitString);
    5466     }
    5467 
    5468     if (!p_psDBRunQuery(config->dbh, query)) {
    5469         psError(PS_ERR_UNKNOWN, false, "database error");
    5470         psFree(query);
    5471         return false;
    5472     }
    5473     psFree(query);
    5474 
    5475     psArray *output = p_psDBFetchResult(config->dbh);
    5476     if (!output) {
    5477         psError(PS_ERR_UNKNOWN, false, "database error");
    5478         return false;
    5479     }
    5480     if (!psArrayLength(output)) {
    5481         psTrace("dettool", PS_LOG_INFO, "no rows found");
    5482         psFree(output);
    5483         return true;
    5484     }
    5485 
    5486     bool simple = false;
    5487     {
    5488         bool status = false;
    5489         simple = psMetadataLookupBool(&status, config->args, "-simple");
    5490         if (!status) {
    5491             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    5492             return false;
    5493         }
    5494     }
    5495 
    5496     // negative simple so the default is true
    5497     if (!ippdbPrintMetadatas(stdout, output, "detResidExp", !simple)) {
    5498         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    5499         psFree(output);
    5500         return false;
    5501     }
    5502 
    5503     psFree(output);
    5504 
    5505     return true;
    5506 }
    5507 
    5508 
    5509 static bool revertresidexpMode(pxConfig *config)
    5510 {
    5511     PS_ASSERT_PTR_NON_NULL(config, false);
    5512 
    5513     psString query = pxDataGet("dettool_revertresidexp.sql");
    5514     if (!query) {
    5515         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    5516         return false;
    5517     }
    5518 
    5519     if (config->where) {
    5520         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detResidExp");
    5521         psStringAppend(&query, " AND %s", whereClause);
    5522         psFree(whereClause);
    5523     }
    5524 
    5525     if (!p_psDBRunQuery(config->dbh, query)) {
    5526         psError(PS_ERR_UNKNOWN, false, "database error");
    5527         psFree(query);
    5528         return false;
    5529     }
    5530     psFree(query);
    5531 
    5532     if (psDBAffectedRows(config->dbh) < 1) {
    5533         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    5534         return false;
    5535     }
    5536 
    5537     return true;
    5538 }
    5539 
    5540 
    5541 static bool todetrunsummaryMode(pxConfig *config)
    5542 {
    5543     PS_ASSERT_PTR_NON_NULL(config, false);
    5544 
    5545     bool status = false;
    5546     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    5547     if (!status) {
    5548         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    5549         return false;
    5550     }
    5551 
    5552     /* which returns a list of detrend runs (with detrend id, iteration and
    5553      * detrend type) which have completed all residexps.
    5554      */
    5555 
    5556     psString query = pxDataGet("dettool_todetrunsummary.sql");
    5557     if (!query) {
    5558         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    5559         return false;
    5560     }
    5561 
    5562     // XXX does it make sense to accept any search params?
    5563 #if 0
    5564     if (config->where) {
    5565         psString whereClause = psDBGenerateWhereConditionSQL(config->where);
    5566         psStringAppend(&query, " AND %s", whereClause);
    5567         psFree(whereClause);
    5568     }
    5569 #endif
    5570 
    5571     // treat limit == 0 as "no limit"
    5572     if (limit) {
    5573         psString limitString = psDBGenerateLimitSQL(limit);
    5574         psStringAppend(&query, " %s", limitString);
    5575         psFree(limitString);
    5576     }
    5577 
    5578     if (!p_psDBRunQuery(config->dbh, query)) {
    5579         psError(PS_ERR_UNKNOWN, false, "database error");
    5580         psFree(query);
    5581         return false;
    5582     }
    5583     psFree(query);
    5584 
    5585     psArray *output = p_psDBFetchResult(config->dbh);
    5586     if (!output) {
    5587         psError(PS_ERR_UNKNOWN, false, "database error");
    5588         return false;
    5589     }
    5590     if (!psArrayLength(output)) {
    5591         psTrace("dettool", PS_LOG_INFO, "no rows found");
    5592         psFree(output);
    5593         return true;
    5594     }
    5595 
    5596     bool simple = false;
    5597     {
    5598         bool status = false;
    5599         simple = psMetadataLookupBool(&status, config->args, "-simple");
    5600         if (!status) {
    5601             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    5602             return false;
    5603         }
    5604     }
    5605 
    5606     // negative simple so the default is true
    5607     if (!ippdbPrintMetadatas(stdout, output, "detRejectExp", !simple)) {
    5608         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    5609         psFree(output);
    5610         return false;
    5611     }
    5612 
    5613     psFree(output);
    5614 
    5615     return true;
    5616 }
    5617 
    5618 static bool updateresidexpMode(pxConfig *config)
    5619 {
    5620     PS_ASSERT_PTR_NON_NULL(config, false);
    5621 
    5622     // build a query to search by det_id, iteration, exp_id
    5623     psMetadata *where = psMetadataAlloc();
    5624     bool status = false;
    5625     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    5626     if (!status) {
    5627         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    5628         psFree(where);
    5629         return false;
    5630     }
    5631     if (det_id) {
    5632         if (!psMetadataAddStr(where, PS_LIST_TAIL, "det_id", 0, "==", det_id)) {
    5633             psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    5634             psFree(where);
    5635             return false;
    5636         }
    5637     }
    5638     psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    5639     if (!status) {
    5640         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");
    5641         psFree(where);
    5642         return false;
    5643     }
    5644     // always where iteration
    5645     if (!psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", iteration)) {
    5646         psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
    5647         psFree(where);
    5648         return false;
    5649     }
    5650     psString exp_id = psMetadataLookupStr(&status, config->args, "-exp_id");
    5651     if (!status) {
    5652         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_id");
    5653         psFree(where);
    5654         return false;
    5655     }
    5656     if (exp_id) {
    5657         if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id", 0, "==", exp_id)) {
    5658             psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    5659             psFree(where);
    5660             return false;
    5661         }
    5662     }
    5663 
    5664     // find the values we're going to set
    5665     // copy everything but det_id, iteration, & exp_id from the args and
    5666     // remove the '-' prefix
    5667     psMetadata *set = psMetadataAlloc();
    5668     psString recipe = psMetadataLookupStr(&status, config->args, "-recip");
    5669     if (!status) {
    5670         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -recip");
    5671         psFree(set);
    5672         psFree(where);
    5673         return false;
    5674     }
    5675     if (recipe) {
    5676         if (!psMetadataAddStr(set, PS_LIST_TAIL, "recipe", 0, "==", recipe)) {
    5677             psError(PS_ERR_UNKNOWN, false, "failed to add item recipe");
    5678             psFree(set);
    5679             psFree(where);
    5680             return false;
    5681         }
    5682     }
    5683 
    5684     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    5685     if (!status) {
    5686         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    5687         psFree(set);
    5688         psFree(where);
    5689         return false;
    5690     }
    5691     if (!isnan(bg)) {
    5692         if (!psMetadataAddF64(set, PS_LIST_TAIL, "bg", 0, "==", bg)) {
    5693             psError(PS_ERR_UNKNOWN, false, "failed to add item bg");
    5694             psFree(set);
    5695             psFree(where);
    5696             return false;
    5697         }
    5698     }
    5699 
    5700     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    5701     if (!status) {
    5702         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    5703         psFree(set);
    5704         psFree(where);
    5705         return false;
    5706     }
    5707     if (!isnan(bg_stdev)) {
    5708         if (!psMetadataAddF64(set, PS_LIST_TAIL, "bg_stdev", 0, "==", bg_stdev)) {
    5709             psError(PS_ERR_UNKNOWN, false, "failed to add item bg_stdev");
    5710             psFree(set);
    5711             psFree(where);
    5712             return false;
    5713         }
    5714     }
    5715 
    5716     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    5717     if (!status) {
    5718         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    5719         psFree(set);
    5720         psFree(where);
    5721         return false;
    5722     }
    5723     if (!isnan(bg_mean_stdev)) {
    5724         if (!psMetadataAddF64(set, PS_LIST_TAIL, "bg_mean_stdev", 0, "==", bg_mean_stdev)) {
    5725             psError(PS_ERR_UNKNOWN, false, "failed to add item bg_mean_stdev");
    5726             psFree(set);
    5727             psFree(where);
    5728             return false;
    5729         }
    5730     }
    5731 
    5732     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    5733     if (!status) {
    5734         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    5735         psFree(set);
    5736         psFree(where);
    5737         return false;
    5738     }
    5739     if (path_base) {
    5740         if (!psMetadataAddStr(set, PS_LIST_TAIL, "path_base", 0, "==", path_base)) {
    5741             psError(PS_ERR_UNKNOWN, false, "failed to add item path_base");
    5742             psFree(set);
    5743             psFree(where);
    5744             return false;
    5745         }
    5746     }
    5747 
    5748     bool reject = psMetadataLookupBool(&status, config->args, "-reject");
    5749     if (!status) {
    5750         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -reject");
    5751         psFree(set);
    5752         psFree(where);
    5753         return false;
    5754     }
    5755     if (!psMetadataAddBool(set, PS_LIST_TAIL, "accept", 0, "==", !reject)) {
    5756         psError(PS_ERR_UNKNOWN, false, "failed to add item accept");
    5757         psFree(set);
    5758         psFree(where);
    5759         return false;
    5760     }
    5761 
    5762     long changed = psDBUpdateRows(config->dbh, "detResidExp", where, set);
    5763     psFree(set);
    5764     psFree(where);
    5765 
    5766     if (changed < 0) {
    5767         psError(PS_ERR_UNKNOWN, false, "no rows were updated");
    5768         return false;
    5769     }
    5770 
    5771     return true;
    5772 }
    5773 
    5774 static bool adddetrunsummaryMode(pxConfig *config)
    5775 {
    5776     PS_ASSERT_PTR_NON_NULL(config, false);
    5777 
    5778     // required
    5779     bool status = false;
    5780     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    5781     if (!status) {
    5782         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    5783         return false;
    5784     }
    5785     if (!det_id) {
    5786         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    5787         return false;
    5788     }
    5789 
    5790     // optional
    5791     bool again = psMetadataLookupBool(&status, config->args, "-again");
    5792     if (!status) {
    5793         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -again");
    5794         return false;
    5795     }
    5796 
    5797     psString query = pxDataGet("dettool_find_completed_runs.sql");
    5798     if (!query) {
    5799         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    5800         return false;
    5801     }
    5802 
    5803     {
    5804         // build a query to search by det_id, iteration, exp_id
    5805         psMetadata *where = psMetadataAlloc();
    5806         bool status = false;
    5807         if (det_id) {
    5808             if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", (psS64)atoll(det_id))) {
    5809                 psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    5810                 psFree(where);
    5811                 psFree(query);
    5812                 return false;
    5813             }
    5814         }
    5815         psS32 iteration = psMetadataLookupS32(&status, config->args, "-iteration");
    5816         if (!status) {
    5817             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -iteration");
    5818             psFree(where);
    5819             psFree(query);
    5820             return false;
    5821         }
    5822         // always set iteration
    5823         if (!psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", iteration)) {
    5824             psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
    5825             psFree(where);
    5826             psFree(query);
    5827             return false;
    5828         }
    5829 
    5830         // there's not
    5831         psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
    5832         psFree(where);
    5833         if (whereClause) {
    5834             psStringAppend(&query, " WHERE %s", whereClause);
    5835             psFree(whereClause);
    5836         }
    5837     }
    5838 
    5839     if (!p_psDBRunQuery(config->dbh, query)) {
    5840         psError(PS_ERR_UNKNOWN, false, "database error");
    5841         psFree(query);
    5842         return false;
    5843     }
    5844     psFree(query);
    5845 
    5846     psArray *output = p_psDBFetchResult(config->dbh);
    5847     if (!output) {
    5848         psError(PS_ERR_UNKNOWN, false, "database error");
    5849         return false;
    5850     }
    5851     if (!psArrayLength(output)) {
    5852         psTrace("dettool", PS_LOG_INFO, "no rows found");
    5853         psFree(output);
    5854         return true;
    5855     }
    5856 
    5857     // start a transaction so it's all rows or nothing
    5858     if (!psDBTransaction(config->dbh)) {
    5859         psError(PS_ERR_UNKNOWN, false, "database error");
    5860         psFree(output);
    5861         return false;
    5862     }
    5863 
    5864     for (long i = 0; i < psArrayLength(output); i++) {
    5865         psMetadata *row = output->data[i];
    5866         // convert metadata into a detResidExp object
    5867         detRunSummaryRow *runSummary = mdToDetRunSummary(config, row);
    5868         if (!runSummary) {
    5869             if (!psDBRollback(config->dbh)) {
    5870                 psError(PS_ERR_UNKNOWN, false, "database error");
    5871             }
    5872             psError(PS_ERR_UNKNOWN, false, "failed to convert metadata to detResidExp");
    5873             psFree(output);
    5874             return false;
    5875         }
    5876         // insert detResidExp object into the database
    5877         if (!detRunSummaryInsertObject(config->dbh, runSummary)) {
    5878             if (!psDBRollback(config->dbh)) {
    5879                 psError(PS_ERR_UNKNOWN, false, "database error");
    5880             }
    5881             psError(PS_ERR_UNKNOWN, false, "database error");
    5882             psFree(runSummary);
    5883             psFree(output);
    5884             return false;
    5885         }
    5886         psFree(runSummary);
    5887     }
    5888 
    5889     psFree(output);
    5890 
    5891     // XXX this logic does not deal with the case of -code being set
    5892     if (again) {
    5893         if (!startNewIteration(config, (psS64)atoll(det_id))) {
    5894             if (!psDBRollback(config->dbh)) {
    5895                 psError(PS_ERR_UNKNOWN, false, "database error");
    5896             }
    5897             psError(PS_ERR_UNKNOWN, false, "failed to start new iteration");
    5898             return false;
    5899         }
    5900     } else {
    5901         // set detRun.state to stop
    5902         if (!setDetRunState(config, (psS64)atoll(det_id), "stop")) {
    5903             if (!psDBRollback(config->dbh)) {
    5904                 psError(PS_ERR_UNKNOWN, false, "database error");
    5905             }
    5906             psError(PS_ERR_UNKNOWN, false, "failed to set detRun.state");
    5907             return false;
    5908         }
    5909     }
    5910 
    5911     if (!psDBCommit(config->dbh)) {
    5912         psError(PS_ERR_UNKNOWN, false, "database error");
    5913         return false;
    5914     }
    5915     return true;
    5916 }
    5917 
    5918 static detRunSummaryRow *mdToDetRunSummary(pxConfig *config, psMetadata *row)
    5919 {
    5920     PS_ASSERT_PTR_NON_NULL(config, false);
    5921 
    5922     bool status = false;
    5923     // from row
    5924     psS64 det_id = psMetadataLookupS64(&status, row, "det_id");
    5925     if (!status) {
    5926         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for det_id");
    5927         return false;
    5928     }
    5929     psS32 iteration = psMetadataLookupS32(&status, row, "iteration");
    5930     if (!status) {
    5931         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for iteration");
    5932         return false;
    5933     }
    5934 
    5935     // from config->args
    5936     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    5937     if (!status) {
    5938         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    5939         return NULL;
    5940     }
    5941     //if (isnan(bg)) {
    5942     //  psError(PS_ERR_UNKNOWN, true, "-bg is required");
    5943     //  return NULL;
    5944     //}
    5945     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    5946     if (!status) {
    5947         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    5948         return NULL;
    5949     }
    5950     //if (isnan(bg_stdev)) {
    5951     //  psError(PS_ERR_UNKNOWN, true, "-bg_stdev is required");
    5952     //  return NULL;
    5953     //}
    5954     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    5955     if (!status) {
    5956         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    5957         return NULL;
    5958     }
    5959     // optional
    5960     bool accept = psMetadataLookupBool(&status, config->args, "-accept");
    5961     if (!status) {
    5962         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -accept");
    5963         return NULL;
    5964     }
    5965     // default values
    5966     psS16 code = psMetadataLookupS16(&status, config->args, "-code");
    5967     if (!status) {
    5968         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -code");
    5969         return false;
    5970     }
    5971 
    5972     return detRunSummaryRowAlloc(
    5973             det_id,
    5974             iteration,
    5975             bg,
    5976             bg_stdev,
    5977             bg_mean_stdev,
    5978             accept,
    5979             code
    5980         );
    5981 }
    5982 
    5983 static bool detrunsummaryMode(pxConfig *config)
    5984 {
    5985     PS_ASSERT_PTR_NON_NULL(config, false);
    5986 
    5987     bool status = false;
    5988     psU64 limit = psMetadataLookupU64(&status, config->args, "-limit");
    5989     if (!status) {
    5990         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -limit");
    5991         return false;
    5992     }
    5993 
    5994     bool faulted = psMetadataLookupU64(&status, config->args, "-faulted");
    5995     if (!status) {
    5996         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -faulted");
    5997         return false;
    5998     }
    5999 
    6000     psString query = psStringCopy(
    6001         "SELECT DISTINCT\n"
    6002         "   detRunSummary.*,\n"
    6003         "   detRun.det_type,\n"
    6004         "   detRun.mode\n"
    6005         " FROM detRun\n"
    6006         " JOIN detRunSummary\n"
    6007         "   USING(det_id, iteration)\n"
    6008         " WHERE\n"
    6009         "   detRun.state = 'run'\n"
    6010         );
    6011 
    6012     if (config->where) {
    6013         psString whereClause = psDBGenerateWhereConditionSQL(config->where, NULL);
    6014         psStringAppend(&query, " AND %s", whereClause);
    6015         psFree(whereClause);
    6016     }
    6017 
    6018     if (faulted) {
    6019         // list only faulted rows
    6020         psStringAppend(&query, " %s", "AND detRunSummary.fault != 0");
    6021     } else {
    6022         // don't list faulted rows
    6023         psStringAppend(&query, " %s", "AND detRunSummary.fault = 0");
    6024     }
    6025 
    6026     // treat limit == 0 as "no limit"
    6027     if (limit) {
    6028         psString limitString = psDBGenerateLimitSQL(limit);
    6029         psStringAppend(&query, " %s", limitString);
    6030         psFree(limitString);
    6031     }
    6032 
    6033     if (!p_psDBRunQuery(config->dbh, query)) {
    6034         psError(PS_ERR_UNKNOWN, false, "database error");
    6035         psFree(query);
    6036         return false;
    6037     }
    6038     psFree(query);
    6039 
    6040     psArray *output = p_psDBFetchResult(config->dbh);
    6041     if (!output) {
    6042         psError(PS_ERR_UNKNOWN, false, "database error");
    6043         return false;
    6044     }
    6045     if (!psArrayLength(output)) {
    6046         psTrace("dettool", PS_LOG_INFO, "no rows found");
    6047         psFree(output);
    6048         return true;
    6049     }
    6050 
    6051     bool simple = false;
    6052     {
    6053         bool status = false;
    6054         simple = psMetadataLookupBool(&status, config->args, "-simple");
    6055         if (!status) {
    6056             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    6057             return false;
    6058         }
    6059     }
    6060 
    6061     // negative simple so the default is true
    6062     if (!ippdbPrintMetadatas(stdout, output, "rawDetrendImfile", !simple)) {
    6063         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    6064         psFree(output);
    6065         return false;
    6066     }
    6067 
    6068     psFree(output);
    6069 
    6070     return true;
    6071 }
    6072 
    6073 
    6074 static bool revertdetrunsummaryMode(pxConfig *config)
    6075 {
    6076     PS_ASSERT_PTR_NON_NULL(config, false);
    6077 
    6078     psString query = pxDataGet("dettool_revertdetrunsummary.sql");
    6079     if (!query) {
    6080         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    6081         return false;
    6082     }
    6083 
    6084     if (config->where) {
    6085         psString whereClause = psDBGenerateWhereConditionSQL(config->where, "detRunSummary");
    6086         psStringAppend(&query, " AND %s", whereClause);
    6087         psFree(whereClause);
    6088     }
    6089 
    6090     if (!p_psDBRunQuery(config->dbh, query)) {
    6091         psError(PS_ERR_UNKNOWN, false, "database error");
    6092         psFree(query);
    6093         return false;
    6094     }
    6095     psFree(query);
    6096 
    6097     if (psDBAffectedRows(config->dbh) < 1) {
    6098         psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
    6099         return false;
    6100     }
    6101 
    6102     return true;
    6103 }
    6104 
    6105 
    6106 static bool updatedetrunMode(pxConfig *config)
    6107 {
    6108     PS_ASSERT_PTR_NON_NULL(config, false);
    6109 
    6110     // det_id is required
    6111     // XXX this isn't strictly nessicary but not having the det_id complicates
    6112     // incrementing the iteration number
    6113     bool status = false;
    6114     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    6115     if (!status) {
    6116         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    6117         return false;
    6118     }
    6119     if (!det_id) {
    6120         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    6121         return false;
    6122     }
    6123     // either -rerun or -state must be specified
    6124     bool again = psMetadataLookupBool(&status, config->args, "-again");
    6125     if (!status) {
    6126         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -again");
    6127         return false;
    6128     }
    6129     psString state = psMetadataLookupStr(&status, config->args, "-state");
    6130     if (!status) {
    6131         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -state");
    6132         return false;
    6133     }
    6134     if (!(again || state)) {
    6135         psError(PS_ERR_UNKNOWN, true, "either -again or -state must be specified");
    6136         return false;
    6137     }
    6138     if (again && state) {
    6139         psError(PS_ERR_UNKNOWN, true, "either -again or -state must be specified");
    6140         return false;
    6141     }
    6142 
    6143     if (state) {
    6144         // set detRun.state to state
    6145         return setDetRunState(config, (psS64)atoll(det_id), state);
    6146     }
    6147 
    6148     // else
    6149     // -again
    6150     if (!startNewIteration(config, (psS64)atoll(det_id))) {
    6151         psError(PS_ERR_UNKNOWN, false, "failed to start new iteration");
    6152         return false;
    6153     }
    6154 
    6155     return true;
    6156 }
    6157 
    6158 static bool startNewIteration(pxConfig *config, psS64 det_id)
    6159 {
    6160     PS_ASSERT_PTR_NON_NULL(config, false);
    6161 
    6162     psString query = pxDataGet("dettool_start_new_iteration.sql");
    6163     if (!query) {
    6164         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
    6165         return false;
    6166     }
    6167 
    6168     // XXX this query was not restricted by det_id, resulting
    6169     // in an inconsistent UPDATE below.  I added this AND clause
    6170     // though there may be a cleaner method (EAM 2006.10.08)
    6171     psStringAppend(&query, " WHERE det_id = %" PRId64 , det_id);
    6172 
    6173     if (!p_psDBRunQuery(config->dbh, query)) {
    6174         psError(PS_ERR_UNKNOWN, false, "database error");
    6175         psFree(query);
    6176         return false;
    6177     }
    6178     psFree(query);
    6179 
    6180     psArray *output = p_psDBFetchResult(config->dbh);
    6181     if (!output) {
    6182         psError(PS_ERR_UNKNOWN, false, "database error");
    6183         return false;
    6184     }
    6185     if (!psArrayLength(output)) {
    6186         psError(PS_ERR_UNKNOWN, false, "det_id %" PRId64 " not found", det_id);
    6187         psFree(output);
    6188         return false;
    6189     }
    6190 
    6191     // start a transaction so we don't end up with an incremented iteration
    6192     // count but no detInputExps
    6193     if (!psDBTransaction(config->dbh)) {
    6194         psError(PS_ERR_UNKNOWN, false, "database error");
    6195         psFree(output);
    6196         return false;
    6197     }
    6198 
    6199     // up the detRuns iteration count for for the single det_id we are
    6200     // operating on
    6201     // XXX this will have to changed in order to support multiple det_ids with
    6202     // a single invocation of this functions
    6203     psS32 newIteration = incrementIteration(config, det_id);
    6204     if (!newIteration) {
    6205         // rollback
    6206         if (!psDBRollback(config->dbh)) {
    6207             psError(PS_ERR_UNKNOWN, false, "database error");
    6208         }
    6209         psFree(output);
    6210         return false;
    6211     }
    6212 
    6213     for (long i = 0; i < psArrayLength(output); i++) {
    6214         psMetadata *row = output->data[i];
    6215         bool status = false;
    6216         psS64 exp_id = psMetadataLookupS64(&status, row, "exp_id");
    6217         if (!status) {
    6218             // rollback
    6219             if (!psDBRollback(config->dbh)) {
    6220                 psError(PS_ERR_UNKNOWN, false, "database error");
    6221             }
    6222             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for exp_id");
    6223             psFree(output);
    6224             return false;
    6225         }
    6226         bool accept = psMetadataLookupBool(&status, row, "accept");
    6227         if (!status) {
    6228             // rollback
    6229             if (!psDBRollback(config->dbh)) {
    6230                 psError(PS_ERR_UNKNOWN, false, "database error");
    6231             }
    6232             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for accept");
    6233             psFree(output);
    6234             return false;
    6235         }
    6236 
    6237         // detResidExp.include is used to set detInputExp.include
    6238         if (!detInputExpInsert(
    6239                     config->dbh,
    6240                     det_id,
    6241                     newIteration,
    6242                     exp_id,
    6243                     accept
    6244                 )
    6245         ) {
    6246             // rollback
    6247             if (!psDBRollback(config->dbh)) {
    6248                 psError(PS_ERR_UNKNOWN, false, "database error");
    6249             }
    6250             psError(PS_ERR_UNKNOWN, false, "database error");
    6251             psFree(output);
    6252             return false;
    6253         }
    6254     }
    6255 
    6256     psFree(output);
    6257 
    6258     // point of no return for det_id creation
    6259     if (!psDBCommit(config->dbh)) {
    6260         psError(PS_ERR_UNKNOWN, false, "database error");
    6261         return false;
    6262     }
    6263 
    6264     return true;
    6265 }
    6266 
    6267 static bool rerunMode(pxConfig *config)
    6268 {
    6269     PS_ASSERT_PTR_NON_NULL(config, false);
    6270 
    6271     // det_id is required
    6272     bool status = false;
    6273     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    6274     if (!status) {
    6275         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    6276         return false;
    6277     }
    6278     if (!det_id) {
    6279         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    6280         return false;
    6281     }
    6282 
    6283     // we have to support multipe exp_ids
    6284     psMetadataItem *item = psMetadataLookup(config->args, "-exp_id");
    6285     if (!item) {
    6286         // this shouldn't actually happen when using psArgs
    6287         psError(PS_ERR_UNKNOWN, true, "-exp_id is required");
    6288         return false;
    6289     }
    6290 
    6291     psList *exp_id_list = item->data.list;
    6292     psMetadata *where = psMetadataAlloc();
    6293     // make sure that -exp_id was parsed correctly
    6294     // XXX this can be removed someday
    6295     if (item->type == PS_DATA_METADATA_MULTI) {
    6296         psListIterator *iter = psListIteratorAlloc(item->data.list, 0, false);
    6297         psMetadataItem *mItem = NULL;
    6298         while ((mItem = psListGetAndIncrement(iter))) {
    6299             psString exp_id = mItem->data.V;
    6300             // if exp_id is NULL then it means that -exp_id has not been
    6301             // specified
    6302             if (!exp_id) {
    6303                 psError(PS_ERR_UNKNOWN, true,
    6304                         "at least one -exp_id is required");
    6305                 psFree(where);
    6306                 return false;
    6307             }
    6308 
    6309             if (!psMetadataAddStr(where, PS_LIST_TAIL, "exp_id",
    6310                         PS_META_DUPLICATE_OK, "==", exp_id)) {
    6311                 psError(PS_ERR_UNKNOWN, false, "failed to add item exp_id");
    6312                 psFree(iter);
    6313                 psFree(where);
    6314                 return false;
    6315             }
    6316         }
    6317         psFree(iter);
    6318     } else {
    6319         psAbort("-exp_id was not parsed correctly (this should not happen");
    6320     }
    6321 
    6322     // check that the specified exp_ids actually exist in the iteration zero
    6323     // detInputExp set
    6324 
    6325     // add the det_id & iteration == 0 to the where clause
    6326     if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==",
    6327                 (psS64)atoll(det_id))) {
    6328         psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    6329         psFree(where);
    6330         return false;
    6331     }
    6332     if (!psMetadataAddS32(where, PS_LIST_TAIL, "iteration", 0, "==", 0)) {
    6333         psError(PS_ERR_UNKNOWN, false, "failed to add item iteration");
    6334         psFree(where);
    6335         return false;
    6336     }
    6337 
    6338     psArray *detrendExps = detInputExpSelectRowObjects(config->dbh, where, 0);
    6339     psFree(where);
    6340     if (!detrendExps) {
    6341         psError(PS_ERR_UNKNOWN, false, "no rawExp rows found");
    6342         psFree(where);
    6343         return false;
    6344     }
    6345 
    6346     // build a hash for the valid exp_ids
    6347     psHash *valid_exp_ids = psHashAlloc(psArrayLength(detrendExps));
    6348     for (long i = 0; i < psArrayLength(detrendExps); i++) {
    6349         psString exp_idStr = psDBIntToString(((detInputExpRow *)detrendExps->data[i])->exp_id);
    6350         psHashAdd(valid_exp_ids, exp_idStr, detrendExps->data[i]);
    6351         psFree(exp_idStr);
    6352     }
    6353     psFree(detrendExps);
    6354 
    6355     // start a transaction so we don't end up with an incremented iteration
    6356     // count but no detInputExps
    6357     if (!psDBTransaction(config->dbh)) {
    6358         psError(PS_ERR_UNKNOWN, false, "database error");
    6359         psFree(valid_exp_ids);
    6360         return false;
    6361     }
    6362 
    6363     // up the detRuns iteration count
    6364     psS32 newIteration = incrementIteration(config, (psS64)atoll(det_id));
    6365     if (!newIteration) {
    6366         // rollback
    6367         if (!psDBRollback(config->dbh)) {
    6368             psError(PS_ERR_UNKNOWN, false, "database error");
    6369         }
    6370         psFree(valid_exp_ids);
    6371         return false;
    6372     }
    6373 
    6374     // check exp_ids and build up an array of new detInputExp rows at the same
    6375     // time
    6376     psListIterator *iter = psListIteratorAlloc(exp_id_list, 0, false);
    6377     psMetadataItem *mItem = NULL;
    6378     psArray *newInputExps = psArrayAllocEmpty(psListLength(exp_id_list));
    6379     while ((mItem = psListGetAndIncrement(iter))) {
    6380         detInputExpRow *inputExp = psHashLookup(valid_exp_ids,
    6381                 (char *)mItem->data.V);
    6382         if (!inputExp) {
    6383             // rollback
    6384             if (!psDBRollback(config->dbh)) {
    6385                 psError(PS_ERR_UNKNOWN, false, "database error");
    6386             }
    6387             // invalid exp_id
    6388             psError(PS_ERR_UNKNOWN, false, "exp_id %s is invalid for det_id %s",
    6389                     (char *)mItem->data.V, det_id);
    6390             psFree(iter);
    6391             psFree(valid_exp_ids);
    6392             return false;
    6393         }
    6394         detInputExpRow *newInputExp = detInputExpRowAlloc(
    6395             (psS64)atoll(det_id),
    6396             newIteration,
    6397             inputExp->exp_id,
    6398             true   // use
    6399         );
    6400         psArrayAdd(newInputExps, 0, newInputExp);
    6401         psFree(newInputExp);
    6402     }
    6403     psFree(iter);
    6404     psFree(valid_exp_ids);
    6405 
    6406     for (long i = 0; i < psArrayLength(newInputExps); i++) {
    6407         if (!detInputExpInsertObject(config->dbh, newInputExps->data[i])) {
    6408             psError(PS_ERR_UNKNOWN, false, "database error");
    6409             // rollback
    6410             if (!psDBRollback(config->dbh)) {
    6411                 psError(PS_ERR_UNKNOWN, false, "database error");
    6412             }
    6413             psFree(newInputExps);
    6414             return false;
    6415         }
    6416     }
    6417     psFree(newInputExps);
    6418 
    6419     // point of no return for det_id creation
    6420     if (!psDBCommit(config->dbh)) {
    6421         psError(PS_ERR_UNKNOWN, false, "database error");
    6422         return false;
    6423     }
    6424 
    6425     return true;
    6426 }
    6427 
    6428 static bool register_detrendMode(pxConfig *config)
    6429 {
    6430     PS_ASSERT_PTR_NON_NULL(config, false);
    6431 
    6432     // required options
    6433     bool status = false;
    6434     psString det_type = psMetadataLookupStr(&status, config->args, "-det_type");
    6435     if (!status) {
    6436         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_type");
    6437         return false;
    6438     }
    6439     if (!det_type) {
    6440         psError(PS_ERR_UNKNOWN, true, "-det_type is required");
    6441         return false;
    6442     }
    6443 
    6444     psString filelevel = psMetadataLookupStr(&status, config->args, "-filelevel");
    6445     if (!status) {
    6446         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filelevel");
    6447         return false;
    6448     }
    6449     if (!filelevel) {
    6450         psError(PS_ERR_UNKNOWN, true, "-filelevel is required");
    6451         return false;
    6452     }
    6453 
    6454     psString workdir = psMetadataLookupStr(&status, config->args, "-workdir");
    6455     if (!status) {
    6456         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -workdir");
    6457         return false;
    6458     }
    6459 
    6460     psString camera = psMetadataLookupStr(&status, config->args, "-inst");
    6461     if (!status) {
    6462         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -inst");
    6463         return false;
    6464     }
    6465 
    6466     psString telescope = psMetadataLookupStr(&status, config->args, "-telescope");
    6467     if (!status) {
    6468         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -telescope");
    6469         return false;
    6470     }
    6471 
    6472     psString exp_type = psMetadataLookupStr(&status, config->args, "-exp_type");
    6473     if (!status) {
    6474         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_type");
    6475         return false;
    6476     }
    6477 
    6478     psString filter = psMetadataLookupStr(&status, config->args, "-filter");
    6479     if (!status) {
    6480         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -filter");
    6481         return false;
    6482     }
    6483 
    6484     psF32 airmass_min = psMetadataLookupF32(&status, config->args, "-airmass_min");
    6485     if (!status) {
    6486         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -airmass_min");
    6487         return false;
    6488     }
    6489 
    6490     psF32 airmass_max = psMetadataLookupF32(&status, config->args, "-airmass_max");
    6491     if (!status) {
    6492         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -airmass_max");
    6493         return false;
    6494     }
    6495 
    6496     psF32 exp_time_min = psMetadataLookupF32(&status, config->args, "-exp_time_min");
    6497     if (!status) {
    6498         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_time_min");
    6499         return false;
    6500     }
    6501 
    6502     psF32 exp_time_max = psMetadataLookupF32(&status, config->args, "-exp_time_max");
    6503     if (!status) {
    6504         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -exp_time_max");
    6505         return false;
    6506     }
    6507 
    6508     psF32 ccd_temp_min = psMetadataLookupF32(&status, config->args, "-ccd_temp_min");
    6509     if (!status) {
    6510         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -ccd_temp_min");
    6511         return false;
    6512     }
    6513 
    6514     psF32 ccd_temp_max = psMetadataLookupF32(&status, config->args, "-ccd_temp_max");
    6515     if (!status) {
    6516         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -ccd_temp_max");
    6517         return false;
    6518     }
    6519 
    6520     psF64 posang_min = psMetadataLookupF32(&status, config->args, "-posang_min");
    6521     if (!status) {
    6522         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -posang_min");
    6523         return false;
    6524     }
    6525 
    6526     psF64 posang_max = psMetadataLookupF32(&status, config->args, "-posang_max");
    6527     if (!status) {
    6528         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -posang_max");
    6529         return false;
    6530     }
    6531 
    6532     psF64 solang_min = psMetadataLookupF32(&status, config->args, "-solang_min");
    6533     if (!status) {
    6534         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -solang_min");
    6535         return false;
    6536     }
    6537 
    6538     psF64 solang_max = psMetadataLookupF32(&status, config->args, "-solang_max");
    6539     if (!status) {
    6540         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -solang_max");
    6541         return false;
    6542     }
    6543 
    6544     psTime *registered = NULL;
    6545     {
    6546         psString registeredStr = psMetadataLookupStr(&status, config->args, "-registered");
    6547         if (!status) {
    6548             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -registered");
    6549             return false;
    6550         }
    6551         // pass through NULL as this is an optional field
    6552         if (registeredStr) {
    6553             registered = psTimeFromISO(registeredStr, PS_TIME_UTC);
    6554         } else {
    6555             registered = NULL;
    6556         }
    6557     }
    6558 
    6559     psTime *time_begin = NULL;
    6560     {
    6561         psString time_beginStr = psMetadataLookupStr(&status, config->args, "-time_begin");
    6562         if (!status) {
    6563             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -time_begin");
    6564             return false;
    6565         }
    6566         // pass through NULL as this is an optional field
    6567         if (time_beginStr) {
    6568             time_begin = psTimeFromISO(time_beginStr, PS_TIME_UTC);
    6569         } else {
    6570             time_begin = NULL;
    6571         }
    6572     }
    6573 
    6574     psTime *time_end = NULL;
    6575     {
    6576         psString time_endStr = psMetadataLookupStr(&status, config->args, "-time_end");
    6577         if (!status) {
    6578             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -time_end");
    6579             return false;
    6580         }
    6581         // pass through NULL as this is an optional field
    6582         if (time_endStr) {
    6583             time_end = psTimeFromISO(time_endStr, PS_TIME_UTC);
    6584         } else {
    6585             time_end = NULL;
    6586         }
    6587     }
    6588 
    6589     psTime *use_begin = NULL;
    6590     {
    6591         psString use_beginStr = psMetadataLookupStr(&status, config->args, "-use_begin");
    6592         if (!status) {
    6593             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -use_begin");
    6594             return false;
    6595         }
    6596         // pass through NULL as this is an optional field
    6597         if (use_beginStr) {
    6598             use_begin = psTimeFromISO(use_beginStr, PS_TIME_UTC);
    6599         } else {
    6600             use_begin = NULL;
    6601         }
    6602     }
    6603 
    6604     psTime *use_end = NULL;
    6605     {
    6606         psString use_endStr = psMetadataLookupStr(&status, config->args, "-use_end");
    6607         if (!status) {
    6608             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -use_end");
    6609             return false;
    6610         }
    6611         // pass through NULL as this is an optional field
    6612         if (use_endStr) {
    6613             use_end = psTimeFromISO(use_endStr, PS_TIME_UTC);
    6614         } else {
    6615             use_end = NULL;
    6616         }
    6617     }
    6618 
    6619     psString parent = psMetadataLookupStr(&status, config->args, "-parent");
    6620     if (!status) {
    6621         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -parent");
    6622         return false;
    6623     }
    6624 
    6625     psString label = psMetadataLookupStr(&status, config->args, "-label");
    6626     if (!status) {
    6627         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -label");
    6628         return false;
    6629     }
    6630 
    6631     if (!psDBTransaction(config->dbh)) {
    6632         psError(PS_ERR_UNKNOWN, false, "database error");
    6633         return false;
    6634     }
    6635 
    6636     if (!detRunInsert(config->dbh,
    6637                       0,            // det_id
    6638                       0,            // the iteration is fixed at 0
    6639                       det_type,
    6640                       "register",   // mode
    6641                       "register",   // state
    6642                       filelevel,
    6643                       workdir,
    6644                       camera,
    6645                       telescope,
    6646                       exp_type,
    6647                       NULL,
    6648                       filter,
    6649                       airmass_min,
    6650                       airmass_max,
    6651                       exp_time_min,
    6652                       exp_time_max,
    6653                       ccd_temp_min,
    6654                       ccd_temp_max,
    6655                       posang_min,
    6656                       posang_max,
    6657                       registered,
    6658                       time_begin,
    6659                       time_end,
    6660                       use_begin,
    6661                       use_end,
    6662                       solang_min,
    6663                       solang_max,
    6664                       label,      // label
    6665                       parent ? (psS64)atoll(parent) : 0
    6666             )) {
    6667         psError(PS_ERR_UNKNOWN, false, "database error");
    6668         // rollback
    6669         if (!psDBRollback(config->dbh)) {
    6670             psError(PS_ERR_UNKNOWN, false, "database error");
    6671         }
    6672         psFree(registered);
    6673         return false;
    6674     }
    6675     psFree(registered);
    6676 
    6677     // print the new detRun
    6678     psS64 det_id = psDBLastInsertID(config->dbh);
    6679 
    6680     if (!psDBCommit(config->dbh)) {
    6681         psError(PS_ERR_UNKNOWN, false, "database error");
    6682         return false;
    6683     }
    6684 
    6685     psArray *detRuns = NULL;
    6686     {
    6687         psMetadata *where = psMetadataAlloc();
    6688         psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id);
    6689         detRuns = psDBSelectRows(config->dbh, "detRun", where, 0);
    6690         psFree(where);
    6691     }
    6692     if (!detRuns) {
    6693         psError(PS_ERR_UNKNOWN, false, "can't find the detRun we just created");
    6694         return false;
    6695     }
    6696     // sanity check results
    6697     if (psArrayLength(detRuns) != 1) {
    6698         psAbort("found more then one detRun matching det_id %" PRId64 "(this should not happen)", det_id);
    6699         return false;
    6700     }
    6701 
    6702     if (!convertIdToStr(detRuns)) {
    6703         psError(PS_ERR_UNKNOWN, false, "failed to convert id fields into a strings");
    6704         psFree(detRuns);
    6705         return false;
    6706     }
    6707 
    6708     bool simple = false;
    6709     {
    6710         bool status = false;
    6711         simple = psMetadataLookupBool(&status, config->args, "-simple");
    6712         if (!status) {
    6713             psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -simple");
    6714             return false;
    6715         }
    6716     }
    6717 
    6718     // negative simple so the default is true
    6719     if (!ippdbPrintMetadatas(stdout, detRuns, "detRun", !simple)) {
    6720         psError(PS_ERR_UNKNOWN, false, "failed to print array");
    6721         psFree(detRuns);
    6722         return false;
    6723     }
    6724     psFree(detRuns);
    6725 
    6726     return true;
    6727 }
    6728 
    6729 static bool register_detrend_imfileMode(pxConfig *config)
    6730 {
    6731     PS_ASSERT_PTR_NON_NULL(config, false);
    6732 
    6733     // required options
    6734     bool status = false;
    6735     psString det_id = psMetadataLookupStr(&status, config->args, "-det_id");
    6736     if (!status) {
    6737         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -det_id");
    6738         return false;
    6739     }
    6740     if (!det_id) {
    6741         psError(PS_ERR_UNKNOWN, true, "-det_id is required");
    6742         return false;
    6743     }
    6744 
    6745     psString class_id = psMetadataLookupStr(&status, config->args, "-class_id");
    6746     if (!status) {
    6747         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -class_id");
    6748         return false;
    6749     }
    6750     if (!class_id) {
    6751         psError(PS_ERR_UNKNOWN, true, "-class_id is required");
    6752         return false;
    6753     }
    6754 
    6755     psString uri    = psMetadataLookupStr(&status, config->args, "-uri");
    6756     if (!status) {
    6757         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -uri");
    6758         return false;
    6759     }
    6760     if (!uri) {
    6761         psError(PS_ERR_UNKNOWN, true, "-uri is required");
    6762         return false;
    6763     }
    6764 
    6765     // everything else is optional
    6766     psF64 bg = psMetadataLookupF64(&status, config->args, "-bg");
    6767     if (!status) {
    6768         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg");
    6769         return false;
    6770     }
    6771 
    6772     psF64 bg_stdev = psMetadataLookupF64(&status, config->args, "-bg_stdev");
    6773     if (!status) {
    6774         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_stdev");
    6775         return false;
    6776     }
    6777 
    6778     psF64 bg_mean_stdev = psMetadataLookupF64(&status, config->args, "-bg_mean_stdev");
    6779     if (!status) {
    6780         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -bg_mean_stdev");
    6781         return false;
    6782     }
    6783 
    6784     psF64 user_1 = psMetadataLookupF64(&status, config->args, "-user_1");
    6785     if (!status) {
    6786         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_1");
    6787         return false;
    6788     }
    6789     psF64 user_2 = psMetadataLookupF64(&status, config->args, "-user_2");
    6790     if (!status) {
    6791         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_2");
    6792         return false;
    6793     }
    6794     psF64 user_3 = psMetadataLookupF64(&status, config->args, "-user_3");
    6795     if (!status) {
    6796         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_3");
    6797         return false;
    6798     }
    6799     psF64 user_4 = psMetadataLookupF64(&status, config->args, "-user_4");
    6800     if (!status) {
    6801         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_4");
    6802         return false;
    6803     }
    6804     psF64 user_5 = psMetadataLookupF64(&status, config->args, "-user_5");
    6805     if (!status) {
    6806         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -user_5");
    6807         return false;
    6808     }
    6809 
    6810     psString path_base = psMetadataLookupStr(&status, config->args, "-path_base");
    6811     if (!status) {
    6812         psError(PS_ERR_UNKNOWN, false, "failed to lookup value for -path_base");
    6813         return false;
    6814     }
    6815 
    6816     if (!detRegisteredImfileInsert(config->dbh,
    6817                                    (psS64)atoll(det_id),
    6818                                    0,  // the iteration is fixed at 0
    6819                                    class_id,
    6820                                    uri,
    6821                                    bg,
    6822                                    bg_stdev,
    6823                                    bg_mean_stdev,
    6824                                    user_1,
    6825                                    user_2,
    6826                                    user_3,
    6827                                    user_4,
    6828                                    user_5,
    6829                                    path_base,
    6830                                    0       // fault code
    6831             )) {
    6832         psError(PS_ERR_UNKNOWN, false, "database error");
    6833         return false;
    6834     }
    6835 
    6836     return true;
    6837 }
    6838 
    6839 static psS32 incrementIteration(pxConfig *config, psS64 det_id)
    6840 {
    6841     // this function returns zero on error
    6842     PS_ASSERT_PTR_NON_NULL(config, 0);
    6843 
    6844     char *query = "UPDATE detRun SET iteration = iteration + 1 WHERE det_id = %" PRId64;
    6845     if (!p_psDBRunQuery(config->dbh, query, det_id)) {
    6846         psError(PS_ERR_UNKNOWN, false,
    6847                 "failed to increment iteration for det_id %" PRId64, det_id);
    6848         return 0;
    6849     }
    6850 
    6851     psMetadata *where = psMetadataAlloc();
    6852     if (!psMetadataAddS64(where, PS_LIST_TAIL, "det_id", 0, "==", det_id)) {
    6853         psError(PS_ERR_UNKNOWN, false, "failed to add item det_id");
    6854         psFree(where);
    6855         return 0;
    6856     }
    6857 
    6858     psArray *detRuns = detRunSelectRowObjects(config->dbh, where, 0);
    6859     psFree(where);
    6860     if (!detRuns) {
    6861         psError(PS_ERR_UNKNOWN, false, "no detRun rows found");
    6862         return 0;
    6863     }
    6864 
    6865     // sanity check the database
    6866     if (psArrayLength(detRuns) != 1) {
    6867         // this should no happen
    6868         psAbort(                "database query return too many rows (this should not happen");
    6869     }
    6870 
    6871     psS32 newIteration = ((detRunRow *)detRuns->data[0])->iteration;
    6872     psFree(detRuns);
    6873 
    6874     return newIteration;
    6875 }
    6876 
    6877 static bool setDetRunState(pxConfig *config, psS64 det_id, const char *state)
    6878 {
    6879     PS_ASSERT_PTR_NON_NULL(config, false);
    6880     PS_ASSERT_PTR_NON_NULL(state, false);
    6881 
    6882     // check that state is a valid string value
    6883     if (!(
    6884             (strncmp(state, "run", 4) == 0)
    6885             || (strncmp(state, "stop", 5) == 0)
    6886             || (strncmp(state, "drop", 5) == 0)
    6887             || (strncmp(state, "register", 4) == 0)
    6888         )
    6889     ) {
    6890         psError(PS_ERR_UNKNOWN, false,
    6891                 "invalid detRun state: %s", state);
    6892         return false;
    6893     }
    6894 
    6895     char *query = "UPDATE detRun SET state = '%s' WHERE det_id = %" PRId64;
    6896     if (!p_psDBRunQuery(config->dbh, query, state, det_id)) {
    6897         psError(PS_ERR_UNKNOWN, false,
    6898                 "failed to change state for det_id %" PRId64, det_id);
    6899         return false;
    6900     }
    6901 
    6902     return true;
    6903 }
    6904 
    6905 static bool isValidMode(pxConfig *config, const char *mode)
    6906 {
    6907     PS_ASSERT_PTR_NON_NULL(config, false);
    6908     PS_ASSERT_PTR_NON_NULL(mode, false);
    6909 
    6910     // check that state is a valid string value
    6911     if (!(
    6912             (strncmp(mode, "master", 7) == 0)
    6913             || (strncmp(mode, "verify", 7) == 0)
    6914         )
    6915     ) {
    6916         psError(PS_ERR_UNKNOWN, false,
    6917                 "invalid detRun mode: %s", mode);
    6918         return false;
    6919     }
    6920 
    6921     return true;
    6922 }
     35#endif // CALTOOL_H
Note: See TracChangeset for help on using the changeset viewer.