1859400ffa0d1aa7af657780447cb1544060be76 tdreszer Fri Mar 25 14:01:06 2011 -0700 Added useful functions to mdb, including encode specific ones. Fixed encodeExp retreival by mdb obj. diff --git src/hg/lib/mdb.c src/hg/lib/mdb.c index 6317523..e8fbb2a 100644 --- src/hg/lib/mdb.c +++ src/hg/lib/mdb.c @@ -1408,30 +1408,70 @@ if(multiVals) dyStringPrintf(dy, ")"); dyStringPrintf(dy, ")"); } dyStringPrintf(dy, " ORDER BY binary T1.obj, T1.var"); // binary forces case sensitive sort verbose(2, "Requesting query:\n\t%s;\n",dyStringContents(dy)); struct mdb *mdb = mdbLoadByQuery(conn, dyStringCannibalize(&dy)); verbose(3, "rows (vars) returned: %d\n",slCount(mdb)); struct mdbObj *mdbObjs = mdbObjsLoadFromMemory(&mdb,TRUE); verbose(3, "Returned %d object(s) with %d var(s).\n", mdbObjCount(mdbObjs,TRUE),mdbObjCount(mdbObjs,FALSE)); return mdbObjs; } +struct mdbObj *mdbObjsQueryByVarValString(struct sqlConnection *conn,char *tableName,char *varVals) +// returns mdbObjs matching varVals in form of: [var1=val1 var2=val2a,val2b var3=v%3 var4="val 4" var5!=val5 var6=] +// var2=val2a,val2b: matches asny of comma separated list +// var3=v%3 : matches '%' and '?' wild cards. +// var4="val 4" : matches simple double quoted strings. +// var5!=val5 : matches not equal. +// var6= : matches that var exists (same as var6=%). var6!= also works. +{ +struct mdbByVar *mdbByVars = mdbByVarsLineParse(varVals); +if (mdbByVars == NULL) + return NULL; +return mdbObjsQueryByVars(conn,tableName,mdbByVars); +} + +struct mdbObj *mdbObjsQueryByVarPairs(struct sqlConnection *conn,char *tableName,struct slPair *varValPairs) +// returns mdbObjs matching varValPairs provided. +// The != logic of mdbObjsQueryByVarValString() is not possible, but other cases are supported: +// as val may be NULL, a comma delimited list, double quoted string, containing wilds: % and ? +{ +// Note: there is a bit of inefficiency creating a string then tearing it down, but it streamlines code +char *varValString = slPairListToString(varValPairs); +struct mdbObj *mdbObjs = mdbObjsQueryByVarValString(conn,tableName,varValString); +freeMem(varValString); +return mdbObjs; +} + +struct mdbObj *mdbObjQueryCompositeObj(struct sqlConnection *conn,char *tableName,struct mdbObj *mdbObj) +// returns NULL or the composite mdbObj associated with the object passed in. +{ +char *objType = mdbObjFindValue(mdbObj,MDB_OBJ_TYPE); +assert(objType != NULL); +if (sameWord(objType,MDB_VAR_COMPOSITE)) + return mdbObjClone(mdbObj); +char *compName = mdbObjFindValue(mdbObj,MDB_VAR_COMPOSITE); +if (compName == NULL) + return NULL; + +return mdbObjQuery(conn,tableName,mdbObjCreate(compName,NULL,NULL)); +} + // ----------- Printing and Counting ----------- static void mdbVarValPrint(struct mdbVar *mdbVar,boolean raStyle, FILE *outF) { if(mdbVar != NULL && mdbVar->var != NULL) { if(raStyle) fprintf(outF, "\n%s ",mdbVar->var); else fprintf(outF, " %s=",mdbVar->var); if(mdbVar->val != NULL) { if(!raStyle && strchr(mdbVar->val, ' ') != NULL) // Has blanks fprintf(outF, "\"%s\"",mdbVar->val); else @@ -2301,31 +2341,50 @@ } struct slName *mdbObjToSlName(struct mdbObj *mdbObjs) // Creates slNames list of mdbObjs->obj. mdbObjs remains untouched { struct slName *mdbNames = NULL; struct mdbObj *mdbObj = mdbObjs; for( ;mdbObj!=NULL; mdbObj=mdbObj->next) { slAddHead(&mdbNames,slNameNew(mdbObj->obj)); //allocates memory } slReverse(&mdbNames); return mdbNames; } -// ----------------- Validateion and specialty APIs ----------------- +// ----------------- Validation and specialty APIs ----------------- + +boolean mdbObjIsComposite(struct mdbObj *mdbObj) +// returns TRUE if this is a valid composite object +{ +char *objType = mdbObjFindValue(mdbObj,MDB_OBJ_TYPE); +assert(objType != NULL); +return sameWord(objType,MDB_OBJ_TYPE_COMPOSITE); +} + +boolean mdbObjIsCompositeMember(struct mdbObj *mdbObj) +// returns TRUE if this is a valid member of a composite. DOES not confirm that composite obj exists +{ +char *objType = mdbObjFindValue(mdbObj,MDB_OBJ_TYPE); +assert(objType != NULL); +if (differentWord(objType,MDB_OBJ_TYPE_TABLE) && differentWord(objType,MDB_OBJ_TYPE_FILE)) + return FALSE; +return mdbObjContains(mdbObj,MDB_VAR_COMPOSITE,NULL); +} + int mdbObjsValidate(struct mdbObj *mdbObjs, boolean full) // Validates vars and vals against cv.ra. Returns count of errors found. // Full considers vars not defined in cv as invalids { struct hash *termTypeHash = (struct hash *)cvTermTypeHash(); struct mdbObj *mdbObj = NULL; int invalids = 0; for( mdbObj=mdbObjs; mdbObj!=NULL; mdbObj=mdbObj->next ) { struct mdbVar *mdbVar = NULL; for(mdbVar = mdbObj->vars;mdbVar != NULL;mdbVar=mdbVar->next) { struct hash *termHash = hashFindVal(termTypeHash,mdbVar->var); if (termHash == NULL) // No cv definition for term so no validation can be done { @@ -2477,250 +2536,292 @@ { //char buffer[128]; //regerror(err, ®Ex, buffer, sizeof buffer); printf("INVALID regex '%s' match: %s -> %s = '%s'.\n",validationRule, mdbObj->obj,mdbVar->var,mdbVar->val); invalids++; } regfree(®Ex); } else verbose(1,"ERROR in %s: Unknown validationRule rule '%s' for term %s.\n",CV_FILE_NAME,validationRule,mdbVar->var); } } return invalids; } +static struct slName *mdbCompositeFindEncodeEdvs(struct mdbObj *compObj) +// returns NULL or the list of EDVs defined for this composite +{ +char *edvs = mdbObjFindValue(compObj,MDB_VAR_ENCODE_EDVS); +if (edvs == NULL) + return NULL; + +edvs = cloneString(edvs); +if (strchr( edvs,',') != NULL) // Tolerate delimit by commas + strSwapChar(edvs,',',' '); +else if (strchr(edvs,';') != NULL) // Tolerate delimit by semicolons + strSwapChar(edvs,';',' '); + +struct slName *compositeEdvs = slNameListFromString(edvs,' '); +freeMem(edvs); +return compositeEdvs; +} + +static struct mdbVar *mdbObjEncodeEdvsAsMdbVars(struct mdbObj *mdbObj,struct slName *compositeEdvs,boolean includeNone) +// returns the EDVs and values for the composite member object +// If includeNone, then defined variables not found in obj will be included as {var}="None". +{ +struct mdbVar *edvVars = NULL; +struct slName *var = compositeEdvs; +for(;var!=NULL;var=var->next) + { + char *val = mdbObjFindValue(mdbObj,var->name); + if (val) + mdbVarAdd(&edvVars, var->name,val); + else if (includeNone) + { + if (differentWord(var->name,ENCODE_EXP_FIELD_ORGANISM)) // Does not go into EDV's sent to encodeExp table + mdbVarAdd(&edvVars, var->name,MDB_VAL_ENCODE_EDV_NONE); + } + } +slReverse(&edvVars); +return edvVars; +} + +struct slName *mdbObjFindCompositeEncodeEdvNames(struct sqlConnection *conn,char *tableName,struct mdbObj *mdbObj) +// returns NULL or the Experiment Defining Variable names for this composite +{ +if (!mdbObjIsCompositeMember(mdbObj)) + return NULL; // This should be a valid composite memeber + +struct mdbObj *compObj = mdbObjQueryCompositeObj(conn,tableName,mdbObj); +if (compObj == NULL) + return NULL; + +struct slName *edvs = mdbCompositeFindEncodeEdvs(compObj); +mdbObjFree(&compObj); +return edvs; +} + +struct mdbVar *mdbObjFindEncodeEdvPairs(struct sqlConnection *conn,char *tableName,struct mdbObj *mdbObj,boolean includeNone) +// returns NULL or the Experiment Defining Variables and values for this composite member object +// If includeNone, then defined variables not found in obj will be included as {var}="None". +{ +struct slName *compositeEdvs = mdbObjFindCompositeEncodeEdvNames(conn,tableName,mdbObj); +if (compositeEdvs == NULL) + return NULL; + +return mdbObjEncodeEdvsAsMdbVars(mdbObj,compositeEdvs,includeNone); +} + #define EXPERIMENTS_TABLE "hgFixed.encodeExp" struct mdbObj *mdbObjsEncodeExperimentify(struct sqlConnection *conn,char *db,char *tableName,struct mdbObj **pMdbObjs, int warn,boolean createExpIfNecessary) // Organizes objects into experiments and validates experiment IDs. Will add/update the ids in the structures. // If warn=1, then prints to stdout all the experiments/obs with missing or wrong expIds; // warn=2, then print line for each obj with expId or warning. // createExpIfNecessary means go ahead and add to the hgFixed.encodeExp table to get an ID // Returns a new set of mdbObjs that is what can (and should) be used to update the mdb via mdbObjsSetToDb(). { +// Here is what "experimentify" does from "mdbPrint -encodeExp" and "mdbUpdate -encodeExp": +// - Uses normal selection methods to get a set of objects (e.g. one composite worth) or all objs. (in mdbPrint and mdbUpdate) +// - This API: +// - Breaks up and walks through set of objects composite by composite +// - Looks up EDVs (Experiment Defining Variables) for composite. +// These are defined in the mdb under objType=composite expVars= +// - Breaks up and walks through composite objects exp by exp as defined by EDVs +// - Uses encodeExp API to determine what expId should be. +// - Creates new mdbObjs list of updates needed to put expId and dccAccession into the mdb. +// - From "mdbPrint", this API warns of mismatches or missing expIds +// - From "mdbUpdate" (not -test) then that utility will update the mdb from this API's return structs. If -test, will reveal what would be updated. +// - FIXME: Need to extend this to update dccAccession separately from expId. + if (pMdbObjs == NULL || *pMdbObjs == NULL) return 0; struct mdbObj *mdbObjs = *pMdbObjs; struct mdbObj *mdbProcessedObs = NULL; struct mdbObj *mdbUpdateObjs = NULL; -/* Here is what "experimentify" does from "mdbPrint -encodeExp" and "mdbUpdate -encodeExp": - - Uses normal selection methods to get a set of objects (e.g. one composite worth) or all objs. (in mdbPrint and mdbUpdate) - - This API: - - Breaks up and walks through set of objects composite by composite - - Looks up EDVs (expiment defining vars) for composite. - Currently these are defined in the mdb under objType=composite expVars= - (e.g. obj=wgEncodeBroadHistone objType=composite expVars=lab,dataType,cell,antibody) - FIXME: Nice to add white-list to cv.ra typeOfTerms - - Breaks up and walks through composite objects exp by exp (handle's "None"s gracefully) - - Determines what expId should be. - - Creates new mdbObjs list of updates needed to put expId and dccAccession into the mdb. - - From "mdbPrint", this API warns of mismatches or missing expIds - - From "mdbUpdate" (not -test) then that utility will update the mdb from this API's return structs. If -test, will reveal what would be updated. */ - // Sort all objects by composite, so that we handle composite by composite mdbObjsSortOnVars(&mdbObjs, MDB_VAR_COMPOSITE); struct dyString *dyVars = dyStringNew(256); while(mdbObjs != NULL) { // Work on a composite at a time char *compName = NULL; while(mdbObjs != NULL && compName == NULL) { compName = mdbObjFindValue(mdbObjs,MDB_VAR_COMPOSITE); if (compName == NULL) { verbose(1, "Object '%s' has no %s defined.\n",mdbObjs->obj,MDB_VAR_COMPOSITE); mdbProcessedObs = slCat(mdbProcessedObs,slPopHead(&mdbObjs)); continue; } } struct mdbObj *mdbCompositeObjs = mdbObjsFilter(&mdbObjs, MDB_VAR_COMPOSITE, compName,TRUE); // --- At this point we have nibbled off a composite worth of objects from the full set of objects - // Find the composite obj if it exists struct mdbObj *compObj = mdbObjsFilter(&mdbCompositeObjs, MDB_OBJ_TYPE, MDB_OBJ_TYPE_COMPOSITE,TRUE); if (compObj == NULL) // May be NULL if mdbObjs passed in was produced by too narrow of selection criteria + compObj = mdbObjQueryCompositeObj(conn,tableName,mdbCompositeObjs); // First obj on list will do + else + slAddHead(&mdbProcessedObs,compObj); // We can still use the pointer, but will not "process" it. NOTE: leak the queried one + if(compObj == NULL) { - dyStringClear(dyVars); - dyStringPrintf(dyVars,"%s=%s %s=", MDB_VAR_COMPOSITE, compName,MDB_VAR_ENCODE_EDVS); - struct mdbByVar *mdbByVars = mdbByVarsLineParse(dyStringContents(dyVars)); - compObj = mdbObjsQueryByVars(conn,tableName,mdbByVars); + verbose(1, "Composite '%s' has not been defined.\n",compName); + mdbProcessedObs = slCat(mdbProcessedObs,mdbCompositeObjs); + mdbCompositeObjs = NULL; + continue; } // Obtain experiment defining variables for the composite - dyStringClear(dyVars); - if (compObj != NULL) + struct slName *compositeEdvs = mdbCompositeFindEncodeEdvs(compObj); + if (compositeEdvs == NULL) { - char *expVars = mdbObjFindValue(compObj,MDB_VAR_ENCODE_EDVS); - if (expVars) - dyStringAppend(dyVars, expVars); // expVars in form of "var1 var2 var3" - } - if (dyStringLen(dyVars) == 0) - { - // figure them out? - // NOTE: Kate wants white list of EDVs from the cv. Wranglers satisfied with defining them in an mdbObj of objType=composite - // Walk through the mdbCompositeObjs looking for matching vars. verbose(1, "There are no experiment defining variables established for this %s. Add them to obj %s => var:%s.\n", MDB_VAR_COMPOSITE, compName,MDB_VAR_ENCODE_EDVS); mdbProcessedObs = slCat(mdbProcessedObs,mdbCompositeObjs); mdbCompositeObjs = NULL; continue; } - - // Parse into individual Experiment Defining Variables (no vals at the composite level) - if (strchr(dyStringContents(dyVars), ',') != NULL) // Tolerate delimit by commas - strSwapChar(dyStringContents(dyVars),',',' '); - else if (strchr(dyStringContents(dyVars), ';') != NULL) // Tolerate delimit by semicolons - strSwapChar(dyStringContents(dyVars),';',' '); - struct slName *compositeEdvs = slNameListFromString(dyStringContents(dyVars), ' '); - assert(slCount(compositeEdvs) > 0); + dyStringClear(dyVars); + dyStringAppend(dyVars,slNameListToString(compositeEdvs, ' ')); if (warn > 0) printf("Composite '%s' with %d objects has %d EDVs(%s): [%s].\n",compName,slCount(mdbCompositeObjs), slCount(compositeEdvs),MDB_VAR_ENCODE_EDVS,dyStringContents(dyVars)); // Set the stage // Organize composite objs by EDVs dyStringPrintf(dyVars, " view replicate "); // Allows for nicer sorted list char *edvSortOrder = cloneString(dyStringContents(dyVars)); // Walk through objs for an exp as defined by EDVs int expCount=0; // Count of experiments in composite int expMissing=0; // Count of experiments with missing expId int expObjsCount=0; // Total of all experimental object accoss the composite int expMax=0; // Largest experiment (in number of objects) int expMin=999; // Smallest experiment (in number of objects) while(mdbCompositeObjs != NULL) { // Must sort each cycle, because sort order is lost during mdbObjs FilterByVars(); mdbObjsSortOnVars(&mdbCompositeObjs, edvSortOrder); - // Construct the var=val string for the exp at the top of the stack - struct mdbVar *edvVars = NULL,*edvVar = NULL; - dyStringClear(dyVars); - struct dyString *filterVars = dyStringNew(256); - struct slName *var = compositeEdvs; - int valsFound = 0; - for(;var!=NULL;var=var->next) + // Get the EDVs for the first obj + struct mdbVar *edvVarVals = mdbObjEncodeEdvsAsMdbVars(mdbCompositeObjs,compositeEdvs,TRUE); // use first obj on list and include Nones + if (edvVarVals == NULL) { - char *val = mdbObjFindValue(mdbCompositeObjs,var->name); // Looking at first obj in queue - if (val) - { - valsFound++; - dyStringPrintf(filterVars,"%s=%s ",var->name,val); - edvVar = mdbVarAdd(&edvVars, var->name,val); - dyStringPrintf(dyVars,"%s=%s ",edvVar->var,edvVar->val); + verbose(1, "There are no experiment defining variables for this object '%s'.\n",mdbCompositeObjs->obj); + slAddHead(&mdbProcessedObs,slPopHead(&mdbCompositeObjs)); // We're done with this one + continue; } - else - { - if (differentWord(var->name,ENCODE_EXP_FIELD_ORGANISM)) // Does not go into EDV's sent to encodeExp table + + // Construct the var=val string for filtering a single exp (set of objs) from composite worth of objs + dyStringClear(dyVars); + struct mdbVar *edvVar = edvVarVals; + int valsFound = 0; + for(;edvVar!=NULL;edvVar=edvVar->next) { - dyStringPrintf(filterVars,"%s=%s ",var->name,MDB_VAL_ENCODE_EDV_NONE); - edvVar = mdbVarAdd(&edvVars, var->name,MDB_VAL_ENCODE_EDV_NONE); dyStringPrintf(dyVars,"%s=%s ",edvVar->var,edvVar->val); - } - } + if (differentString(edvVar->val,MDB_VAL_ENCODE_EDV_NONE)) + valsFound++; } dyStringContents(dyVars)[dyStringLen(dyVars) -1] = '\0'; // Nicer printing is all if (valsFound == 0) { verbose(1, "There are no experiment defining variables for this object '%s'.\n",mdbCompositeObjs->obj); slAddHead(&mdbProcessedObs,slPopHead(&mdbCompositeObjs)); // We're done with this one - dyStringFree(&filterVars); - mdbVarsFree(&edvVars); + mdbVarsFree(&edvVarVals); continue; } - // Work on one experiment at a time - struct mdbObj *mdbExpObjs = mdbObjsFilterByVars(&mdbCompositeObjs,dyStringContents(filterVars),TRUE,TRUE); - dyStringFree(&filterVars); - // --- At this point we have nibbled off an experiment worth of objects from the composite set of objects + struct mdbObj *mdbExpObjs = mdbObjsFilterByVars(&mdbCompositeObjs,dyStringContents(dyVars),TRUE,TRUE); // None={notFound} + // --- At this point we have nibbled off an experiment worth of objects from the composite set of objects int objsInExp = slCount(mdbExpObjs); assert(objsInExp > 0); expCount++; - expObjsCount += objsInExp; // Total of all experimental object across the composite + expObjsCount += objsInExp; // Total of all experimental objects across the composite // Look up each exp in EXPERIMENTS_TABLE char experimentId[128]; int expId = -1; - struct encodeExp *exp = encodeExpGetByMdbVars(db, edvVars); + struct encodeExp *exp = encodeExpGetByMdbVars(db, edvVarVals); if (exp == NULL && createExpIfNecessary) - exp = encodeExpGetOrCreateByMdbVars(db, edvVars); - mdbVarsFree(&edvVars); // No longer needed + exp = encodeExpGetOrCreateByMdbVars(db, edvVarVals); + mdbVarsFree(&edvVarVals); // No longer needed if (exp != NULL) expId = exp->ix; if (expId == -1) { safef(experimentId,sizeof(experimentId),"{missing}"); if (warn > 0) printf("Experiment %s EDV: [%s] is not defined in %s table.\n",experimentId,dyStringContents(dyVars),EXPERIMENTS_TABLE); //printf("Experiment %s EDV: [%s] is not defined in %s table. Remaining:%d and %d\n",experimentId,dyStringContents(dyVars),EXPERIMENTS_TABLE,slCount(mdbCompositeObjs),slCount(mdbObjs)); if (warn < 2) // From mdbUpdate (warn=1), just interested in testing waters. From mdbPrint (warn=2) list all objs in exp. { expMissing++; mdbProcessedObs = slCat(mdbProcessedObs,mdbExpObjs); mdbExpObjs = NULL; encodeExpFree(&exp); continue; } } else { safef(experimentId,sizeof(experimentId),"%d",expId); if (warn > 0) printf("Experiment %s has %d objects based upon %d EDVs: [%s].\n",experimentId,slCount(mdbExpObjs),valsFound,dyStringContents(dyVars)); // Set the stage } - // Now we can walk through each obj in experiment and determine if it has the coorect expId + // Now we can walk through each obj in experiment and determine if it has the correct expId int foundId = FALSE; int errors = objsInExp; if (expMax < objsInExp) expMax = objsInExp; if (expMin > objsInExp) expMin = objsInExp; while(mdbExpObjs != NULL) { struct mdbObj *obj = slPopHead(&mdbExpObjs); { // NOTE: This list could expand but we expect only tables and files to be objs in an experiment char *objType = mdbObjFindValue(obj,MDB_OBJ_TYPE); assert(objType != NULL && (sameString(objType,MDB_OBJ_TYPE_TABLE) || sameString(objType,MDB_OBJ_TYPE_FILE))); } boolean updateObj = FALSE; char *val = mdbObjFindValue(obj,MDB_VAR_ENCODE_EXP_ID); if (val != NULL) { foundId = TRUE; // warn==1 will give only 1 exp wide error if no individual errors. NOTE: would be nice if those with expId sorted to beginning, but can't have everything. int thisId = atoi(val); if (thisId == expId && expId != -1) { errors--; // One less error if (warn > 1) // NOTE: Could give more info for each obj as per wrangler's desires { - char *acc = mdbObjFindValue(obj,MDB_VAR_DCC_ACCESSION); + char *acc = mdbObjFindValue(obj,MDB_VAR_DCC_ACCESSION); // FIXME: Add code to update accession to encodeExp if (acc == NULL) printf(" %s %s\n",experimentId,obj->obj); else printf(" %s %s %s set, needs %s.\n",experimentId,obj->obj,MDB_VAR_ENCODE_EXP_ID,MDB_VAR_DCC_ACCESSION); } } else { updateObj = TRUE; if (warn > 0) printf(" %s %s has bad %s=%s.\n",experimentId,obj->obj,MDB_VAR_ENCODE_EXP_ID,val); } } else { @@ -2987,35 +3088,32 @@ dyStringPrintf(dyTerms,"%s=%s ",onePair->name,(char *)onePair->val); else if (searchBy == cvSearchByDateRange || searchBy == cvSearchByIntegerRange) { // TO BE IMPLEMENTED // Requires new mdbObjSearch API and more than one (char *)onePair->val warn("mdb search by date or number is not yet implemented."); } } // Be sure to include table or file in selections if (tables) dyStringPrintf(dyTerms,"%s=%s ",MDB_OBJ_TYPE,MDB_OBJ_TYPE_TABLE); if (files) dyStringPrintf(dyTerms,"%s=? ",MDB_VAR_FILENAME); // Build the mdbByVals struct and then select all mdbObjs in one query -struct mdbByVar *mdbByVars = mdbByVarsLineParse(dyStringContents(dyTerms)); -dyStringClear(dyTerms); -char *tableName = mdbTableName(conn,TRUE); // Look for sandBox name first -struct mdbObj *mdbObjs = mdbObjsQueryByVars(conn,tableName,mdbByVars); - +struct mdbObj *mdbObjs = mdbObjsQueryByVarVals(conn,dyStringContents(dyTerms)); +dyStringFree(&dyTerms); return mdbObjs; } struct slName *mdbObjNameSearch(struct sqlConnection *conn, char *var, char *val, char *op, int limit, boolean tables, boolean files) // Search the metaDb table for objs by var and val. Can restrict by op "is", "like", "in" and accept (non-zero) limited string size // Search is via mysql, so it's case-insensitive. Return is sorted on obj. { // Note: This proves faster than getting mdbObjs then converting to slNames struct mdbObj *mdbObjs = mdbObjSearch(conn,var,val,op,limit); // May only be interested in tables or files: if (tables || files) { struct mdbObj *mdbObjsDropped = mdbObjsFilterTablesOrFiles(&mdbObjs,tables,files); mdbObjsFree(&mdbObjsDropped); }