Index: trunk/ippTools/share/addtool_find_cam_id.sql
===================================================================
--- trunk/ippTools/share/addtool_find_cam_id.sql	(revision 26913)
+++ trunk/ippTools/share/addtool_find_cam_id.sql	(revision 26915)
@@ -6,4 +6,15 @@
 JOIN rawExp
     USING(exp_id)
+LEFT JOIN (SELECT exp_id       AS added_exp_id,
+                  addRun.dvodb AS previous_dvodb
+           FROM addRun
+           JOIN camRun USING(cam_id)
+           JOIN chipRun USING(chip_id)
+          ) as foo
+     ON exp_id = added_exp_id
+     -- hook for qualifying the join on the previous_dvodb
+     AND %s
 WHERE
     camRun.state = 'full'
+    AND added_exp_id IS NULL
+    -- addtool adds checks on exposure being added to the dvodb previously
Index: trunk/ippTools/src/addtool.c
===================================================================
--- trunk/ippTools/src/addtool.c	(revision 26913)
+++ trunk/ippTools/src/addtool.c	(revision 26915)
@@ -99,4 +99,5 @@
     PXOPT_COPY_S64(config->args, where,  "-cam_id",    "camRun.cam_id", "==");
     pxAddLabelSearchArgs (config, where, "-label",     "camRun.label", "=="); // define using camRun label
+    pxAddLabelSearchArgs (config, where, "-data_group","camRun.data_group", "=="); // define using camRun label
     PXOPT_COPY_STR(config->args, where,  "-reduction", "camRun.reduction", "==");
 
@@ -119,10 +120,26 @@
 
     // find the cam_id of all the exposures that we want to queue up.
-    psString query = pxDataGet("addtool_find_cam_id.sql");
-    if (!query) {
+    psString bare_query = pxDataGet("addtool_find_cam_id.sql");
+    if (!bare_query) {
         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
         psFree(where);
         return false;
     }
+
+    // prevent queueing an addRun if a given exposure has already been added to 
+    // the given dvo database
+    psString dvodb_string = NULL;
+    if (dvodb) {
+        // user supplied dvodb
+        psStringAppend(&dvodb_string, "(previous_dvodb = '%s')", dvodb);
+    } else {
+        // inherit dvodb from camRun, avoid matching NULL
+        psStringAppend(&dvodb_string, "(camRun.dvodb IS NOT NULL AND previous_dvodb = camRun.dvodb)");
+    }
+    // Take the bare query and add the dvodb selector
+    psString query = NULL;
+    psStringAppend(&query, bare_query, dvodb_string);
+    psFree(dvodb_string);
+    psFree(bare_query);
 
     // use psDBGenerateWhereConditionSQL because the SQL ends in a WHERE
@@ -131,4 +148,7 @@
         psStringAppend(&query, " AND %s", whereClause);
         psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        return false;
     }
     psFree(where);
Index: trunk/ippTools/src/addtoolConfig.c
===================================================================
--- trunk/ippTools/src/addtoolConfig.c	(revision 26913)
+++ trunk/ippTools/src/addtoolConfig.c	(revision 26915)
@@ -52,4 +52,5 @@
     pxcamSetSearchArgs(definebyqueryArgs);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by camRun label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by camRun data_group", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",          0, "search by camRun reduction class", NULL);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-destreaked",           0, "only queue destreaked runs", false);
