Index: trunk/ippdb/src/ippdb.c
===================================================================
--- trunk/ippdb/src/ippdb.c	(revision 10060)
+++ trunk/ippdb/src/ippdb.c	(revision 10110)
@@ -31,4 +31,5 @@
 #include "ippdb.h"
 
+#define EXPTAGCOUNTER_TABLE_NAME "expTagCounter"
 #define SUMMITEXP_TABLE_NAME "summitExp"
 #define SUMMITIMFILE_TABLE_NAME "summitImfile"
@@ -235,4 +236,262 @@
 }
 
+static void expTagCounterRowFree(expTagCounterRow *object);
+
+expTagCounterRow *expTagCounterRowAlloc(psU64 counter)
+{
+    expTagCounterRow *_object;
+
+    _object = psAlloc(sizeof(expTagCounterRow));
+    psMemSetDeallocator(_object, (psFreeFunc)expTagCounterRowFree);
+
+    _object->counter = counter;
+
+    return _object;
+}
+
+static void expTagCounterRowFree(expTagCounterRow *object)
+{
+}
+
+bool expTagCounterCreateTable(psDB *dbh)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "counter", PS_DATA_U64, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item counter");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBCreateTable(dbh, EXPTAGCOUNTER_TABLE_NAME, md);
+
+    psFree(md);
+
+    return status;
+}
+
+bool expTagCounterDropTable(psDB *dbh)
+{
+    return psDBDropTable(dbh, EXPTAGCOUNTER_TABLE_NAME);
+}
+
+bool expTagCounterInsert(psDB * dbh, psU64 counter)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "counter", PS_DATA_U64, NULL, counter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item counter");
+        psFree(md);
+        return false;
+    }
+
+    bool status = psDBInsertOneRow(dbh, EXPTAGCOUNTER_TABLE_NAME, md);
+    psFree(md);
+
+    return status;
+}
+
+long long expTagCounterDelete(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    long long count = psDBDeleteRows(dbh, EXPTAGCOUNTER_TABLE_NAME, where, limit);
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from expTagCounter");
+        return count;
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool expTagCounterInsertObject(psDB *dbh, expTagCounterRow *object)
+{
+    return expTagCounterInsert(dbh, object->counter);
+}
+
+bool expTagCounterInsertObjects(psDB *dbh, psArray *objects)
+{
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        if (!expTagCounterInsertObject(dbh, objects->data[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool expTagCounterInsertFits(psDB *dbh, const psFits *fits)
+{
+    psArray         *rowSet;
+
+    // move to (the first?) extension named  EXPTAGCOUNTER_TABLE_NAME
+    if (!psFitsMoveExtName(fits, EXPTAGCOUNTER_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, true, "failed to find FITS extension %s", EXPTAGCOUNTER_TABLE_NAME);
+        return false;
+    }
+
+    // check HDU type
+    if (psFitsGetExtType(fits) != PS_FITS_TYPE_BINARY_TABLE)  {
+        psError(PS_ERR_UNKNOWN, true, "FITS HDU type is not PS_FITS_TYPE_BINARY_TABLE");
+        return false;
+    }
+
+    // read fits table
+    rowSet = psFitsReadTable(fits);
+    if (!rowSet) {
+        psError(PS_ERR_UNKNOWN, true, "FITS read error or FITS table is empty");
+        psFree(rowSet);
+        return false;
+    }
+
+    if (!psDBInsertRows(dbh, EXPTAGCOUNTER_TABLE_NAME, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "databse insert failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool expTagCounterSelectRowsFits(psDB *dbh, psFits *fits, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+
+    rowSet = psDBSelectRows(dbh, EXPTAGCOUNTER_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return false;
+    }
+
+    // output to fits
+    if (!psFitsWriteTable(fits, NULL, rowSet, EXPTAGCOUNTER_TABLE_NAME)) {
+        psError(PS_ERR_UNKNOWN, false, "FITS table write failed");
+        psFree(rowSet);
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+psMetadata *expTagCounterMetadataFromObject(const expTagCounterRow *object)
+{
+    psMetadata *md = psMetadataAlloc();
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "counter", PS_DATA_U64, NULL, object->counter)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to add item counter");
+        psFree(md);
+        return false;
+    }
+
+
+    return md;
+}
+
+expTagCounterRow *expTagCounterObjectFromMetadata(psMetadata *md)
+{
+
+bool status = false;
+    psU64 counter = psMetadataLookupU64(&status, md, "counter");
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item counter");
+        return false;
+    }
+
+    return expTagCounterRowAlloc(counter);
+}
+psArray *expTagCounterSelectRowObjects(psDB *dbh, const psMetadata *where, unsigned long long limit)
+{
+    psArray         *rowSet;
+    psArray         *returnSet;
+    psU64           i;
+
+    rowSet = psDBSelectRows(dbh, EXPTAGCOUNTER_TABLE_NAME, where, limit);
+    if (!rowSet) {
+        return NULL;
+    }
+
+    // convert psMetadata rows to row objects
+
+    returnSet = psArrayAllocEmpty(rowSet->n);
+
+    for (i = 0; i < rowSet->n; i++) {
+        expTagCounterRow *object = expTagCounterObjectFromMetadata(rowSet->data[i]);
+        psArrayAdd(returnSet, 0, object);
+        psFree(object);
+    }
+
+    psFree(rowSet);
+
+    return returnSet;
+}
+bool expTagCounterDeleteObject(psDB *dbh, const expTagCounterRow *object)
+{
+    psMetadata *where = expTagCounterMetadataFromObject(object);
+    long long count = psDBDeleteRows(dbh, EXPTAGCOUNTER_TABLE_NAME, where, 0);
+    psFree(where)
+    if (count < 0) {
+        psError(PS_ERR_UNKNOWN, true, "failed to delete row from expTagCounter");
+        return false;
+    }
+    if (count > 1) {
+        // XXX should this be a psAbort() instead?  It is possible that
+        // having an object match multiple rows was by design.
+        psError(PS_ERR_UNKNOWN, true, "expTagCounterRow object matched more then one row.  Check your database schema");
+        return false;
+    }
+
+    return true;
+}
+long long expTagCounterDeleteRowObjects(psDB *dbh, const psArray *objects, unsigned long long limit)
+{
+    long long       deleted = 0;
+
+    for (long long i = 0; i < objects->n; i++) {
+        expTagCounterRow *object = objects->data[i];
+        psMetadata *where = expTagCounterMetadataFromObject(object);
+        long long count = psDBDeleteRows(dbh, EXPTAGCOUNTER_TABLE_NAME, where, limit);
+        psFree(where)
+        if (count < 0) {
+            psError(PS_ERR_UNKNOWN, true, "failed to delete row from expTagCounter");
+            return count;
+        }
+
+        deleted += count;
+    }
+
+    return deleted;
+}
+bool expTagCounterPrintObjects(FILE *stream, psArray *objects, bool mdcf)
+{
+    PS_ASSERT_PTR_NON_NULL(objects, false);
+
+    psMetadata *output = psMetadataAlloc();
+    for (long i = 0; i < psArrayLength(objects); i++) {
+        psMetadata *md = expTagCounterMetadataFromObject(objects->data[i]);
+        if (!psMetadataAddMetadata(
+            output,
+            PS_LIST_TAIL,
+            EXPTAGCOUNTER_TABLE_NAME,
+            PS_META_DUPLICATE_OK,
+            NULL,
+            md
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to add metadata");
+            psFree(md);
+            psFree(output);
+            return false;
+        }
+        psFree(md);
+    }
+
+    if (!ippdbPrintMetadataRaw(stream, output, mdcf)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print metadata");
+        psFree(output);
+    }
+    psFree(output);
+
+    return true;
+}
 static void summitExpRowFree(summitExpRow *object);
 
@@ -2408,5 +2667,5 @@
 static void newExpRowFree(newExpRow *object);
 
-newExpRow *newExpRowAlloc(psU64 exp_tag, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, psS32 imfiles)
+newExpRow *newExpRowAlloc(const char *exp_tag, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, psS32 imfiles)
 {
     newExpRow       *_object;
@@ -2415,5 +2674,5 @@
     psMemSetDeallocator(_object, (psFreeFunc)newExpRowFree);
 
-    _object->exp_tag = exp_tag;
+    _object->exp_tag = psStringCopy(exp_tag);
     _object->exp_id = psStringCopy(exp_id);
     _object->camera = psStringCopy(camera);
@@ -2428,4 +2687,5 @@
 static void newExpRowFree(newExpRow *object)
 {
+    psFree(object->exp_tag);
     psFree(object->exp_id);
     psFree(object->camera);
@@ -2438,5 +2698,5 @@
 {
     psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_U64, "Primary Key AUTO_INCREMENT", 0)) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, "Primary Key", "64")) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
@@ -2489,8 +2749,8 @@
 }
 
-bool newExpInsert(psDB * dbh, psU64 exp_tag, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, psS32 imfiles)
+bool newExpInsert(psDB * dbh, const char *exp_tag, const char *exp_id, const char *camera, const char *telescope, psTime* dateobs, const char *exp_type, psS32 imfiles)
 {
     psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_U64, NULL, exp_tag)) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, exp_tag)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
@@ -2623,5 +2883,5 @@
 {
     psMetadata *md = psMetadataAlloc();
-    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_U64, NULL, object->exp_tag)) {
+    if (!psMetadataAdd(md, PS_LIST_TAIL, "exp_tag", PS_DATA_STRING, NULL, object->exp_tag)) {
         psError(PS_ERR_UNKNOWN, false, "failed to add item exp_tag");
         psFree(md);
@@ -2667,5 +2927,5 @@
 
 bool status = false;
-    psU64 exp_tag = psMetadataLookupU64(&status, md, "exp_tag");
+    char* exp_tag = psMetadataLookupPtr(&status, md, "exp_tag");
     if (!status) {
         psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item exp_tag");
