otsdaq  3.10.00
ConfigurationManagerRW.cc
1 #include "otsdaq/ConfigurationInterface/ConfigurationManagerRW.h"
2 
3 #include <dirent.h>
4 #include <chrono>
5 
6 using namespace ots;
7 
8 #undef __MF_SUBJECT__
9 #define __MF_SUBJECT__ "ConfigurationManagerRW"
10 
11 #define TABLE_INFO_PATH std::string(__ENV__("TABLE_INFO_PATH")) + "/"
12 #define TABLE_INFO_EXT "Info.xml"
13 
14 #define CORE_TABLE_INFO_FILENAME \
15  ((getenv("SERVICE_DATA_PATH") == NULL) \
16  ? (std::string(__ENV__("USER_DATA")) + "/ServiceData") \
17  : (std::string(__ENV__("SERVICE_DATA_PATH")))) + \
18  "/CoreTableInfoNames.dat"
19 
20 std::atomic<bool> ConfigurationManagerRW::firstTimeConstructed_ = true;
21 
22 std::mutex ConfigurationManagerRW::versionCreationTimeCacheMutex_;
23 std::map<std::string, std::map<TableVersion, time_t>>
24  ConfigurationManagerRW::versionCreationTimeCache_;
25 
26 //==============================================================================
29  : ConfigurationManager(username) // for use as author of new views
30 {
31  __GEN_COUT__ << "Instantiating Config Manager with Write Access! (for " << username
32  << ") time=" << time(0) << " runTimeSeconds()=" << runTimeSeconds()
33  << __E__;
34 
35  theInterface_ = ConfigurationInterface::getInstance(
36  ConfigurationInterface::CONFIGURATION_MODE::
37  ARTDAQ_DATABASE); // false to use artdaq DB
38 
39  //=========================
40  // dump names of core tables (so UpdateOTS.sh can copy core tables for user)
41  // only if table does not exist
42  if(firstTimeConstructed_)
43  {
44  firstTimeConstructed_ = false;
45 
46  // make table group history directory here and at Gateway Supervisor (just in case)
47  mkdir((ConfigurationManager::LAST_TABLE_GROUP_SAVE_PATH).c_str(), 0755);
48 
49  const std::set<std::string>& contextMemberNames = getFixedContextMemberNames();
50  const std::set<std::string>& backboneMemberNames = getBackboneMemberNames();
51  const std::set<std::string>& iterateMemberNames = getIterateMemberNames();
52 
53  FILE* fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "r");
54 
55  if(fp) // check for all core table names in file, and force their presence
56  {
57  std::vector<unsigned int> foundVector;
58  char line[100];
59  for(const auto& name : contextMemberNames)
60  {
61  foundVector.push_back(false);
62  rewind(fp);
63  while(fgets(line, 100, fp))
64  {
65  if(strlen(line) < 1)
66  continue;
67  line[strlen(line) - 1] = '\0'; // remove endline
68  if(strcmp(line, ("ContextGroup/" + name).c_str()) == 0) // is match?
69  {
70  foundVector.back() = true;
71  break;
72  }
73  }
74  }
75 
76  for(const auto& name : backboneMemberNames)
77  {
78  foundVector.push_back(false);
79  rewind(fp);
80  while(fgets(line, 100, fp))
81  {
82  if(strlen(line) < 1)
83  continue;
84  line[strlen(line) - 1] = '\0'; // remove endline
85  if(strcmp(line, ("BackboneGroup/" + name).c_str()) == 0) // is match?
86  {
87  foundVector.back() = true;
88  break;
89  }
90  }
91  }
92 
93  for(const auto& name : iterateMemberNames)
94  {
95  foundVector.push_back(false);
96  rewind(fp);
97  while(fgets(line, 100, fp))
98  {
99  if(strlen(line) < 1)
100  continue;
101  line[strlen(line) - 1] = '\0'; // remove endline
102  if(strcmp(line, ("IterateGroup/" + name).c_str()) == 0) // is match?
103  {
104  foundVector.back() = true;
105  break;
106  }
107  }
108  }
109 
110  //look for optional Context table ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
111  {
112  foundVector.push_back(false);
113  rewind(fp);
114  while(fgets(line, 100, fp))
115  {
116  if(strlen(line) < 1)
117  continue;
118  line[strlen(line) - 1] = '\0'; // remove endline
119  if(strcmp(line,
120  ("ContextGroup/" +
121  ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE)
122  .c_str()) == 0) // is match?
123  {
124  foundVector.back() = true;
125  break;
126  }
127  }
128  }
129 
130  fclose(fp);
131 
132  // open file for appending the missing names
133  fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "a");
134  if(fp)
135  {
136  unsigned int i = 0;
137  for(const auto& name : contextMemberNames)
138  {
139  if(!foundVector[i])
140  fprintf(fp, "\nContextGroup/%s", name.c_str());
141 
142  ++i;
143  }
144  for(const auto& name : backboneMemberNames)
145  {
146  if(!foundVector[i])
147  fprintf(fp, "\nBackboneGroup/%s", name.c_str());
148 
149  ++i;
150  }
151  for(const auto& name : iterateMemberNames)
152  {
153  if(!foundVector[i])
154  fprintf(fp, "\nIterateGroup/%s", name.c_str());
155 
156  ++i;
157  }
158 
159  //last is optional Context table ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
160  if(!foundVector[i])
161  fprintf(
162  fp,
163  "\nContextGroup/%s",
164  ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE.c_str());
165 
166  fclose(fp);
167  }
168  else
169  {
170  __SS__ << "Failed to open core table info file for appending: "
171  << CORE_TABLE_INFO_FILENAME << __E__;
172  __SS_THROW__;
173  }
174  }
175  else
176  {
177  fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "w");
178  if(fp)
179  {
180  fprintf(fp, "ARTDAQ/*");
181  fprintf(fp, "\nConfigCore/*");
182  for(const auto& name : contextMemberNames)
183  fprintf(fp, "\nContextGroup/%s", name.c_str());
184  for(const auto& name : backboneMemberNames)
185  fprintf(fp, "\nBackboneGroup/%s", name.c_str());
186  for(const auto& name : iterateMemberNames)
187  fprintf(fp, "\nIterateGroup/%s", name.c_str());
188  fclose(fp);
189  }
190  else
191  {
192  __SS__ << "Failed to open core table info file: "
193  << CORE_TABLE_INFO_FILENAME << __E__;
194  __SS_THROW__;
195  }
196  }
197  } // end dump names of core tables
198 
199  __GEN_COUTV__(runTimeSeconds());
200 } // end constructor
201 
202 //==============================================================================
209 const std::map<std::string, TableInfo>& ConfigurationManagerRW::getAllTableInfo(
210  bool refresh /* = false */,
211  std::string* accumulatedWarnings /* = 0 */,
212  const std::string& errorFilterName /* = "" */,
213  bool getGroupKeys /* = false */,
214  bool getGroupInfo /* = false */,
215  bool initializeActiveGroups /* = false */)
216 {
217  // allTableInfo_ is container to be returned
218 
219  if(!refresh)
220  return allTableInfo_;
221 
222  // else refresh!
223  allTableInfo_.clear();
224 
225  TableBase* table;
226 
227  // existing configurations are defined by which infos are in TABLE_INFO_PATH
228  // can test that the class exists based on this
229  // and then which versions
230  __GEN_COUT__ << "======================================================== "
231  "getAllTableInfo start runTimeSeconds()="
232  << runTimeSeconds() << __E__;
233  {
234  __GEN_COUT__ << "Refreshing all! Extracting list of tables..." << __E__;
235  DIR* pDIR;
236  struct dirent* entry;
237  std::string path = TABLE_INFO_PATH;
238  char fileExt[] = TABLE_INFO_EXT;
239  const unsigned char MIN_TABLE_NAME_SZ = 3;
240 
241  const int numOfThreads = StringMacros::getConcurrencyCount() / 2;
242  __GEN_COUT__ << " getConcurrencyCount " << StringMacros::getConcurrencyCount()
243  << " ==> " << numOfThreads << " threads." << __E__;
244  if(numOfThreads < 2) // no multi-threading
245  {
246  if((pDIR = opendir(path.c_str())) != 0)
247  {
248  while((entry = readdir(pDIR)) != 0)
249  {
250  // enforce table name length
251  if(strlen(entry->d_name) < strlen(fileExt) + MIN_TABLE_NAME_SZ)
252  continue;
253 
254  // find file names with correct file extenstion
255  if(strcmp(&(entry->d_name[strlen(entry->d_name) - strlen(fileExt)]),
256  fileExt) != 0)
257  continue; // skip different extentions
258 
259  entry->d_name[strlen(entry->d_name) - strlen(fileExt)] =
260  '\0'; // remove file extension to get table name
261 
262  // 0 will force the creation of new instance (and reload from Info)
263  table = 0;
264 
265  try // only add valid table instances to maps
266  {
267  theInterface_->get(table,
268  entry->d_name,
269  0,
270  0,
271  true); // dont fill
272  }
273  catch(cet::exception const&)
274  {
275  if(table)
276  delete table;
277  table = 0;
278 
279  __GEN_COUT__ << "Skipping! No valid class found for... "
280  << entry->d_name << "\n";
281  continue;
282  }
283  catch(std::runtime_error& e)
284  {
285  if(table)
286  delete table;
287  table = 0;
288 
289  __GEN_COUT__ << "Skipping! No valid class found for... "
290  << entry->d_name << "\n";
291  __GEN_COUT__ << "Error: " << e.what() << __E__;
292 
293  // for a runtime_error, it is likely that columns are the problem
294  // the Table Editor needs to still fix these.. so attempt to
295  // proceed.
296  if(accumulatedWarnings)
297  {
298  if(errorFilterName == "" || errorFilterName == entry->d_name)
299  {
300  *accumulatedWarnings += std::string("\nIn table '") +
301  entry->d_name + "'..." +
302  e.what(); // global accumulate
303 
304  __SS__ << "Attempting to allow illegal columns!" << __E__;
305  *accumulatedWarnings += ss.str();
306  }
307 
308  // attempt to recover and build a mock-up
309  __GEN_COUT__ << "Attempting to allow illegal columns!"
310  << __E__;
311 
312  std::string returnedAccumulatedErrors;
313  try
314  {
315  table = new TableBase(entry->d_name,
316  &returnedAccumulatedErrors);
317  }
318  catch(...)
319  {
320  __GEN_COUT__ << "Skipping! Allowing illegal columns "
321  "didn't work either... "
322  << entry->d_name << "\n";
323  continue;
324  }
325  __GEN_COUT__
326  << "Error (but allowed): " << returnedAccumulatedErrors
327  << __E__;
328 
329  if(errorFilterName == "" || errorFilterName == entry->d_name)
330  *accumulatedWarnings +=
331  std::string("\nIn table '") + entry->d_name + "'..." +
332  returnedAccumulatedErrors; // global accumulate
333  }
334  else
335  continue;
336  }
337 
338  if(nameToTableMap_[entry->d_name]) // handle if instance existed
339  {
340  // copy the existing temporary versions! (or else all is lost)
341  std::set<TableVersion> versions =
342  nameToTableMap_[entry->d_name]->getStoredVersions();
343  for(auto& version : versions)
344  if(version.isTemporaryVersion())
345  {
346  try // do NOT let TableView::init() throw here
347  {
348  nameToTableMap_[entry->d_name]->setActiveView(
349  version);
350  table->copyView( // this calls TableView::init()
351  nameToTableMap_[entry->d_name]->getView(),
352  version,
353  username_);
354  }
355  catch(
356  ...) // do NOT let invalid temporary version throw at this
357  // point
358  {
359  } // just trust configurationBase throws out the failed version
360  }
361 
362  delete nameToTableMap_[entry->d_name];
363  nameToTableMap_[entry->d_name] = 0;
364  }
365 
366  nameToTableMap_[entry->d_name] = table;
367 
368  allTableInfo_[entry->d_name].tablePtr_ = table;
369  allTableInfo_[entry->d_name].versions_ =
370  theInterface_->getVersions(table);
371 
372  // also add any existing temporary versions to all table info
373  // because the interface wont find those versions
374  std::set<TableVersion> versions =
375  nameToTableMap_[entry->d_name]->getStoredVersions();
376  for(auto& version : versions)
377  if(version.isTemporaryVersion())
378  {
379  allTableInfo_[entry->d_name].versions_.emplace(version);
380  }
381  } //end table name handling from directory
382  closedir(pDIR);
383  }
384  }
385  else //multi-threading
386  {
387  int threadsLaunched = 0;
388  int foundThreadIndex = 0;
389  std::string tableName;
390 
391  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
392  for(int i = 0; i < numOfThreads; ++i)
393  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
394 
395  std::vector<std::shared_ptr<ots::TableInfo>> sharedTableInfoPtrs;
396 
397  if((pDIR = opendir(path.c_str())) != 0)
398  {
399  while((entry = readdir(pDIR)) != 0)
400  {
401  // enforce table name length
402  if(strlen(entry->d_name) < strlen(fileExt) + MIN_TABLE_NAME_SZ)
403  continue;
404 
405  // find file names with correct file extenstion
406  if(strcmp(&(entry->d_name[strlen(entry->d_name) - strlen(fileExt)]),
407  fileExt) != 0)
408  continue; // skip different extentions
409 
410  entry->d_name[strlen(entry->d_name) - strlen(fileExt)] =
411  '\0'; // remove file extension to get table name
412  tableName =
413  entry
414  ->d_name; //copy immediate, thread does not like using pointer to char*, corrupts immediately on wraparound
415 
416  //make temporary table info for thread
417  sharedTableInfoPtrs.push_back(std::make_shared<ots::TableInfo>());
418  sharedTableInfoPtrs.back()->accumulatedWarnings_ =
419  accumulatedWarnings ? "ALLOW"
420  : ""; //mark to allow accumulated warnings
421 
422  if(threadsLaunched >= numOfThreads)
423  {
424  //find availableThreadIndex
425  foundThreadIndex = -1;
426  size_t ii = 0;
427  while(foundThreadIndex == -1)
428  {
429  for(int i = 0; i < numOfThreads; ++i)
430  if(*(threadDone[i]))
431  {
432  foundThreadIndex = i;
433  break;
434  }
435  if(foundThreadIndex == -1)
436  {
437  __GEN_COUT_TYPE__(TLVL_DEBUG + 2)
438  << __COUT_HDR__
439  << "Waiting for available thread... iteration # "
440  << ii << __E__;
441  if(++ii > 100 /* 1s */ * 10)
442  {
443  __GEN_SS__ << "Threads seem to be stuck getting "
444  "table info! Timeout while waiting..."
445  << __E__;
446  __GEN_SS_THROW__;
447  }
448  usleep(10000);
449  }
450  } //end thread search loop
451  threadsLaunched = numOfThreads - 1;
452  }
453  __GEN_COUTT__ << "Starting thread... " << foundThreadIndex
454  << " for table " << tableName << __E__;
455 
456  *(threadDone[foundThreadIndex]) = false;
457  std::thread(
458  [](ConfigurationManagerRW* theCfgMgr,
459  std::string theTableName,
460  TableBase* existingTable,
461  std::shared_ptr<ots::TableInfo> theTableInfo,
462  std::shared_ptr<std::atomic<bool>> theThreadDone) {
464  theTableName,
465  existingTable,
466  theTableInfo,
467  theThreadDone);
468  },
469  this,
470  tableName,
471  nameToTableMap_[tableName],
472  sharedTableInfoPtrs.back(),
473  threadDone[foundThreadIndex])
474  .detach();
475 
476  ++threadsLaunched;
477  ++foundThreadIndex;
478  } //end table name handling from directory
479  closedir(pDIR);
480  } //end tableInfo thread loop
481 
482  //check for all threads done
483  do
484  {
485  foundThreadIndex = -1;
486  for(int i = 0; i < numOfThreads; ++i)
487  if(!*(threadDone[i]))
488  {
489  foundThreadIndex = i;
490  break;
491  }
492  if(foundThreadIndex != -1)
493  {
494  __GEN_COUTT__ << "Waiting for thread to finish... "
495  << foundThreadIndex << __E__;
496  usleep(10000);
497  }
498  } while(foundThreadIndex != -1); //end thread done search loop
499 
500  //threads done now, so copy table info
501  for(auto& tableInfo : sharedTableInfoPtrs)
502  {
503  if(tableInfo->tablePtr_ == nullptr)
504  {
505  __SS__ << "Fatal error occurred loading table info into cache! "
506  "Perhaps there is an illegal Table schema definition? "
507  "Check logs to resolve."
508  << __E__;
509  __SS_THROW__;
510  }
511  __GEN_COUT_TYPE__(TLVL_DEBUG + 3)
512  << __COUT_HDR__ << "Copying table info for "
513  << tableInfo->tablePtr_->getTableName() << __E__;
514  nameToTableMap_[tableInfo->tablePtr_->getTableName()] =
515  tableInfo->tablePtr_;
516  allTableInfo_[tableInfo->tablePtr_->getTableName()].tablePtr_ =
517  tableInfo->tablePtr_;
518  allTableInfo_[tableInfo->tablePtr_->getTableName()].versions_ =
519  tableInfo->versions_;
520  } //end copy group info loop
521  }
522  __GEN_COUT__ << "Extracting list of tables complete." << __E__;
523  } //end Extracting list of tables
524 
525  // call init to load active versions by default, activate with warnings allowed (assuming development going on)
526  if(initializeActiveGroups)
527  {
528  __GEN_COUT__ << "Now initializing..." << __E__;
529  // if there is a filter name, do not include init warnings (it just scares people in the table editor)
530  std::string tmpAccumulateWarnings;
531  init(0 /*accumulatedErrors*/,
532  false /*initForWriteAccess*/,
533  accumulatedWarnings ? &tmpAccumulateWarnings : nullptr);
534 
535  if(accumulatedWarnings && errorFilterName == "")
536  *accumulatedWarnings += tmpAccumulateWarnings;
537  }
538  __GEN_COUT__ << "======================================================== "
539  "getAllTableInfo end runTimeSeconds()="
540  << runTimeSeconds() << __E__;
541 
542  // get Group Info too!
543  if(getGroupKeys || getGroupInfo)
544  {
545  allGroupInfo_.clear();
546  try
547  {
548  // build allGroupInfo_ for the ConfigurationManagerRW
549 
550  std::set<std::string /*name*/> tableGroups =
551  theInterface_->getAllTableGroupNames();
552  __GEN_COUT__ << "Number of Groups: " << tableGroups.size() << __E__;
553 
554  __GEN_COUTT__ << "Group Info start runTimeSeconds()=" << runTimeSeconds()
555  << __E__;
556 
557  TableGroupKey key;
558  std::string name;
559  for(const auto& fullName : tableGroups)
560  {
561  TableGroupKey::getGroupNameAndKey(fullName, name, key);
562  allGroupInfo_[name].keys_.emplace(key); //store in cache
563  }
564 
565  __GEN_COUTT__ << "Group Keys end runTimeSeconds()=" << runTimeSeconds()
566  << __E__;
567 
568  // for each group get member map & comment, author, time, and type for latest key
569  if(getGroupInfo)
570  {
571  const int numOfThreads = StringMacros::getConcurrencyCount() / 2;
572  __GEN_COUT__ << " getConcurrencyCount "
573  << StringMacros::getConcurrencyCount() << " ==> "
574  << numOfThreads << " threads." << __E__;
575  if(numOfThreads < 2) // no multi-threading
576  for(auto& groupInfo : allGroupInfo_)
577  {
578  try
579  {
580  groupInfo.second.latestKey_ = groupInfo.second.getLastKey();
582  groupInfo.first /*groupName*/,
583  groupInfo.second.latestKey_,
584  false /*doActivate*/,
585  &groupInfo.second.latestKeyMemberMap_ /*groupMembers*/,
586  0 /*progressBar*/,
587  0 /*accumulateErrors*/,
588  &groupInfo.second.latestKeyGroupComment_,
589  &groupInfo.second.latestKeyGroupAuthor_,
590  &groupInfo.second.latestKeyGroupCreationTime_,
591  true /*doNotLoadMember*/,
592  &groupInfo.second.latestKeyGroupTypeString_);
593  }
594  catch(...)
595  {
596  __GEN_COUT_WARN__ << "Error occurred loading latest group "
597  "info into cache for '"
598  << groupInfo.first << "("
599  << groupInfo.second.latestKey_ << ")'..."
600  << __E__;
601  groupInfo.second.latestKey_ = TableGroupKey::INVALID;
602  groupInfo.second.latestKeyGroupComment_ =
603  ConfigurationManager::UNKNOWN_INFO;
604  groupInfo.second.latestKeyGroupAuthor_ =
605  ConfigurationManager::UNKNOWN_INFO;
606  groupInfo.second.latestKeyGroupCreationTime_ =
607  ConfigurationManager::UNKNOWN_TIME;
608  groupInfo.second.latestKeyGroupTypeString_ =
609  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
610  groupInfo.second.latestKeyMemberMap_ = {};
611  }
612  } // end group info loop
613  else //multi-threading
614  {
615  int threadsLaunched = 0;
616  int foundThreadIndex = 0;
617 
618  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
619  for(int i = 0; i < numOfThreads; ++i)
620  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
621 
622  std::vector<std::shared_ptr<ots::GroupInfo>> sharedGroupInfoPtrs;
623 
624  for(auto& groupInfo : allGroupInfo_)
625  {
626  //make temporary group info for thread
627  sharedGroupInfoPtrs.push_back(std::make_shared<ots::GroupInfo>());
628 
629  if(threadsLaunched >= numOfThreads)
630  {
631  //find availableThreadIndex
632  foundThreadIndex = -1;
633  while(foundThreadIndex == -1)
634  {
635  for(int i = 0; i < numOfThreads; ++i)
636  if(*(threadDone[i]))
637  {
638  foundThreadIndex = i;
639  break;
640  }
641  if(foundThreadIndex == -1)
642  {
643  __GEN_COUTT__ << "Waiting for available thread..."
644  << __E__;
645  usleep(10000);
646  }
647  } //end thread search loop
648  threadsLaunched = numOfThreads - 1;
649  }
650  __GEN_COUTT__ << "Starting thread... " << foundThreadIndex
651  << " for " << groupInfo.first << "("
652  << groupInfo.second.getLastKey() << ")" << __E__;
653 
654  *(threadDone[foundThreadIndex]) = false;
655 
656  std::thread(
657  [](ConfigurationManagerRW* theCfgMgr,
658  std::string theGroupName,
659  ots::TableGroupKey theGroupKey,
660  std::shared_ptr<ots::GroupInfo> theGroupInfo,
661  std::shared_ptr<std::atomic<bool>> theThreadDone) {
663  theCfgMgr,
664  theGroupName,
665  theGroupKey,
666  theGroupInfo,
667  theThreadDone);
668  },
669  this,
670  groupInfo.first,
671  groupInfo.second.getLastKey(),
672  sharedGroupInfoPtrs.back(),
673  threadDone[foundThreadIndex])
674  .detach();
675 
676  ++threadsLaunched;
677  ++foundThreadIndex;
678  } //end groupInfo thread loop
679 
680  //check for all threads done
681  do
682  {
683  foundThreadIndex = -1;
684  for(int i = 0; i < numOfThreads; ++i)
685  if(!*(threadDone[i]))
686  {
687  foundThreadIndex = i;
688  break;
689  }
690  if(foundThreadIndex != -1)
691  {
692  __GEN_COUTT__ << "Waiting for thread to finish... "
693  << foundThreadIndex << __E__;
694  usleep(10000);
695  }
696  } while(foundThreadIndex != -1); //end thread done search loop
697 
698  //threads done now, so copy group info
699  size_t i = 0;
700  for(auto& groupInfo : allGroupInfo_)
701  {
702  groupInfo.second.latestKey_ = sharedGroupInfoPtrs[i]->latestKey_;
703  groupInfo.second.latestKeyGroupComment_ =
704  sharedGroupInfoPtrs[i]->latestKeyGroupComment_;
705  groupInfo.second.latestKeyGroupAuthor_ =
706  sharedGroupInfoPtrs[i]->latestKeyGroupAuthor_;
707  groupInfo.second.latestKeyGroupCreationTime_ =
708  sharedGroupInfoPtrs[i]->latestKeyGroupCreationTime_;
709  groupInfo.second.latestKeyGroupTypeString_ =
710  sharedGroupInfoPtrs[i]->latestKeyGroupTypeString_;
711  groupInfo.second.latestKeyMemberMap_ =
712  sharedGroupInfoPtrs[i]->latestKeyMemberMap_;
713  ++i;
714  } //end copy group info loop
715 
716  } //end multi-thread handling
717  }
718  } // end get group info
719  catch(const std::runtime_error& e)
720  {
721  __SS__
722  << "A fatal error occurred reading the info for all table groups. Error: "
723  << e.what() << __E__;
724  __GEN_COUT_ERR__ << "Error at time: " << time(0) << "\n" << ss.str();
725  if(accumulatedWarnings)
726  *accumulatedWarnings += ss.str();
727  else
728  throw;
729  }
730  catch(...)
731  {
732  __SS__ << "An unknown fatal error occurred reading the info for all table "
733  "groups."
734  << __E__;
735  try
736  {
737  throw;
738  } //one more try to printout extra info
739  catch(const std::exception& e)
740  {
741  ss << "Exception message: " << e.what();
742  }
743  catch(...)
744  {
745  }
746  __GEN_COUT_ERR__ << "\n" << ss.str();
747  if(accumulatedWarnings)
748  *accumulatedWarnings += ss.str();
749  else
750  throw;
751  }
752  __GEN_COUTT__ << "Group Info end runTimeSeconds()=" << runTimeSeconds() << __E__;
753  } //end getGroupInfo
754  else
755  __GEN_COUTT__ << "Table Info end runTimeSeconds()=" << runTimeSeconds() << __E__;
756 
757  return allTableInfo_;
758 } // end getAllTableInfo()
759 
760 //==============================================================================
763  ConfigurationManagerRW* cfgMgr,
764  std::string tableName,
765  TableBase* existingTable,
766  std::shared_ptr<ots::TableInfo> tableInfo,
767  std::shared_ptr<std::atomic<bool>> threadDone)
768 try
769 {
770  __COUTT__ << "Thread started... table " << tableName << __E__;
771 
772  // 0 will force the creation of new instance (and reload from Info)
773  tableInfo->tablePtr_ = 0;
774 
775  try // only add valid table instances to maps
776  {
777  cfgMgr->theInterface_->get(tableInfo->tablePtr_,
778  tableName,
779  0,
780  0,
781  true); // dont fill
782  }
783  catch(cet::exception const&)
784  {
785  if(tableInfo->tablePtr_)
786  delete tableInfo->tablePtr_;
787  tableInfo->tablePtr_ = 0;
788 
789  __COUT__ << "Skipping! No valid class found for... " << tableName << "\n";
790  *(threadDone) = true;
791  return;
792  }
793  catch(std::runtime_error& e)
794  {
795  if(tableInfo->tablePtr_)
796  delete tableInfo->tablePtr_;
797  tableInfo->tablePtr_ = 0;
798 
799  __COUT__ << "Skipping! No valid class found for... " << tableName << "\n";
800  __COUTT__ << "Error: " << e.what() << __E__;
801 
802  // for a runtime_error, it is likely that columns are the problem
803  // the Table Editor needs to still fix these.. so attempt to
804  // proceed.
805  if(tableInfo->accumulatedWarnings_ == "ALLOW")
806  {
807  tableInfo->accumulatedWarnings_ = "";
808  if(1) //errorFilterName == "" || errorFilterName == tableName)
809  {
810  tableInfo->accumulatedWarnings_ += std::string("\nIn table '") +
811  tableName + "'..." +
812  e.what(); // global accumulate
813 
814  __SS__ << "Attempting to allow illegal columns!" << __E__;
815  tableInfo->accumulatedWarnings_ += ss.str();
816  }
817 
818  // attempt to recover and build a mock-up
819  __COUT__ << "Attempting to allow illegal columns!" << __E__;
820 
821  std::string returnedAccumulatedErrors;
822  try
823  {
824  tableInfo->tablePtr_ =
825  new TableBase(tableName, &returnedAccumulatedErrors);
826  }
827  catch(...)
828  {
829  __COUT__ << "Skipping! Allowing illegal columns didn't work either... "
830  << tableName << "\n";
831  *(threadDone) = true;
832  return;
833  }
834  __COUT_WARN__ << "Error (but allowed): " << returnedAccumulatedErrors
835  << __E__;
836 
837  if(1) //errorFilterName == "" || errorFilterName == entry->d_name)
838  tableInfo->accumulatedWarnings_ +=
839  std::string("\nIn table '") + tableName + "'..." +
840  returnedAccumulatedErrors; // global accumulate
841  }
842  else
843  {
844  tableInfo->accumulatedWarnings_ = "";
845  *(threadDone) = true;
846  return;
847  }
848  }
849 
850  if(existingTable) // handle if instance existed
851  {
852  __COUTT__ << "Copying temporary version from existing table object for "
853  << tableName << __E__;
854  // copy the existing temporary versions! (or else all is lost)
855  std::set<TableVersion> versions = existingTable->getStoredVersions();
856  for(auto& version : versions)
857  if(version.isTemporaryVersion())
858  {
859  try // do NOT let TableView::init() throw here
860  {
861  existingTable->setActiveView(version);
862  tableInfo->tablePtr_->copyView( // this calls TableView::init()
863  existingTable->getView(),
864  version,
865  cfgMgr->username_);
866  }
867  catch(...) // do NOT let invalid temporary version throw at this
868  // point
869  {
870  } // just trust configurationBase throws out the failed version
871  }
872 
873  delete existingTable;
874  existingTable = 0;
875  }
876 
877  tableInfo->versions_ = cfgMgr->theInterface_->getVersions(tableInfo->tablePtr_);
878 
879  // also add any existing temporary versions to all table info
880  // because the interface wont find those versions
881  std::set<TableVersion> versions = tableInfo->tablePtr_->getStoredVersions();
882  for(auto& version : versions)
883  if(version.isTemporaryVersion())
884  {
885  tableInfo->versions_.emplace(version);
886  }
887 
888  __COUTT__ << "Thread done... table " << tableName << __E__;
889  *(threadDone) = true;
890 } // end loadTableInfoThread
891 catch(...)
892 {
893  __COUT_ERR__ << "Error occurred loading latest table info into cache for '"
894  << tableName << "'..." << __E__;
895  *(threadDone) = true;
896 } // end loadTableInfoThread catch
897 
898 //==============================================================================
902  const std::string& groupName,
903  const TableGroupKey& groupKey,
904  bool doActivate /*=false*/,
905  std::map<std::string /*table name*/, TableVersion>*
906  groupMembers /*=0 , note: db time intensive! */,
907  ProgressBar* progressBar /*=0*/,
908  std::string* accumulatedWarnings /*=0*/,
909  std::string* groupComment /*=0 , note: in metadata */,
910  std::string* groupAuthor /*=0 , note: in metadata */,
911  std::string* groupCreateTime /*=0 , note: in metadata */,
912  bool doNotLoadMembers /*=false*/,
913  std::string* groupTypeString /*=0 , note: db time intensive! */,
914  std::map<std::string /*name*/, std::string /*alias*/>*
915  groupAliases /*=0 , note: in metadata */,
916  ConfigurationManager::LoadGroupType
917  groupTypeToLoad /*=ConfigurationManager::LoadGroupType::ALL_TYPES*/,
918  bool ignoreVersionTracking /*=false*/)
919 {
921  groupKey,
922  doActivate,
923  groupMembers,
924  progressBar,
925  accumulatedWarnings,
926  groupComment,
927  groupAuthor,
928  groupCreateTime,
929  doNotLoadMembers,
930  groupTypeString,
931  groupAliases,
932  groupTypeToLoad,
933  ignoreVersionTracking);
934 
935  if(!groupMembers || !groupMembers->size() || !groupComment || groupKey.isInvalid() ||
936  !groupAuthor || !groupCreateTime)
937  return;
938 
939  //treat successfull load as latest group key
940  auto groupInfo = allGroupInfo_.find(groupName);
941  if(groupInfo == allGroupInfo_.end())
942  return; //ignore if no group info cache
943 
944  groupInfo->second.latestKey_ = groupKey;
945  groupInfo->second.latestKeyGroupComment_ = *groupComment;
946  groupInfo->second.latestKeyGroupAuthor_ = *groupAuthor;
947  groupInfo->second.latestKeyGroupCreationTime_ = *groupCreateTime;
948  if(groupTypeString) //assume unlikely to change types
949  groupInfo->second.latestKeyGroupTypeString_ = *groupTypeString;
950  groupInfo->second.latestKeyMemberMap_ = *groupMembers;
951 
952 } //end loadTableGroup() RW version
953 
954 //==============================================================================
957  ConfigurationManagerRW* cfgMgr,
958  std::string groupName,
959  ots::TableGroupKey groupKey,
960  std::shared_ptr<ots::GroupInfo> groupInfo,
961  std::shared_ptr<std::atomic<bool>> threadDone)
962 try
963 {
964  __COUTT__ << "Thread started... " << groupName << "(" << groupKey << ")" << __E__;
965 
966  groupInfo->latestKey_ = groupKey;
967  cfgMgr->loadTableGroup(groupName,
968  groupKey,
969  false /*doActivate*/,
970  &(groupInfo->latestKeyMemberMap_) /*groupMembers*/,
971  0 /*progressBar*/,
972  0 /*accumulateErrors*/,
973  &(groupInfo->latestKeyGroupComment_),
974  &(groupInfo->latestKeyGroupAuthor_),
975  &(groupInfo->latestKeyGroupCreationTime_),
976  true /*doNotLoadMember*/,
977  &(groupInfo->latestKeyGroupTypeString_));
978 
979  *(threadDone) = true;
980 } // end loadTableGroupThread
981 catch(...)
982 {
983  __COUT_WARN__ << "Error occurred loading latest group info into cache for '"
984  << groupName << "(" << groupInfo->latestKey_ << ")'..." << __E__;
985  groupInfo->latestKey_ = TableGroupKey::INVALID;
986  groupInfo->latestKeyGroupComment_ = ConfigurationManager::UNKNOWN_INFO;
987  groupInfo->latestKeyGroupAuthor_ = ConfigurationManager::UNKNOWN_INFO;
988  groupInfo->latestKeyGroupCreationTime_ = ConfigurationManager::UNKNOWN_TIME;
989  groupInfo->latestKeyGroupTypeString_ = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
990  groupInfo->latestKeyMemberMap_ = {};
991  *(threadDone) = true;
992 } // end loadTableGroupThread catch
993 
994 //==============================================================================
999  ConfigurationManagerRW* cfgMgr,
1000  std::string groupName,
1001  ots::TableGroupKey groupKeyToCompare,
1002  const std::map<std::string, TableVersion>& groupMemberMap,
1003  const std::map<std::string /*name*/, std::string /*alias*/>& memberTableAliases,
1004  std::atomic<bool>* foundIdentical,
1005  ots::TableGroupKey* identicalKey,
1006  std::mutex* threadMutex,
1007  std::shared_ptr<std::atomic<bool>> threadDone)
1008 try
1009 {
1010  std::map<std::string /*name*/, TableVersion /*version*/> compareToMemberMap;
1011  std::map<std::string /*name*/, std::string /*alias*/> compareToMemberTableAliases;
1012  std::map<std::string /*name*/, std::string /*alias*/>*
1013  compareToMemberTableAliasesPtr = nullptr;
1014  if(memberTableAliases
1015  .size()) //only give pointer if necessary, without will load group faster
1016  compareToMemberTableAliasesPtr = &compareToMemberTableAliases;
1017 
1018  cfgMgr->loadTableGroup(groupName,
1019  groupKeyToCompare,
1020  false /*doActivate*/,
1021  &compareToMemberMap /*memberMap*/,
1022  0, /*progressBar*/
1023  0, /*accumulatedWarnings*/
1024  0, /*groupComment*/
1025  0,
1026  0, /*null pointers*/
1027  true /*doNotLoadMember*/,
1028  0 /*groupTypeString*/,
1029  compareToMemberTableAliasesPtr);
1030 
1031  bool debug = false;
1032  if(TTEST(9)) // = DEBUG+9
1033  {
1034  debug = true;
1035  for(auto& memberPair : groupMemberMap)
1036  __COUTS__(9) << "member " << memberPair.first << " (" << memberPair.second
1037  << ")" << __E__;
1038  for(auto& memberPair : compareToMemberMap)
1039  __COUTS__(9) << "compare " << groupName << " (" << groupKeyToCompare
1040  << ") member:" << memberPair.first << " (" << memberPair.second
1041  << ")" << __E__;
1042  }
1043 
1044  bool isDifferent = false;
1045  for(auto& memberPair : groupMemberMap)
1046  {
1047  //groups can be different if Alias names are different
1048  // or if resolved Alias versions are different (for the current active backbone)
1049  if(memberTableAliases.find(memberPair.first) != memberTableAliases.end())
1050  {
1051  // handle this table as alias, not version
1052  if(compareToMemberTableAliases.find(memberPair.first) ==
1053  compareToMemberTableAliases.end() || // alias is missing
1054  memberTableAliases.at(memberPair.first) !=
1055  compareToMemberTableAliases.at(memberPair.first))
1056  { // then different
1057  isDifferent = true;
1058  if(debug)
1059  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare
1060  << ") on alias " << memberPair.first << __E__;
1061  break;
1062  }
1063  } // else check if compareTo group is using an alias for table
1064  else if(compareToMemberTableAliases.find(memberPair.first) !=
1065  compareToMemberTableAliases.end())
1066  {
1067  // then different
1068  isDifferent = true;
1069  if(debug)
1070  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare
1071  << ") on reverse alias " << memberPair.first << __E__;
1072  break;
1073 
1074  } // else handle as table version comparison
1075 
1076  //normal table version check
1077  if(compareToMemberMap.find(memberPair.first) ==
1078  compareToMemberMap.end() || // name is missing
1079  memberPair.second !=
1080  compareToMemberMap.at(memberPair.first)) // or version mismatch
1081  {
1082  // then different
1083  isDifferent = true;
1084  if(debug)
1085  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare
1086  << ") on mismatch " << memberPair.first << __E__;
1087  break;
1088  }
1089  } //end table version mismatch checking loop
1090 
1091  // check member size for exact match
1092  if(!isDifferent &&
1093  groupMemberMap.size() !=
1094  compareToMemberMap
1095  .size()) // different size, so not same (groupMemberMap is a subset of memberPairs)
1096  {
1097  isDifferent = true;
1098 
1099  if(debug)
1100  __COUTT__ << "diff " << groupName << " (" << groupKeyToCompare << ") on size "
1101  << __E__;
1102  }
1103 
1104  if(!isDifferent) //found an exact match!
1105  {
1106  *foundIdentical = true;
1107  __COUT__ << "=====> Found exact match with key: " << groupKeyToCompare << __E__;
1108 
1109  std::lock_guard<std::mutex> lock(*threadMutex);
1110  *identicalKey = groupKeyToCompare;
1111  }
1112 
1113  *(threadDone) = true;
1114 } // end compareTableGroupThread
1115 catch(...)
1116 {
1117  __COUT_WARN__ << "Error occurred comparing group '" << groupName << "("
1118  << groupKeyToCompare << ")'..." << __E__;
1119 
1120  *(threadDone) = true;
1121 } // end compareTableGroupThread catch
1122 
1123 //==============================================================================
1127 std::map<std::string /*table name*/,
1128  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
1130 {
1131  std::map<std::string /*table name*/,
1132  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
1134 
1135  // always have scratch alias for each table that has a scratch version
1136  // overwrite map entry if necessary
1137  if(!ConfigurationInterface::isVersionTrackingEnabled())
1138  for(const auto& tableInfo : allTableInfo_)
1139  for(const auto& version : tableInfo.second.versions_)
1140  if(version.isScratchVersion())
1141  retMap[tableInfo.first][ConfigurationManager::SCRATCH_VERSION_ALIAS] =
1142  TableVersion(TableVersion::SCRATCH);
1143 
1144  return retMap;
1145 } // end getVersionAliases()
1146 
1147 //==============================================================================
1151 void ConfigurationManagerRW::activateTableGroup(const std::string& tableGroupName,
1152  TableGroupKey tableGroupKey,
1153  std::string* accumulatedTreeErrors,
1154  std::string* groupTypeString)
1155 {
1156  try
1157  {
1158  loadTableGroup(tableGroupName,
1159  tableGroupKey,
1160  true, // loads and activates
1161  0, // no members needed
1162  0, // no progress bar
1163  accumulatedTreeErrors, // accumulate warnings or not
1164  0 /* groupComment */,
1165  0 /* groupAuthor */,
1166  0 /* groupCreateTime */,
1167  false /* doNotLoadMember */,
1168  groupTypeString);
1169  }
1170  catch(...)
1171  {
1172  __GEN_COUT_ERR__ << "There were errors, so de-activating group: "
1173  << tableGroupName << " (" << tableGroupKey << ")" << __E__;
1174  try // just in case any lingering pieces, let's deactivate
1175  {
1176  destroyTableGroup(tableGroupName, true);
1177  }
1178  catch(...)
1179  {
1180  }
1181  throw; // re-throw original exception
1182  }
1183 
1184  __GEN_COUT_INFO__ << "Updating persistent active groups to "
1185  << ConfigurationManager::ACTIVE_GROUPS_FILENAME << " ..." << __E__;
1186 
1187  __COUT_INFO__ << "Active Context table group: " << theContextTableGroup_ << "("
1188  << (theContextTableGroupKey_
1189  ? theContextTableGroupKey_->toString().c_str()
1190  : "-1")
1191  << ")" << __E__;
1192  __COUT_INFO__ << "Active Backbone table group: " << theBackboneTableGroup_ << "("
1193  << (theBackboneTableGroupKey_
1194  ? theBackboneTableGroupKey_->toString().c_str()
1195  : "-1")
1196  << ")" << __E__;
1197  __COUT_INFO__ << "Active Iterate table group: " << theIterateTableGroup_ << "("
1198  << (theIterateTableGroupKey_
1199  ? theIterateTableGroupKey_->toString().c_str()
1200  : "-1")
1201  << ")" << __E__;
1202  __COUT_INFO__ << "Active Configuration table group: " << theConfigurationTableGroup_
1203  << "("
1204  << (theConfigurationTableGroupKey_
1205  ? theConfigurationTableGroupKey_->toString().c_str()
1206  : "-1")
1207  << ")" << __E__;
1208 
1210  FILE* fp = fopen(fn.c_str(), "w");
1211  if(!fp)
1212  {
1213  __SS__ << "Fatal Error! Unable to open the file "
1215  << " for editing! Is there a permissions problem?" << __E__;
1216  __GEN_COUT_ERR__ << ss.str();
1217  __SS_THROW__;
1218  return;
1219  }
1220  fprintf(fp, "%s\n", theContextTableGroup_.c_str());
1221  fprintf(
1222  fp,
1223  "%s\n",
1224  theContextTableGroupKey_ ? theContextTableGroupKey_->toString().c_str() : "-1");
1225  fprintf(fp, "%s\n", theBackboneTableGroup_.c_str());
1226  fprintf(
1227  fp,
1228  "%s\n",
1229  theBackboneTableGroupKey_ ? theBackboneTableGroupKey_->toString().c_str() : "-1");
1230  fprintf(fp, "%s\n", theIterateTableGroup_.c_str());
1231  fprintf(
1232  fp,
1233  "%s\n",
1234  theIterateTableGroupKey_ ? theIterateTableGroupKey_->toString().c_str() : "-1");
1235  fprintf(fp, "%s\n", theConfigurationTableGroup_.c_str());
1236  fprintf(fp,
1237  "%s\n",
1238  theConfigurationTableGroupKey_
1239  ? theConfigurationTableGroupKey_->toString().c_str()
1240  : "-1");
1241  fclose(fp);
1242 
1243  // save last activated group
1244  {
1245  std::pair<std::string /*group name*/, TableGroupKey> activatedGroup(
1246  std::string(tableGroupName), tableGroupKey);
1247  if(theConfigurationTableGroupKey_ &&
1248  theConfigurationTableGroup_ == tableGroupName &&
1249  *theConfigurationTableGroupKey_ == tableGroupKey)
1250  {
1251  ConfigurationManager::saveGroupNameAndKey(
1252  activatedGroup,
1253  ConfigurationManager::LAST_ACTIVATED_CONFIG_GROUP_FILE,
1254  false /* appendMode */,
1255  username_);
1256  ConfigurationManager::saveGroupNameAndKey(
1257  activatedGroup,
1258  ConfigurationManager::ACTIVATED_CONFIGS_FILE,
1259  true /* appendMode */,
1260  username_);
1261  }
1262  else if(theContextTableGroupKey_ && theContextTableGroup_ == tableGroupName &&
1263  *theContextTableGroupKey_ == tableGroupKey)
1264  {
1265  ConfigurationManager::saveGroupNameAndKey(
1266  activatedGroup,
1267  ConfigurationManager::LAST_ACTIVATED_CONTEXT_GROUP_FILE,
1268  false /* appendMode */,
1269  username_);
1270  ConfigurationManager::saveGroupNameAndKey(
1271  activatedGroup,
1272  ConfigurationManager::ACTIVATED_CONTEXTS_FILE,
1273  true /* appendMode */,
1274  username_);
1275  }
1276  else if(theBackboneTableGroupKey_ && theBackboneTableGroup_ == tableGroupName &&
1277  *theBackboneTableGroupKey_ == tableGroupKey)
1278  {
1279  ConfigurationManager::saveGroupNameAndKey(
1280  activatedGroup,
1281  ConfigurationManager::LAST_ACTIVATED_BACKBONE_GROUP_FILE,
1282  false /* appendMode */,
1283  username_);
1284  ConfigurationManager::saveGroupNameAndKey(
1285  activatedGroup,
1286  ConfigurationManager::ACTIVATED_BACKBONES_FILE,
1287  true /* appendMode */,
1288  username_);
1289  }
1290  else if(theIterateTableGroupKey_ && theIterateTableGroup_ == tableGroupName &&
1291  *theIterateTableGroupKey_ == tableGroupKey)
1292  {
1293  ConfigurationManager::saveGroupNameAndKey(
1294  activatedGroup,
1295  ConfigurationManager::LAST_ACTIVATED_ITERATE_GROUP_FILE,
1296  false /* appendMode */,
1297  username_);
1298  ConfigurationManager::saveGroupNameAndKey(
1299  activatedGroup,
1300  ConfigurationManager::ACTIVATED_ITERATES_FILE,
1301  true /* appendMode */,
1302  username_);
1303  }
1304  } // end save last activated group
1305 
1306 } // end activateTableGroup()
1307 
1308 //==============================================================================
1313  TableVersion sourceViewVersion)
1314 {
1315  __GEN_COUT_INFO__ << "Creating temporary backbone view from version "
1316  << sourceViewVersion << __E__;
1317 
1318  // find common available temporary version among backbone members
1319  TableVersion tmpVersion =
1320  TableVersion::getNextTemporaryVersion(); // get the default temporary version
1321  TableVersion retTmpVersion;
1322  auto backboneMemberNames = ConfigurationManager::getBackboneMemberNames();
1323  for(auto& name : backboneMemberNames)
1324  {
1325  retTmpVersion =
1327  if(retTmpVersion < tmpVersion)
1328  tmpVersion = retTmpVersion;
1329  }
1330 
1331  __GEN_COUT__ << "Common temporary backbone version found as " << tmpVersion << __E__;
1332 
1333  // create temporary views from source version to destination temporary version
1334  for(auto& name : backboneMemberNames)
1335  {
1336  retTmpVersion =
1337  getTableByName(name)->createTemporaryView(sourceViewVersion, tmpVersion);
1338  if(retTmpVersion != tmpVersion)
1339  {
1340  __SS__ << "Failure! Temporary view requested was " << tmpVersion
1341  << ". Mismatched temporary view created: " << retTmpVersion << __E__;
1342  __GEN_COUT_ERR__ << ss.str();
1343  __SS_THROW__;
1344  }
1345  }
1346 
1347  return tmpVersion;
1348 } // end createTemporaryBackboneView()
1349 
1350 //==============================================================================
1351 TableBase* ConfigurationManagerRW::getTableByName(const std::string& tableName)
1352 {
1353  if(nameToTableMap_.find(tableName) == nameToTableMap_.end())
1354  {
1355  if(tableName == ConfigurationManager::ARTDAQ_TOP_TABLE_NAME)
1356  {
1357  __GEN_COUT_WARN__
1358  << "Since target table was the artdaq top configuration level, "
1359  "attempting to help user by appending to core tables file: "
1360  << CORE_TABLE_INFO_FILENAME << __E__;
1361  FILE* fp = fopen((CORE_TABLE_INFO_FILENAME).c_str(), "a");
1362  if(fp)
1363  {
1364  fprintf(fp, "\nARTDAQ/*");
1365  fclose(fp);
1366  }
1367  }
1368 
1369  __SS__ << "Table not found with name: " << tableName << __E__;
1370  size_t f;
1371  if((f = tableName.find(' ')) != std::string::npos)
1372  ss << "There was a space character found in the table name needle at "
1373  "position "
1374  << f << " in the string (was this intended?). " << __E__;
1375 
1376  ss << "\nIf you think this table should exist in the core set of tables, try "
1377  "running 'UpdateOTS.sh --tables' to update your tables, then relaunch ots."
1378  << __E__;
1379  ss << "\nTables must be defined at path $USER_DATA/TableInfo/ to exist in ots. "
1380  "Please verify your table definitions, and then restart ots."
1381  << __E__;
1382  __GEN_COUT_ERR__ << "\n" << ss.str();
1383  __SS_THROW__;
1384  }
1385  return nameToTableMap_[tableName];
1386 } // end getTableByName()
1387 
1388 //==============================================================================
1393 time_t ConfigurationManagerRW::getVersionCreationTime(const std::string& tableName,
1394  TableVersion version)
1395 {
1396  { // check process-wide cache first (only immutable persistent versions are cached)
1397  std::lock_guard<std::mutex> lock(versionCreationTimeCacheMutex_);
1398 
1399  auto tableIt = versionCreationTimeCache_.find(tableName);
1400  if(tableIt != versionCreationTimeCache_.end())
1401  {
1402  auto versionIt = tableIt->second.find(version);
1403  if(versionIt != tableIt->second.end())
1404  return versionIt->second;
1405  }
1406  }
1407 
1408  std::string localAccumulatedErrors;
1409  const auto loadStartTime = std::chrono::steady_clock::now();
1410  time_t creationTime = getVersionedTableByName(tableName,
1411  version,
1412  true /* looseColumnMatching */,
1413  &localAccumulatedErrors,
1414  false /* getRawData */,
1415  false /* touchLastAccessTime */)
1416  ->getView(version)
1417  .getCreationTime();
1418 
1419  double loadSec =
1420  std::chrono::duration<double>(std::chrono::steady_clock::now() - loadStartTime)
1421  .count();
1422  if(loadSec > 1.0)
1423  __GEN_COUT_WARN__ << "Slow creation time lookup: table '" << tableName
1424  << "' version v" << version << " load took " << loadSec << " s"
1425  << __E__;
1426 
1427  if(!version.isTemporaryVersion() && !version.isScratchVersion())
1428  {
1429  std::lock_guard<std::mutex> lock(versionCreationTimeCacheMutex_);
1430  versionCreationTimeCache_[tableName][version] = creationTime;
1431  }
1432 
1433  return creationTime;
1434 } // end getVersionCreationTime()
1435 
1436 //==============================================================================
1444 {
1445  const auto preloadStartTime = std::chrono::steady_clock::now();
1446 
1447  // Identify uncached versions grouped by table
1448  std::vector<std::pair<std::string, std::vector<TableVersion>>> groupedWork;
1449  size_t missingCount = 0;
1450  {
1451  std::lock_guard<std::mutex> lock(versionCreationTimeCacheMutex_);
1452 
1453  for(const auto& tableInfoPair : allTableInfo_)
1454  {
1455  auto tableIt = versionCreationTimeCache_.find(tableInfoPair.first);
1456 
1457  std::vector<TableVersion> missingVersions;
1458  for(const auto& version : tableInfoPair.second.versions_)
1459  {
1460  if(version.isTemporaryVersion() || version.isScratchVersion())
1461  continue;
1462  if(tableIt != versionCreationTimeCache_.end() &&
1463  tableIt->second.find(version) != tableIt->second.end())
1464  continue;
1465  missingVersions.push_back(version);
1466  }
1467  if(missingVersions.size())
1468  {
1469  missingCount += missingVersions.size();
1470  groupedWork.emplace_back(tableInfoPair.first, std::move(missingVersions));
1471  }
1472  }
1473  }
1474  if(groupedWork.empty())
1475  return;
1476 
1477  // Round-robin interleave so large tables are spread across all threads
1478  std::vector<std::pair<std::string, TableVersion>> flatWork;
1479  flatWork.reserve(missingCount);
1480  {
1481  size_t maxVersions = 0;
1482  for(const auto& gw : groupedWork)
1483  if(gw.second.size() > maxVersions)
1484  maxVersions = gw.second.size();
1485  for(size_t vi = 0; vi < maxVersions; ++vi)
1486  for(const auto& gw : groupedWork)
1487  if(vi < gw.second.size())
1488  flatWork.emplace_back(gw.first, gw.second[vi]);
1489  }
1490 
1491  __GEN_COUT__ << "preloadVersionCreationTimes() loading " << missingCount
1492  << " version creation times for " << groupedWork.size() << " tables..."
1493  << __E__;
1494 
1495  int numOfThreads = StringMacros::getConcurrencyCount() / 2;
1496  if(numOfThreads > (int)flatWork.size())
1497  numOfThreads = flatWork.size();
1498 
1499  if(numOfThreads < 2)
1500  {
1501  for(const auto& work : flatWork)
1502  {
1503  try
1504  {
1505  getVersionCreationTime(work.first, work.second);
1506  }
1507  catch(...)
1508  {
1509  __GEN_COUT__ << "Failed to get creation time for table '" << work.first
1510  << "' version v" << work.second << ", skipping." << __E__;
1511  }
1512  }
1513  }
1514  else
1515  {
1516  std::atomic<size_t> workIndex(0);
1517  std::vector<std::thread> threads;
1518  auto* iface = theInterface_;
1519 
1520  for(int i = 0; i < numOfThreads; ++i)
1521  threads.emplace_back([iface, &workIndex, &flatWork, this]() {
1522  std::map<std::string, TableBase*> localTables;
1523 
1524  try
1525  {
1526  size_t w;
1527  while((w = workIndex++) < flatWork.size())
1528  {
1529  const auto& tableName = flatWork[w].first;
1530  const auto& version = flatWork[w].second;
1531 
1532  try
1533  {
1534  TableBase*& table = localTables[tableName];
1535  if(!table)
1536  {
1537  std::string localAccumulatedErrors;
1538  table = new TableBase(tableName, &localAccumulatedErrors);
1539  }
1540 
1541  std::string localAccumulatedErrors;
1542  iface->get(table,
1543  tableName,
1544  0 /* groupKey */,
1545  0 /* groupName */,
1546  false /* dontFill */,
1547  version,
1548  true /* resetConfiguration */,
1549  true /* looseColumnMatching */,
1550  false /* rawDataOnly */,
1551  &localAccumulatedErrors,
1552  false /* touchLastAccessTime */);
1553 
1554  time_t creationTime =
1555  table->getView(version).getCreationTime();
1556 
1557  {
1558  std::lock_guard<std::mutex> lock(
1559  versionCreationTimeCacheMutex_);
1560  versionCreationTimeCache_[tableName][version] =
1561  creationTime;
1562  }
1563  }
1564  catch(...)
1565  {
1566  __GEN_COUT__ << "Failed to get creation time for table '"
1567  << tableName << "' version v" << version
1568  << ", skipping." << __E__;
1569  }
1570  }
1571  }
1572  catch(...)
1573  {
1574  __GEN_COUT_ERR__ << "Unexpected error in preload thread." << __E__;
1575  }
1576 
1577  for(auto& pair : localTables)
1578  if(pair.second)
1579  delete pair.second;
1580  });
1581 
1582  for(auto& thread : threads)
1583  thread.join();
1584  }
1585 
1586  __GEN_COUT__ << "preloadVersionCreationTimes() loaded " << missingCount
1587  << " version creation times with " << numOfThreads << " thread(s) in "
1588  << std::chrono::duration<double>(std::chrono::steady_clock::now() -
1589  preloadStartTime)
1590  .count()
1591  << " s" << __E__;
1592 } // end preloadVersionCreationTimes()
1593 
1594 //==============================================================================
1601 time_t ConfigurationManagerRW::getVersionLastAccessTime(const std::string& tableName,
1602  TableVersion version)
1603 {
1604  auto it = nameToTableMap_.find(tableName);
1605  if(it == nameToTableMap_.end() || !it->second->isStored(version))
1606  return 0; // never loaded (or evicted from cache) by this process
1607 
1608  return it->second->getView(version).getLastAccessTime();
1609 } // end getVersionLastAccessTime()
1610 
1611 //==============================================================================
1615  TableVersion temporaryVersion,
1616  bool makeTemporary) //,
1618 {
1619  TableVersion newVersion(temporaryVersion);
1620 
1621  // set author of version
1622  TableBase* table = getTableByName(tableName);
1623  table->getTemporaryView(temporaryVersion)->setAuthor(username_);
1624  // NOTE: author is assigned to permanent versions when saved to DBI
1625 
1626  if(!makeTemporary) // saveNewVersion makes the new version the active version
1627  newVersion = theInterface_->saveNewVersion(table, temporaryVersion);
1628  else // make the temporary version active
1629  table->setActiveView(newVersion);
1630 
1631  // if there is a problem, try to recover
1632  while(!makeTemporary && !newVersion.isScratchVersion() &&
1633  allTableInfo_[tableName].versions_.find(newVersion) !=
1634  allTableInfo_[tableName].versions_.end())
1635  {
1636  __GEN_COUT_ERR__
1637  << "What happenened!?? ERROR::: new persistent version v" << newVersion
1638  << " already exists!? How is it possible? Retrace your steps and "
1639  "tell an admin."
1640  << __E__;
1641 
1642  // create a new temporary version of the target view
1643  temporaryVersion = table->createTemporaryView(newVersion);
1644 
1645  if(newVersion.isTemporaryVersion())
1646  newVersion = temporaryVersion;
1647  else
1648  newVersion = TableVersion::getNextVersion(newVersion);
1649 
1650  __GEN_COUT_WARN__ << "Attempting to recover and use v" << newVersion << __E__;
1651 
1652  if(!makeTemporary) // saveNewVersion makes the new version the active version
1653  newVersion =
1654  theInterface_->saveNewVersion(table, temporaryVersion, newVersion);
1655  else // make the temporary version active
1656  table->setActiveView(newVersion);
1657  }
1658 
1659  if(newVersion.isInvalid())
1660  {
1661  __SS__ << "Something went wrong saving the new version v" << newVersion
1662  << ". What happened?! (duplicates? database error?)" << __E__;
1663  __GEN_COUT_ERR__ << "\n" << ss.str();
1664  __SS_THROW__;
1665  }
1666 
1667  // update allTableInfo_ with the new version
1668  allTableInfo_[tableName].versions_.insert(newVersion);
1669 
1670  // table->getView().print();
1671  return newVersion;
1672 } // end saveNewTable()
1673 
1674 //==============================================================================
1679 void ConfigurationManagerRW::eraseTemporaryVersion(const std::string& tableName,
1680  TableVersion targetVersion)
1681 {
1682  TableBase* table = getTableByName(tableName);
1683 
1684  table->trimTemporary(targetVersion);
1685 
1686  // if allTableInfo_ is not setup, then done
1687  if(allTableInfo_.find(tableName) == allTableInfo_.end())
1688  return;
1689  // else cleanup table info
1690 
1691  if(targetVersion.isInvalid())
1692  {
1693  // erase all temporary versions!
1694  for(auto it = allTableInfo_[tableName].versions_.begin();
1695  it != allTableInfo_[tableName].versions_.end();
1696  /*no increment*/)
1697  {
1698  if(it->isTemporaryVersion())
1699  {
1700  __GEN_COUT__ << "Removing '" << tableName << "' version info: " << *it
1701  << __E__;
1702  allTableInfo_[tableName].versions_.erase(it++);
1703  }
1704  else
1705  ++it;
1706  }
1707  }
1708  else // erase target version only
1709  {
1710  //__GEN_COUT__ << "Removing '" << tableName << "' version info: " << targetVersion << __E__;
1711  auto it = allTableInfo_[tableName].versions_.find(targetVersion);
1712  if(it == allTableInfo_[tableName].versions_.end())
1713  {
1714  __GEN_COUT__ << "Target '" << tableName << "' version v" << targetVersion
1715  << " was not found in info versions..." << __E__;
1716  return;
1717  }
1718  allTableInfo_[tableName].versions_.erase(
1719  allTableInfo_[tableName].versions_.find(targetVersion));
1720  }
1721 } // end eraseTemporaryVersion()
1722 
1723 //==============================================================================
1728 void ConfigurationManagerRW::clearCachedVersions(const std::string& tableName)
1729 {
1730  TableBase* table = getTableByName(tableName);
1731 
1732  table->trimCache(0);
1733 } // end clearCachedVersions()
1734 
1735 //==============================================================================
1741 {
1742  for(auto configInfo : allTableInfo_)
1743  configInfo.second.tablePtr_->trimCache(0);
1744 } // end clearAllCachedVersions()
1745 
1746 //==============================================================================
1749  const std::string& tableName, TableVersion sourceVersion)
1750 {
1751  getTableByName(tableName)->reset();
1752 
1753  // make sure source version is loaded
1754  // need to load with loose column rules!
1755  TableBase* table =
1756  getVersionedTableByName(tableName, TableVersion(sourceVersion), true);
1757 
1758  // copy from source version to a new temporary version
1759  TableVersion newTemporaryVersion =
1760  table->copyView(table->getView(), TableVersion(), username_);
1761 
1762  // update allTableInfo_ with the new version
1763  allTableInfo_[tableName].versions_.insert(newTemporaryVersion);
1764 
1765  return newTemporaryVersion;
1766 } // end copyViewToCurrentColumns()
1767 
1768 //==============================================================================
1773  const std::string& groupName, bool attemptToReloadKeys /* = false */)
1774 {
1775  // //NOTE: seems like this filter is taking the long amount of time
1776  // std::set<std::string /*name*/> fullGroupNames =
1777  // theInterface_->getAllTableGroupNames(groupName); //db filter by group name
1778 
1779  // so instead caching ourselves...
1780  auto it = allGroupInfo_.find(groupName);
1781  if(it == allGroupInfo_.end())
1782  {
1783  __SS__ << "Group name '" << groupName
1784  << "' not found in group info! (creating empty info)" << __E__;
1785  __GEN_COUT_WARN__ << ss.str();
1786  //__SS_THROW__;
1787  return allGroupInfo_[groupName];
1788  }
1789 
1790  if(attemptToReloadKeys) //load keys from Interface group cache
1791  {
1792  __GEN_COUT__ << "Reloading keys from special db group cache if it exists..."
1793  << __E__;
1794 
1795  std::set<TableGroupKey> keys;
1796  { //load keys from special db group cache (this avoids pre-cache filling and avoids long db lookup, unless speed table cache missing for this group)
1797  //attempt to use cache first! (potentially way faster .04 s vs 4 s)
1798  bool cacheFailed = false;
1799  try
1800  {
1801  TableBase localGroupMemberCacheLoader(
1802  true /*special table*/
1803  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),,
1804  TableBase::GROUP_CACHE_PREPEND + groupName);
1805  auto versions = theInterface_->getVersions(&localGroupMemberCacheLoader);
1806  for(const auto& version : versions)
1807  keys.emplace(TableGroupKey(version.version()));
1808  }
1809  catch(...)
1810  {
1811  __GEN_COUT__ << "Ignoring cache loading error. Doing full load of keys..."
1812  << __E__;
1813  cacheFailed = true;
1814  }
1815 
1816  if(cacheFailed && 0) //could consider full load if cache failed
1817  keys = theInterface_->getKeys(groupName);
1818 
1819  if(!cacheFailed) //take keys
1820  {
1821  __GEN_COUT__ << "Key from special db group cache were loaded." << __E__;
1822  it->second.keys_ = keys; //update ConfigManager cache!
1823  }
1824  }
1825  }
1826 
1827  return it->second;
1828 } // end getGroupInfo()
1829 
1830 //==============================================================================
1841  const std::string& groupName,
1842  const std::map<std::string, TableVersion>& groupMemberMap,
1843  const std::map<std::string /*name*/, std::string /*alias*/>& memberTableAliases)
1844 {
1845  if(!groupMemberMap.size() || groupName.empty())
1846  {
1847  __SS__ << "Illegal name/members for requested group of name '" << groupName
1848  << "' and member count = " << groupMemberMap.size() << __E__;
1849  __SS_THROW__;
1850  }
1851 
1852  // //NOTE: seems like this filter is taking the long amount of time
1853  // std::set<std::string /*name*/> fullGroupNames =
1854  // theInterface_->getAllTableGroupNames(groupName); //db filter bygroup name
1855  // const GroupInfo& groupInfo = getGroupInfo(groupName); // Note this also seems to take too long because requires a pre-cache load!
1856  std::set<TableGroupKey> keys;
1857  { //so instead load keys from special db group cache (this avoids pre-cache filling and avoids long db lookup, unless speed table cache missing for this group)
1858  //attempt to use cache first! (potentially way faster .04 s vs 4 s)
1859  bool cacheFailed = false;
1860  try
1861  {
1862  TableBase localGroupMemberCacheLoader(
1863  true /*special table*/
1864  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),,
1865  TableBase::GROUP_CACHE_PREPEND + groupName);
1866  auto versions = theInterface_->getVersions(&localGroupMemberCacheLoader);
1867  for(const auto& version : versions)
1868  keys.emplace(TableGroupKey(version.version()));
1869  }
1870  catch(...)
1871  {
1872  __COUT__ << "Ignoring cache loading error. Doing full load of keys..."
1873  << __E__;
1874  cacheFailed = true;
1875  }
1876 
1877  if(cacheFailed) //since cache failed, do full load
1878  keys = theInterface_->getKeys(groupName);
1879  }
1880 
1881  __COUTTV__(StringMacros::setToString(keys));
1882 
1883  const unsigned int MAX_DEPTH_TO_CHECK = 20;
1884  unsigned int keyMinToCheck = 0;
1885 
1886  if(keys.size())
1887  keyMinToCheck = keys.rbegin()->key();
1888  if(keyMinToCheck > MAX_DEPTH_TO_CHECK)
1889  {
1890  keyMinToCheck -= MAX_DEPTH_TO_CHECK;
1891  __GEN_COUT__ << "Checking groups back to key... " << keyMinToCheck << __E__;
1892  }
1893  else
1894  {
1895  keyMinToCheck = 0;
1896  __GEN_COUT__ << "Checking all groups." << __E__;
1897  }
1898 
1899  __GEN_COUTTV__(StringMacros::mapToString(groupMemberMap));
1900 
1901  // have min key to check, now loop through and check groups
1902 
1903  const int numOfThreads = StringMacros::getConcurrencyCount() / 2;
1904  __GEN_COUT__ << " getConcurrencyCount " << StringMacros::getConcurrencyCount()
1905  << " ==> " << numOfThreads << " threads." << __E__;
1906  if(numOfThreads < 2) // no multi-threading
1907  {
1908  std::map<std::string /*name*/, TableVersion /*version*/> compareToMemberMap;
1909  std::map<std::string /*name*/, std::string /*alias*/> compareToMemberTableAliases;
1910  std::map<std::string /*name*/, std::string /*alias*/>*
1911  compareToMemberTableAliasesPtr = nullptr;
1912  if(memberTableAliases.size())
1913  compareToMemberTableAliasesPtr = &compareToMemberTableAliases;
1914 
1915  bool isDifferent;
1916  for(const auto& key : keys)
1917  {
1918  if(key.key() < keyMinToCheck)
1919  continue; // skip keys that are too old
1920 
1921  loadTableGroup(groupName,
1922  key,
1923  false /*doActivate*/,
1924  &compareToMemberMap /*memberMap*/,
1925  0, /*progressBar*/
1926  0, /*accumulatedWarnings*/
1927  0, /*groupComment*/
1928  0, /*groupAuthor*/
1929  0, /*groupCreateTime*/
1930  true /*doNotLoadMember*/,
1931  0 /*groupTypeString*/,
1932  compareToMemberTableAliasesPtr);
1933 
1934  isDifferent = false;
1935  for(auto& memberPair : groupMemberMap)
1936  {
1937  if(memberTableAliases.find(memberPair.first) != memberTableAliases.end())
1938  {
1939  // handle this table as alias, not version
1940  if(compareToMemberTableAliases.find(memberPair.first) ==
1941  compareToMemberTableAliases.end() || // alias is missing
1942  memberTableAliases.at(memberPair.first) !=
1943  compareToMemberTableAliases.at(memberPair.first))
1944  { // then different
1945  isDifferent = true;
1946  break;
1947  }
1948  // FIXED alias matches, but still need to check version
1949  } // else check if compareTo group is using an alias for table
1950  else if(compareToMemberTableAliases.find(memberPair.first) !=
1951  compareToMemberTableAliases.end())
1952  {
1953  // then different
1954  isDifferent = true;
1955  break;
1956  }
1957 
1958  // alias check complete
1959  // handle table version comparison
1960 
1961  if(compareToMemberMap.find(memberPair.first) ==
1962  compareToMemberMap.end() || // name is missing
1963  memberPair.second !=
1964  compareToMemberMap.at(memberPair.first)) // or version mismatch
1965  {
1966  // then different
1967  isDifferent = true;
1968  break;
1969  }
1970  }
1971  if(isDifferent)
1972  continue;
1973 
1974  // check member size for exact match
1975  if(groupMemberMap.size() != compareToMemberMap.size())
1976  continue; // different size, so not same (groupMemberMap is a subset of
1977  // memberPairs)
1978 
1979  __GEN_COUT__ << "Found exact match with key: " << key << __E__;
1980  // else found an exact match!
1981  return key;
1982  }
1983  __GEN_COUT__ << "No match found - this group is new!" << __E__;
1984  // if here, then no match found
1985  return TableGroupKey(); // return invalid key
1986  }
1987  else //multi-threading
1988  {
1989  int threadsLaunched = 0;
1990  int foundThreadIndex = 0;
1991  std::atomic<bool> foundIdentical = false;
1992  ots::TableGroupKey identicalKey;
1993  std::mutex threadMutex;
1994 
1995  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
1996  for(int i = 0; i < numOfThreads; ++i)
1997  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
1998 
1999  for(const auto& key : keys)
2000  {
2001  if(foundIdentical)
2002  break;
2003  if(key.key() < keyMinToCheck)
2004  continue; // skip keys that are too old
2005 
2006  if(threadsLaunched >= numOfThreads)
2007  {
2008  //find availableThreadIndex
2009  foundThreadIndex = -1;
2010  while(foundThreadIndex == -1)
2011  {
2012  if(foundIdentical)
2013  break;
2014 
2015  for(int i = 0; i < numOfThreads; ++i)
2016  if(*(threadDone[i]))
2017  {
2018  foundThreadIndex = i;
2019  break;
2020  }
2021  if(foundThreadIndex == -1)
2022  {
2023  __GEN_COUTT__ << "Waiting for available thread..." << __E__;
2024  usleep(10000);
2025  }
2026  } //end thread search loop
2027  threadsLaunched = numOfThreads - 1;
2028  }
2029  if(foundIdentical)
2030  break;
2031 
2032  __GEN_COUTT__ << "Starting thread... " << foundThreadIndex << __E__;
2033  *(threadDone[foundThreadIndex]) = false;
2034 
2035  std::thread(
2036  [](ConfigurationManagerRW* cfgMgr,
2037  std::string theGroupName,
2038  ots::TableGroupKey groupKeyToCompare,
2039  const std::map<std::string, TableVersion>& groupMemberMap,
2040  const std::map<std::string /*name*/, std::string /*alias*/>&
2041  memberTableAliases,
2042  std::atomic<bool>* theFoundIdentical,
2043  ots::TableGroupKey* theIdenticalKey,
2044  std::mutex* theThreadMutex,
2045  std::shared_ptr<std::atomic<bool>> theThreadDone) {
2047  theGroupName,
2048  groupKeyToCompare,
2049  groupMemberMap,
2050  memberTableAliases,
2051  theFoundIdentical,
2052  theIdenticalKey,
2053  theThreadMutex,
2054  theThreadDone);
2055  },
2056  this,
2057  groupName,
2058  key,
2059  groupMemberMap,
2060  memberTableAliases,
2061  &foundIdentical,
2062  &identicalKey,
2063  &threadMutex,
2064  threadDone[foundThreadIndex])
2065  .detach();
2066 
2067  ++threadsLaunched;
2068  ++foundThreadIndex;
2069  } //end group key check thread loop
2070 
2071  //check for all threads done
2072  do
2073  {
2074  foundThreadIndex = -1;
2075  for(int i = 0; i < numOfThreads; ++i)
2076  if(!*(threadDone[i]))
2077  {
2078  foundThreadIndex = i;
2079  break;
2080  }
2081  if(foundThreadIndex != -1)
2082  {
2083  __GEN_COUTT__ << "Waiting for thread to finish... " << foundThreadIndex
2084  << __E__;
2085  usleep(10000);
2086  }
2087  } while(foundThreadIndex != -1); //end thread done search loop
2088 
2089  if(foundIdentical)
2090  {
2091  __GEN_COUT__ << "Found exact match with key: " << identicalKey << __E__;
2092  return identicalKey;
2093  }
2094 
2095  // if here, then no match found
2096  return TableGroupKey(); // return invalid key
2097  } //end multi-thread handling
2098 } // end findTableGroup()
2099 
2100 //==============================================================================
2106  TableVersion fillVersion /* = TableVersion()*/)
2107 {
2108  if(fillVersion.isInvalid())
2109  return &groupMetadataTable_;
2110  //else load specified fill version
2111 
2112  //only lock metadata table since it is shared by all group accesses
2113  std::lock_guard<std::mutex> lock(metaDataTableMutex_);
2114 
2115  // clear table
2116  while(groupMetadataTable_.getView().getNumberOfRows())
2117  groupMetadataTable_.getViewP()->deleteRow(0);
2118 
2119  // retrieve metadata from database
2120  try
2121  {
2122  theInterface_->fill(&groupMetadataTable_, fillVersion);
2123  }
2124  catch(const std::runtime_error& e)
2125  {
2126  __GEN_COUT_WARN__ << "Failed to load " << groupMetadataTable_.getTableName()
2127  << "-v" << fillVersion << ". Metadata error: " << e.what()
2128  << __E__;
2129  }
2130  catch(...)
2131  {
2132  __GEN_COUT_WARN__ << "Failed to load " << groupMetadataTable_.getTableName()
2133  << "-v" << fillVersion << ". Ignoring unknown metadata error. "
2134  << __E__;
2135  }
2136 
2137  // check that there is only 1 row
2138  if(groupMetadataTable_.getView().getNumberOfRows() != 1)
2139  {
2140  groupMetadataTable_.print();
2141  __GEN_COUT_ERR__ << "Ignoring that groupMetadataTable_ v" << fillVersion
2142  << " has wrong "
2143  "number of rows!' Must "
2144  "be 1. Going with anonymous defaults."
2145  << __E__;
2146 
2147  // fix metadata table
2148  while(groupMetadataTable_.getViewP()->getNumberOfRows() > 1)
2149  groupMetadataTable_.getViewP()->deleteRow(0);
2150  if(groupMetadataTable_.getViewP()->getNumberOfRows() == 0)
2151  groupMetadataTable_.getViewP()->addRow();
2152  }
2153 
2154  return &groupMetadataTable_;
2155 } // end getMetadataTable()
2156 
2157 //==============================================================================
2165  const std::string& groupName,
2166  std::map<std::string, TableVersion>& groupMembers,
2167  const std::string& groupComment,
2168  std::map<std::string /*table*/, std::string /*alias*/>* groupAliases)
2169 {
2170  // steps:
2171  // determine new group key
2172  // verify group members
2173  // verify groupNameWithKey
2174  // verify store
2175 
2176  if(groupMembers.size() == 0) // do not allow empty groups
2177  {
2178  __SS__ << "Empty group member list. Can not create a group without members!"
2179  << __E__;
2180  __SS_THROW__;
2181  }
2182 
2183  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2184 
2185  // verify group members
2186  // - use all table info
2187  std::map<std::string, TableInfo> allCfgInfo = getAllTableInfo();
2188  for(auto& memberPair : groupMembers)
2189  {
2190  // check member name
2191  if(allCfgInfo.find(memberPair.first) == allCfgInfo.end())
2192  {
2193  __GEN_COUT_ERR__ << "Group member \"" << memberPair.first
2194  << "\" not found in database!";
2195 
2196  if(groupMetadataTable_.getTableName() == memberPair.first)
2197  {
2198  __GEN_COUT_WARN__
2199  << "Looks like this is the groupMetadataTable_ '"
2200  << TableBase::GROUP_METADATA_TABLE_NAME
2201  << ".' Note that this table is added to the member map when groups "
2202  "are saved."
2203  << "It should not be part of member map when calling this function."
2204  << __E__;
2205  __GEN_COUT__ << "Attempting to recover." << __E__;
2206  groupMembers.erase(groupMembers.find(memberPair.first));
2207  }
2208  else
2209  {
2210  __SS__ << ("Group member not found!") << __E__;
2211  __SS_THROW__;
2212  }
2213  }
2214  // check member version
2215  if(allCfgInfo[memberPair.first].versions_.find(memberPair.second) ==
2216  allCfgInfo[memberPair.first].versions_.end())
2217  {
2218  __SS__ << "Group member \"" << memberPair.first << "\" version \""
2219  << memberPair.second << "\" not found in database!";
2220  __SS_THROW__;
2221  }
2222  } // end verify members
2223 
2224  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2225 
2226  // verify group aliases
2227  if(groupAliases)
2228  {
2229  for(auto& aliasPair : *groupAliases)
2230  {
2231  // check for alias table in member names
2232  if(groupMembers.find(aliasPair.first) == groupMembers.end())
2233  {
2234  __GEN_COUT_ERR__ << "Group member \"" << aliasPair.first
2235  << "\" not found in group member map!";
2236 
2237  __SS__ << ("Alias table not found in member list!") << __E__;
2238  __SS_THROW__;
2239  }
2240  }
2241  } // end verify group aliases
2242 
2243  TableGroupKey newKey =
2244  TableGroupKey::getNextKey(theInterface_->findLatestGroupKey(groupName));
2245  __GEN_COUT__ << "New Key for group: " << groupName << " found as " << newKey << __E__;
2246  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2247 
2248  time_t groupCreationTime = time(0);
2249  // capture group type before adding metadata table!
2250  std::string groupType = getTypeNameOfGroup(groupMembers);
2251  std::map<std::string /*name*/, TableVersion /*version*/> groupMembersWithoutMeta =
2252  groupMembers;
2253 
2254  // verify groupNameWithKey and attempt to store
2255  try
2256  {
2257  // save meta data for group; reuse groupMetadataTable_
2258  std::string groupAliasesString = "";
2259  if(groupAliases)
2260  groupAliasesString = StringMacros::mapToString(
2261  *groupAliases, "," /*primary delimeter*/, ":" /*secondary delimeter*/);
2262  __GEN_COUT__ << "Metadata: " << username_ << " " << groupCreationTime << " "
2263  << groupComment << " " << groupAliasesString << " " << groupType
2264  << __E__;
2265 
2266  // to compensate for unusual errors upstream, make sure the metadata table has one
2267  // row
2268  while(groupMetadataTable_.getViewP()->getNumberOfRows() > 1)
2269  groupMetadataTable_.getViewP()->deleteRow(0);
2270  if(groupMetadataTable_.getViewP()->getNumberOfRows() == 0)
2271  groupMetadataTable_.getViewP()->addRow();
2272 
2273  // columns are uid,comment,author,time
2274  groupMetadataTable_.getViewP()->setValue(
2275  groupAliasesString, 0, ConfigurationManager::METADATA_COL_ALIASES);
2276  groupMetadataTable_.getViewP()->setValue(
2277  groupComment, 0, ConfigurationManager::METADATA_COL_COMMENT);
2278  groupMetadataTable_.getViewP()->setValue(
2279  username_, 0, ConfigurationManager::METADATA_COL_AUTHOR);
2280  groupMetadataTable_.getViewP()->setValue(
2281  groupCreationTime, 0, ConfigurationManager::METADATA_COL_TIMESTAMP);
2282 
2283  if(TTEST(2))
2284  {
2285  std::stringstream ss;
2286  groupMetadataTable_.print(ss);
2287  __COUT_MULTI__(2, ss.str());
2288  }
2289 
2290  // save table, and retry on save collision
2291  {
2292  // set version to first available persistent version
2294  theInterface_->findLatestVersion(&groupMetadataTable_));
2295  groupMetadataTable_.getViewP()->setVersion(newVersion);
2296 
2297  uint16_t retries = 0;
2298  while(1)
2299  {
2300  try
2301  {
2302  theInterface_->saveActiveVersion(&groupMetadataTable_);
2303  }
2304  catch(const std::runtime_error& e)
2305  {
2306  __GEN_COUT__ << "Caught runtime_error exception during table save."
2307  << __E__;
2308  if(std::string(e.what()).find("there was a collision") !=
2309  std::string::npos)
2310  {
2311  __GEN_COUT_WARN__
2312  << "There was a collision saving the new table "
2313  << groupMetadataTable_ << "(" << newVersion
2314  << "), trying incremented table version... retries="
2315  << retries << __E__;
2316  if(++retries > 3) //give up
2317  throw;
2318  newVersion = TableVersion::getNextVersion(
2319  newVersion); //increment table version
2320  groupMetadataTable_.getViewP()->setVersion(newVersion);
2321  __GEN_COUT__ << "New version for table: " << groupMetadataTable_
2322  << " found as " << newVersion << __E__;
2323  continue;
2324  }
2325  else
2326  throw;
2327  }
2328 
2329  __GEN_COUT__ << "Created table: " << groupMetadataTable_ << "-v"
2330  << newVersion << __E__;
2331  break;
2332  } //end collission retry loop
2333  }
2334 
2335  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds()
2336  << __E__;
2337 
2338  // force groupMetadataTable_ to be a member for the group
2339  groupMembers[groupMetadataTable_.getTableName()] =
2340  groupMetadataTable_.getViewVersion();
2341 
2342  // save group, and retry on save collision
2343  {
2344  uint16_t retries = 0;
2345  while(1)
2346  {
2347  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds()
2348  << __E__;
2349 
2350  try
2351  {
2352  theInterface_->saveTableGroup(
2353  groupMembers,
2354  TableGroupKey::getFullGroupString(groupName, newKey));
2355  }
2356  catch(const std::runtime_error& e)
2357  {
2358  __GEN_COUT__ << "Caught runtime_error exception during group save."
2359  << __E__;
2360  if(std::string(e.what()).find("there was a collision") !=
2361  std::string::npos)
2362  {
2363  __GEN_COUT_WARN__
2364  << "There was a collision saving the new group " << groupName
2365  << "(" << newKey
2366  << "), trying incremented group key... retries=" << retries
2367  << __E__;
2368  if(++retries > 3) //give up
2369  throw;
2370  newKey = TableGroupKey::getNextKey(newKey); //increment group key
2371  __GEN_COUT__ << "New Key for group: " << groupName << " found as "
2372  << newKey << __E__;
2373  continue;
2374  }
2375  else
2376  throw;
2377  }
2378 
2379  __GEN_COUT__ << "Created table group: " << groupName << "(" << newKey
2380  << ")" << __E__;
2381  break;
2382  } //end collission retry loop
2383  }
2384 
2385  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds()
2386  << __E__;
2387  }
2388  catch(std::runtime_error& e)
2389  {
2390  __GEN_COUT_ERR__ << "Failed to create table group: " << groupName << "(" << newKey
2391  << ")" << __E__;
2392  __GEN_COUT_ERR__ << "\n\n" << e.what() << __E__;
2393  throw;
2394  }
2395  catch(...)
2396  {
2397  __GEN_COUT_ERR__ << "Failed to create table group: " << groupName << ":" << newKey
2398  << __E__;
2399  throw;
2400  }
2401 
2402  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2403 
2404  // store cache of recent groups
2405  allGroupInfo_[groupName].keys_.emplace(newKey);
2406  //update latest group info with this group's info
2407  allGroupInfo_.at(groupName).latestKey_ = newKey;
2408  allGroupInfo_.at(groupName).latestKeyGroupAuthor_ = username_;
2409  allGroupInfo_.at(groupName).latestKeyGroupComment_ = groupComment;
2410  allGroupInfo_.at(groupName).latestKeyGroupCreationTime_ = groupCreationTime;
2411  allGroupInfo_.at(groupName).latestKeyGroupTypeString_ = groupType;
2412  allGroupInfo_.at(groupName).latestKeyMemberMap_ = groupMembersWithoutMeta;
2413 
2414  __GEN_COUT__ << "Saved " << groupName << "(" << newKey << ") of type " << groupType
2415  << __E__;
2416 
2417  __GEN_COUTT__ << "saveNewTableGroup runTimeSeconds()=" << runTimeSeconds() << __E__;
2418 
2419  // at this point succeeded!
2420  return newKey;
2421 } // end saveNewTableGroup()
2422 
2423 //==============================================================================
2428 {
2429  __GEN_COUT_INFO__ << "Creating new backbone from temporary version "
2430  << temporaryVersion << __E__;
2431 
2432  // find common available temporary version among backbone members
2433  TableVersion newVersion(TableVersion::DEFAULT);
2434  TableVersion retNewVersion;
2435  auto backboneMemberNames = ConfigurationManager::getBackboneMemberNames();
2436  for(auto& name : backboneMemberNames)
2437  {
2438  retNewVersion = ConfigurationManager::getTableByName(name)->getNextVersion();
2439  __GEN_COUT__ << "New version for backbone member (" << name
2440  << "): " << retNewVersion << __E__;
2441  if(retNewVersion > newVersion)
2442  newVersion = retNewVersion;
2443  }
2444 
2445  __GEN_COUT__ << "Common new backbone version found as " << newVersion << __E__;
2446 
2447  // create new views from source temporary version
2448  for(auto& name : backboneMemberNames)
2449  {
2450  // saveNewVersion makes the new version the active version
2451  retNewVersion = getConfigurationInterface()->saveNewVersion(
2452  getTableByName(name), temporaryVersion, newVersion);
2453  if(retNewVersion != newVersion)
2454  {
2455  __SS__ << "Failure! New view requested was " << newVersion
2456  << ". Mismatched new view created: " << retNewVersion << __E__;
2457  __GEN_COUT_ERR__ << ss.str();
2458  __SS_THROW__;
2459  }
2460  }
2461 
2462  return newVersion;
2463 } // end saveNewBackbone()
2464 
2465 //==============================================================================
2471  const std::string& tableName,
2472  TableVersion originalVersion,
2473  bool makeTemporary,
2474  TableBase* table,
2475  TableVersion temporaryModifiedVersion,
2476  bool ignoreDuplicates /*= false*/,
2477  bool lookForEquivalent /*= false*/,
2478  bool* foundEquivalent /*= nullptr*/)
2479 {
2480  bool needToEraseTemporarySource =
2481  (originalVersion.isTemporaryVersion() && !makeTemporary);
2482 
2483  if(foundEquivalent)
2484  *foundEquivalent = false; // initialize
2485 
2486  // check for duplicate tables already in cache, plus highest version numbers not in cache
2487  if(!ignoreDuplicates)
2488  {
2489  __GEN_COUT__ << "Checking for duplicate '" << tableName << "' tables..." << __E__;
2490 
2491  TableVersion duplicateVersion;
2492 
2493  {
2494  //"DEEP" checking
2495  // load into cache 'recent' versions for this table
2496  // 'recent' := those already in cache, plus highest version numbers not in cache
2497  const std::map<std::string, TableInfo>& allTableInfo =
2498  getAllTableInfo(); // do not refresh
2499 
2500  auto versionReverseIterator =
2501  allTableInfo.at(tableName).versions_.rbegin(); // get reverse iterator
2502  __GEN_COUT__ << "Filling up '" << tableName << "' cache from "
2503  << table->getNumberOfStoredViews() << " to max count of "
2504  << table->MAX_VIEWS_IN_CACHE << __E__;
2505  for(; table->getNumberOfStoredViews() < table->MAX_VIEWS_IN_CACHE &&
2506  versionReverseIterator != allTableInfo.at(tableName).versions_.rend();
2507  ++versionReverseIterator)
2508  {
2509  __GEN_COUTT__ << "'" << tableName << "' versions in reverse order "
2510  << *versionReverseIterator << __E__;
2511  try
2512  {
2513  getVersionedTableByName(tableName,
2514  *versionReverseIterator); // load to cache
2515  }
2516  catch(const std::runtime_error& e)
2517  {
2518  // ignore error
2519  __COUTT__ << "'" << tableName
2520  << "' version failed to load: " << *versionReverseIterator
2521  << __E__;
2522  }
2523  }
2524  }
2525 
2526  __GEN_COUT__ << "Checking '" << tableName << "' for duplicate..." << __E__;
2527 
2528  duplicateVersion = table->checkForDuplicate(
2529  temporaryModifiedVersion,
2530  (!originalVersion.isTemporaryVersion() && !makeTemporary)
2531  ? TableVersion()
2532  : // if from persistent to persistent, then include original version in search
2533  originalVersion);
2534 
2535  if(lookForEquivalent && !duplicateVersion.isInvalid())
2536  {
2537  // found an equivalent!
2538  __GEN_COUT__ << "Equivalent '" << tableName << "' table found in version v"
2539  << duplicateVersion << __E__;
2540 
2541  // if duplicate version was temporary, do not use
2542  if(duplicateVersion.isTemporaryVersion() && !makeTemporary)
2543  {
2544  __GEN_COUT__ << "Need persistent. Duplicate '" << tableName
2545  << "' version was temporary. "
2546  "Abandoning duplicate."
2547  << __E__;
2548  duplicateVersion = TableVersion(); // set invalid
2549  }
2550  else
2551  {
2552  // erase and return equivalent version
2553 
2554  // erase modified equivalent version
2555  eraseTemporaryVersion(tableName, temporaryModifiedVersion);
2556 
2557  // erase original if needed
2558  if(needToEraseTemporarySource)
2559  eraseTemporaryVersion(tableName, originalVersion);
2560 
2561  if(foundEquivalent)
2562  *foundEquivalent = true;
2563 
2564  __GEN_COUT__ << "\t\t Equivalent '" << tableName
2565  << "' assigned version: " << duplicateVersion << __E__;
2566 
2567  return duplicateVersion;
2568  }
2569  }
2570 
2571  if(!duplicateVersion.isInvalid())
2572  {
2573  __SS__ << "This version of table '" << tableName
2574  << "' is identical to another version currently cached v"
2575  << duplicateVersion << ". No reason to save a duplicate." << __E__;
2576  __GEN_COUT_ERR__ << "\n" << ss.str();
2577 
2578  // delete temporaryModifiedVersion
2579  table->eraseView(temporaryModifiedVersion);
2580  __SS_THROW__;
2581  }
2582 
2583  __GEN_COUT__ << "Check for duplicate '" << tableName << "' tables complete."
2584  << __E__;
2585  }
2586 
2587  if(makeTemporary)
2588  __GEN_COUT__ << "\t\t**************************** Save as temporary '"
2589  << tableName << "' table version" << __E__;
2590  else
2591  __GEN_COUT__ << "\t\t**************************** Save as new '" << tableName
2592  << "' table version" << __E__;
2593 
2594  TableVersion newAssignedVersion =
2595  saveNewTable(tableName, temporaryModifiedVersion, makeTemporary);
2596 
2597  __GEN_COUTTV__(table->getView().getComment());
2598 
2599  if(needToEraseTemporarySource)
2600  eraseTemporaryVersion(tableName, originalVersion);
2601 
2602  __GEN_COUT__ << "\t\t '" << tableName
2603  << "' new assigned version: " << newAssignedVersion << __E__;
2604  return newAssignedVersion;
2605 } // end saveModifiedVersion()
2606 
2607 //==============================================================================
2621  const std::string& tableName,
2622  const std::map<std::string, std::map<std::string, std::string>>& cellUpdates,
2623  const std::string& author,
2624  TableVersion sourceVersion /* = TableVersion() */,
2625  const std::string& versionAlias /* = "" */,
2626  const std::string& sourceAlias /* = "" */,
2627  const std::string& comment /* = "" */)
2628 {
2629  TableBase* table = getTableByName(tableName);
2630 
2631  // resolve source alias to a version number
2632  if(!sourceAlias.empty())
2633  {
2634  auto allAliases = getVersionAliases();
2635  auto tableIt = allAliases.find(tableName);
2636  if(tableIt == allAliases.end())
2637  {
2638  __SS__ << "No version aliases found for table '" << tableName << "'."
2639  << __E__;
2640  __SS_THROW__;
2641  }
2642  auto aliasIt = tableIt->second.find(sourceAlias);
2643  if(aliasIt == tableIt->second.end())
2644  {
2645  __SS__ << "Version alias '" << sourceAlias << "' not found for table '"
2646  << tableName << "'. Available aliases: ";
2647  for(const auto& a : tableIt->second)
2648  ss << "'" << a.first << "' (v" << a.second << "), ";
2649  ss << __E__;
2650  __SS_THROW__;
2651  }
2652  sourceVersion = aliasIt->second;
2653  __GEN_COUT__ << "Resolved source alias '" << sourceAlias << "' to version "
2654  << sourceVersion << " for table '" << tableName << "'" << __E__;
2655  }
2656 
2657  // resolve source version: if not specified, use the active version
2658  if(sourceVersion.isInvalid() || sourceVersion.isMockupVersion())
2659  {
2660  if(!table->isActive())
2661  {
2662  __SS__ << "No source version specified and table '" << tableName
2663  << "' has no active version. Please specify a version or "
2664  "ensure active groups are initialized."
2665  << __E__;
2666  __SS_THROW__;
2667  }
2668  sourceVersion = table->getView().getVersion();
2669  __GEN_COUT__ << "Using active version " << sourceVersion << " for table '"
2670  << tableName << "'" << __E__;
2671  }
2672  else
2673  getVersionedTableByName(tableName, sourceVersion);
2674 
2675  __GEN_COUT__ << "Updating cells in table '" << tableName << "' from source version "
2676  << sourceVersion << __E__;
2677 
2678  TableVersion temporaryVersion = table->createTemporaryView(sourceVersion);
2679 
2680  __GEN_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2681 
2682  TableView* cfgView = table->getTemporaryView(temporaryVersion);
2683 
2684  try
2685  {
2686  unsigned int uidCol = cfgView->getColUID();
2687  int authorCol = cfgView->findColByType(TableViewColumnInfo::TYPE_AUTHOR);
2688  int timestampCol = cfgView->findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
2689  unsigned int cellsModified = 0;
2690 
2691  for(const auto& rowUpdate : cellUpdates)
2692  {
2693  const std::string& uid = rowUpdate.first;
2694  unsigned int row = cfgView->findRow(uidCol, uid);
2695 
2696  __GEN_COUT__ << "Updating UID '" << uid << "' at row " << row << __E__;
2697 
2698  bool rowModified = false;
2699  for(const auto& colVal : rowUpdate.second)
2700  {
2701  unsigned int col = cfgView->findCol(colVal.first);
2702  __GEN_COUT__ << "Setting [" << uid << "][" << colVal.first << "] = '"
2703  << colVal.second << "'" << __E__;
2704  cfgView->setValueAsString(colVal.second, row, col);
2705  ++cellsModified;
2706  rowModified = true;
2707  }
2708 
2709  if(rowModified && !author.empty())
2710  {
2711  if(authorCol >= 0)
2712  cfgView->setValue(author, row, authorCol);
2713  if(timestampCol >= 0)
2714  cfgView->setValue(time(0), row, timestampCol);
2715  }
2716  }
2717 
2718  __GEN_COUT__ << cellsModified << " cell(s) modified." << __E__;
2719 
2720  std::stringstream commentSS;
2721  commentSS << cellsModified << " cell(s) updated via updateTableCells().";
2722  if(!comment.empty())
2723  commentSS << " Notes: " << comment;
2724  cfgView->setComment(commentSS.str());
2725  }
2726  catch(...)
2727  {
2728  __GEN_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
2729  table->eraseView(temporaryVersion);
2730  throw;
2731  }
2732 
2733  bool foundEquivalent;
2734  TableVersion newVersion = saveModifiedVersion(tableName,
2735  sourceVersion,
2736  false /* makeTemporary */,
2737  table,
2738  temporaryVersion,
2739  false /* ignoreDuplicates */,
2740  true /* lookForEquivalent */,
2741  &foundEquivalent);
2742 
2743  if(foundEquivalent)
2744  __GEN_COUT_WARN__ << "Found equivalent version as " << tableName << "-v"
2745  << newVersion << ". No new version created." << __E__;
2746  else
2747  __GEN_COUT_INFO__ << tableName << "-v" << newVersion
2748  << " created with updated cells." << __E__;
2749 
2750  // optionally set version alias
2751  if(!versionAlias.empty())
2752  {
2753  __GEN_COUT__ << "Setting version alias '" << versionAlias << "' for " << tableName
2754  << "-v" << newVersion << __E__;
2755 
2756  GroupEditStruct backboneGroupEdit(ConfigurationManager::GroupType::BACKBONE_TYPE,
2757  this);
2758 
2759  TableView* aliasTableView =
2760  backboneGroupEdit
2761  .getTableEditStruct(ConfigurationManager::VERSION_ALIASES_TABLE_NAME,
2762  true /*markModified*/)
2763  .tableView_;
2764 
2765  unsigned int colTableName = aliasTableView->findCol("TableName");
2766  unsigned int colVersionAlias = aliasTableView->findCol("VersionAlias");
2767  unsigned int colVersion = aliasTableView->findCol("Version");
2768 
2769  // search for existing alias to update, or add new row
2770  bool aliasFound = false;
2771  for(unsigned int row = 0; row < aliasTableView->getNumberOfRows(); ++row)
2772  {
2773  if(aliasTableView->getDataView()[row][colTableName] == tableName &&
2774  aliasTableView->getDataView()[row][colVersionAlias] == versionAlias)
2775  {
2776  __GEN_COUT__ << "Updating existing alias at row " << row << __E__;
2777  aliasTableView->setValueAsString(newVersion.toString(), row, colVersion);
2778  aliasFound = true;
2779  break;
2780  }
2781  }
2782 
2783  if(!aliasFound)
2784  {
2785  __GEN_COUT__ << "Adding new alias row." << __E__;
2786  unsigned int newRow = aliasTableView->addRow(
2787  author, true /*incrementUniqueData*/, "versionAlias");
2788  aliasTableView->setValueAsString(tableName, newRow, colTableName);
2789  aliasTableView->setValueAsString(versionAlias, newRow, colVersionAlias);
2790  aliasTableView->setValueAsString(newVersion.toString(), newRow, colVersion);
2791  }
2792 
2793  TableGroupKey newBackboneKey;
2794  backboneGroupEdit.saveChanges(backboneGroupEdit.originalGroupName_,
2795  newBackboneKey,
2796  nullptr /* foundEquivalentGroupKey */,
2797  true /* activateNewGroup */);
2798 
2799  __GEN_COUT_INFO__ << "Version alias '" << versionAlias << "' set to " << tableName
2800  << "-v" << newVersion << " (backbone key " << newBackboneKey
2801  << ")." << __E__;
2802  }
2803 
2804  return newVersion;
2805 } // end updateTableCells()
2806 
2807 //==============================================================================
2808 GroupEditStruct::GroupEditStruct(const ConfigurationManager::GroupType& groupType,
2809  ConfigurationManagerRW* cfgMgr)
2810  : groupType_(groupType)
2811  , originalGroupName_(cfgMgr->getActiveGroupName(groupType))
2812  , originalGroupKey_(cfgMgr->getActiveGroupKey(groupType))
2813  , cfgMgr_(cfgMgr)
2814  , mfSubject_(cfgMgr->getUsername())
2815 {
2816  if(originalGroupName_ == "" || originalGroupKey_.isInvalid())
2817  {
2818  __SS__ << "Error! No active group found for type '"
2820  << ".' There must be an active group to edit the group." << __E__ << __E__
2821  << StringMacros::stackTrace() << __E__;
2822  __SS_THROW__;
2823  }
2824 
2825  __GEN_COUT__ << "Extracting Group-Edit Struct for type "
2826  << ConfigurationManager::convertGroupTypeToName(groupType) << __E__;
2827 
2828  std::map<std::string, TableVersion> activeTables = cfgMgr->getActiveVersions();
2829 
2830  const std::set<std::string>& memberNames =
2831  groupType == ConfigurationManager::GroupType::CONTEXT_TYPE
2832  ? cfgMgr->getActiveContextMemberNames()
2833  : (groupType == ConfigurationManager::GroupType::BACKBONE_TYPE
2834  ? ConfigurationManager::getBackboneMemberNames()
2835  : (groupType == ConfigurationManager::GroupType::ITERATE_TYPE
2836  ? ConfigurationManager::getIterateMemberNames()
2837  : cfgMgr->getConfigurationMemberNames()));
2838 
2839  for(auto& memberName : memberNames)
2840  try
2841  {
2842  groupMembers_.emplace(
2843  std::make_pair(memberName, activeTables.at(memberName)));
2844  groupTables_.emplace(std::make_pair(
2845  memberName,
2846  TableEditStruct(memberName, cfgMgr))); // Table ready for editing!
2847  }
2848  catch(...)
2849  {
2850  __GEN_COUTV__(StringMacros::mapToString(activeTables));
2851  __SS__ << "Error! Could not find group member table '" << memberName
2852  << "' for group type '"
2854  << ".' All group members must be present to create the group editing "
2855  "structure."
2856  << __E__ << __E__ << StringMacros::stackTrace() << __E__;
2857  __SS_THROW__;
2858  }
2859 
2860 } // end GroupEditStruct constructor()
2861 
2862 //==============================================================================
2863 GroupEditStruct::~GroupEditStruct()
2864 {
2865  __GEN_COUT__ << "GroupEditStruct from editing '" << originalGroupName_ << "("
2866  << originalGroupKey_ << ")' Destructing..." << __E__;
2867  dropChanges();
2868  __GEN_COUT__ << "GroupEditStruct from editing '" << originalGroupName_ << "("
2869  << originalGroupKey_ << ")' Desctructed." << __E__;
2870 } // end GroupEditStruct destructor()
2871 
2872 //==============================================================================
2875  bool markModified /*= false*/)
2876 {
2877  auto it = groupTables_.find(tableName);
2878  if(it == groupTables_.end())
2879  {
2880  if(groupType_ == ConfigurationManager::GroupType::CONFIGURATION_TYPE &&
2881  markModified)
2882  {
2883  __GEN_COUT__ << "Table '" << tableName
2884  << "' not found in configuration table members from editing '"
2885  << originalGroupName_ << "(" << originalGroupKey_ << ")..."
2886  << " Attempting to add it!" << __E__;
2887 
2888  // emplace returns pair<object,bool wasAdded>
2889  auto newIt = groupTables_.emplace(std::make_pair(
2890  tableName,
2891  TableEditStruct(tableName, cfgMgr_))); // Table ready for editing!
2892  if(newIt.second)
2893  {
2894  newIt.first->second.modified_ =
2895  markModified; // could indicate 'dirty' immediately in user code, which will cause a save of table
2896  groupMembers_.emplace(
2897  std::make_pair(tableName, newIt.first->second.temporaryVersion_));
2898  return newIt.first->second;
2899  }
2900  __GEN_COUT_ERR__ << "Failed to emplace new table..." << __E__;
2901  }
2902 
2903  __SS__ << "Table '" << tableName << "' not found in table members from editing '"
2904  << originalGroupName_ << "(" << originalGroupKey_ << ")!'" << __E__;
2905  __SS_THROW__;
2906  }
2907  it->second.modified_ =
2908  markModified; // could indicate 'dirty' immediately in user code, which will cause a save of table
2909  return it->second;
2910 } // end getTableEditStruct()
2911 
2912 //==============================================================================
2913 void GroupEditStruct::dropChanges()
2914 {
2915  __GEN_COUT__ << "Dropping unsaved changes from editing '" << originalGroupName_ << "("
2916  << originalGroupKey_ << ")'..." << __E__;
2917 
2918  ConfigurationManagerRW* cfgMgr = cfgMgr_;
2919 
2920  // drop all temporary versions
2921  for(auto& groupTable : groupTables_)
2922  if(groupTable.second
2923  .createdTemporaryVersion_) // if temporary version created here
2924  {
2925  // erase with proper version management
2926  cfgMgr->eraseTemporaryVersion(groupTable.second.tableName_,
2927  groupTable.second.temporaryVersion_);
2928  groupTable.second.createdTemporaryVersion_ = false;
2929  groupTable.second.modified_ = false;
2930  }
2931 
2932  __GEN_COUT__ << "Unsaved changes dropped from editing '" << originalGroupName_ << "("
2933  << originalGroupKey_ << ").'" << __E__;
2934 } // end GroupEditStruct::dropChanges()
2935 
2936 //==============================================================================
2937 void GroupEditStruct::saveChanges(const std::string& groupNameToSave,
2938  TableGroupKey& newGroupKey,
2939  bool* foundEquivalentGroupKey /*= nullptr*/,
2940  bool activateNewGroup /*= false*/,
2941  bool updateGroupAliases /*= false*/,
2942  bool updateTableAliases /*= false*/,
2943  TableGroupKey* newBackboneKey /*= nullptr*/,
2944  bool* foundEquivalentBackboneKey /*= nullptr*/,
2945  std::string* accumulatedWarnings /*= nullptr*/)
2946 {
2947  __GEN_COUT__ << "Saving changes..." << __E__;
2948 
2949  newGroupKey = TableGroupKey(); // invalidate reference parameter
2950  if(newBackboneKey)
2951  *newBackboneKey = TableGroupKey(); // invalidate reference parameter
2952  if(foundEquivalentBackboneKey)
2953  *foundEquivalentBackboneKey = false; // clear to start
2954  ConfigurationManagerRW* cfgMgr = cfgMgr_;
2955 
2956  // save all temporary modified versions
2957  bool anyTableNew = false;
2958  for(auto& groupTable : groupTables_)
2959  {
2960  if(!groupTable.second.modified_)
2961  continue; // skip if not modified
2962 
2963  __GEN_COUT__ << "Original version is " << groupTable.second.tableName_ << "-v"
2964  << groupTable.second.originalVersion_ << __E__;
2965 
2966  groupMembers_.at(groupTable.first) = cfgMgr->saveModifiedVersion(
2967  groupTable.second.tableName_,
2968  groupTable.second.originalVersion_,
2969  true /*make temporary*/,
2970  groupTable.second.table_,
2971  groupTable.second.temporaryVersion_,
2972  true /*ignoreDuplicates*/); // make temporary version to save persistent version properly
2973 
2974  __GEN_COUT__ << "Temporary target version is " << groupTable.second.tableName_
2975  << "-v" << groupMembers_.at(groupTable.first) << "-v"
2976  << groupTable.second.temporaryVersion_ << __E__;
2977 
2978  groupMembers_.at(groupTable.first) = cfgMgr->saveModifiedVersion(
2979  groupTable.second.tableName_,
2980  groupTable.second.originalVersion_,
2981  false /*make temporary*/,
2982  groupTable.second.table_,
2983  groupTable.second.temporaryVersion_,
2984  false /*ignoreDuplicates*/,
2985  true /*lookForEquivalent*/); // save persistent version properly
2986 
2987  if(groupTable.second.originalVersion_ != groupMembers_.at(groupTable.first))
2988  {
2989  anyTableNew = true;
2990  __GEN_COUT__ << "Final NEW target version is " << groupTable.second.tableName_
2991  << "-v" << groupMembers_.at(groupTable.first) << __E__;
2992  }
2993  else
2994  __GEN_COUT__ << "Final target version is " << groupTable.second.tableName_
2995  << "-v" << groupMembers_.at(groupTable.first) << __E__;
2996 
2997  groupTable.second.modified_ = false; // clear modified flag
2998  groupTable.second.createdTemporaryVersion_ = false; // modified version is gone
2999  } // loop through table edit structs
3000 
3001  for(auto& table : groupMembers_)
3002  {
3003  __GEN_COUT__ << table.first << " v" << table.second << __E__;
3004  }
3005 
3006  if(!anyTableNew) //then could be duplicate group
3007  {
3008  __GEN_COUT__ << "Checking for duplicate groups..." << __E__;
3009  newGroupKey = cfgMgr->findTableGroup(groupNameToSave, groupMembers_);
3010  }
3011  else
3012  __GEN_COUT__ << "New table found, so no need to check duplicate groups." << __E__;
3013 
3014  if(!newGroupKey.isInvalid())
3015  {
3016  __GEN_COUT__ << "Found equivalent group key (" << newGroupKey << ") for "
3017  << groupNameToSave << "." << __E__;
3018  if(foundEquivalentGroupKey)
3019  *foundEquivalentGroupKey = true;
3020  }
3021  else
3022  {
3023  newGroupKey = cfgMgr->saveNewTableGroup(groupNameToSave, groupMembers_);
3024  __GEN_COUT__ << "Saved new Context group key (" << newGroupKey << ") for "
3025  << groupNameToSave << "." << __E__;
3026  }
3027 
3028  bool groupAliasChange = false;
3029  bool tableAliasChange = false;
3030 
3031  if(groupType_ !=
3032  ConfigurationManager::GroupType::
3033  BACKBONE_TYPE) //if not backbone group save, consider changing aliases (not if it is backbone group, tables not necessarily active yet, which causes error in GroupEditStruct backboneGroupEdit)
3034  {
3035  GroupEditStruct backboneGroupEdit(ConfigurationManager::GroupType::BACKBONE_TYPE,
3036  cfgMgr);
3037 
3038  if(groupType_ != ConfigurationManager::GroupType::BACKBONE_TYPE &&
3039  updateGroupAliases)
3040  {
3041  // check group aliases ... a la
3042  // ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML
3043 
3044  TableEditStruct& groupAliasTable = backboneGroupEdit.getTableEditStruct(
3045  ConfigurationManager::GROUP_ALIASES_TABLE_NAME, true /*markModified*/);
3046  TableView* tableView = groupAliasTable.tableView_;
3047 
3048  // unsigned int col;
3049  unsigned int row = 0;
3050 
3051  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
3052  cfgMgr->getNode(ConfigurationManager::GROUP_ALIASES_TABLE_NAME)
3053  .getChildren();
3054  std::string groupName, groupKey;
3055  for(auto& aliasNodePair : aliasNodePairs)
3056  {
3057  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
3058  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
3059 
3060  __GEN_COUT__ << "Group Alias: " << aliasNodePair.first << " => "
3061  << groupName << "(" << groupKey << "); row=" << row << __E__;
3062 
3063  if(groupName == originalGroupName_ &&
3064  TableGroupKey(groupKey) == originalGroupKey_)
3065  {
3066  __GEN_COUT__ << "Found alias! Changing group key from ("
3067  << originalGroupKey_ << ") to (" << newGroupKey << ")"
3068  << __E__;
3069 
3070  groupAliasChange = true;
3071 
3072  tableView->setValueAsString(
3073  newGroupKey.toString(), row, tableView->findCol("GroupKey"));
3074  }
3075 
3076  ++row;
3077  }
3078 
3079  if(groupAliasChange)
3080  {
3081  std::stringstream ss;
3082  tableView->print(ss);
3083  __GEN_COUT__ << ss.str();
3084  }
3085  } // end updateGroupAliases handling
3086 
3087  if(groupType_ != ConfigurationManager::GroupType::BACKBONE_TYPE &&
3088  updateTableAliases)
3089  {
3090  // update all table version aliases
3091  TableView* tableView =
3092  backboneGroupEdit
3093  .getTableEditStruct(ConfigurationManager::VERSION_ALIASES_TABLE_NAME,
3094  true /*markModified*/)
3095  .tableView_;
3096 
3097  for(auto& groupTable : groupTables_)
3098  {
3099  if(groupTable.second.originalVersion_ ==
3100  groupMembers_.at(groupTable.second.tableName_))
3101  continue; // skip if no change
3102 
3103  __GEN_COUT__ << "Checking alias... original version is "
3104  << groupTable.second.tableName_ << "-v"
3105  << groupTable.second.originalVersion_
3106  << " and new version is v"
3107  << groupMembers_.at(groupTable.second.tableName_) << __E__;
3108 
3109  // unsigned int col;
3110  unsigned int row = 0;
3111 
3112  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
3113  cfgMgr->getNode(ConfigurationManager::VERSION_ALIASES_TABLE_NAME)
3114  .getChildren();
3115  std::string tableName, tableVersion;
3116  for(auto& aliasNodePair : aliasNodePairs)
3117  {
3118  tableName =
3119  aliasNodePair.second.getNode("TableName").getValueAsString();
3120  tableVersion =
3121  aliasNodePair.second.getNode("Version").getValueAsString();
3122 
3123  __GEN_COUT__ << "Table Alias: " << aliasNodePair.first << " => "
3124  << tableName << "-v" << tableVersion << "" << __E__;
3125 
3126  if(tableName == groupTable.second.tableName_ &&
3127  TableVersion(tableVersion) == groupTable.second.originalVersion_)
3128  {
3129  __GEN_COUT__ << "Found alias! Changing icon table version alias."
3130  << __E__;
3131 
3132  tableAliasChange = true;
3133 
3134  tableView->setValueAsString(
3135  groupMembers_.at(groupTable.second.tableName_).toString(),
3136  row,
3137  tableView->findCol("Version"));
3138  }
3139 
3140  ++row;
3141  }
3142  }
3143 
3144  if(tableAliasChange)
3145  {
3146  std::stringstream ss;
3147  tableView->print(ss);
3148  __GEN_COUT__ << ss.str();
3149  }
3150  } // end updateTableAliases handling
3151 
3152  TableGroupKey localNewBackboneKey;
3153  // if backbone modified, save group and activate it
3154  if(groupAliasChange || tableAliasChange)
3155  {
3156  for(auto& table : backboneGroupEdit.groupMembers_)
3157  {
3158  __GEN_COUT__ << table.first << " v" << table.second << __E__;
3159  }
3160  backboneGroupEdit.saveChanges(
3161  backboneGroupEdit.originalGroupName_,
3162  localNewBackboneKey,
3163  foundEquivalentBackboneKey ? foundEquivalentBackboneKey : nullptr);
3164 
3165  if(newBackboneKey)
3166  *newBackboneKey = localNewBackboneKey;
3167  }
3168 
3169  // acquire all active groups and ignore errors, so that activateTableGroup does not
3170  // erase other active groups
3171  {
3172  __GEN_COUT__
3173  << "Restoring active table groups, before activating new groups..."
3174  << __E__;
3175 
3176  std::string localAccumulatedWarnings;
3177  cfgMgr->restoreActiveTableGroups(
3178  false /*throwErrors*/,
3179  "" /*pathToActiveGroupsFile*/,
3180  ConfigurationManager::LoadGroupType::
3181  ALL_TYPES /*onlyLoadIfBackboneOrContext*/,
3182  &localAccumulatedWarnings);
3183  }
3184 
3185  // activate new groups
3186  if(!localNewBackboneKey.isInvalid())
3187  cfgMgr->activateTableGroup(
3188  backboneGroupEdit.originalGroupName_,
3189  localNewBackboneKey,
3190  accumulatedWarnings ? accumulatedWarnings : nullptr);
3191 
3192  } //end non-backbone save type handling
3193  else //is backbone save type
3194  {
3195  // acquire all active groups and ignore errors, so that activateTableGroup does not
3196  // erase other active groups
3197  {
3198  __GEN_COUT__
3199  << "Restoring active table groups, before activating new groups..."
3200  << __E__;
3201 
3202  std::string localAccumulatedWarnings;
3203  cfgMgr->restoreActiveTableGroups(
3204  false /*throwErrors*/,
3205  "" /*pathToActiveGroupsFile*/,
3206  ConfigurationManager::LoadGroupType::
3207  ALL_TYPES /*onlyLoadIfBackboneOrContext*/,
3208  &localAccumulatedWarnings);
3209  }
3210  } //end backbone save type handling
3211 
3212  if(activateNewGroup)
3213  cfgMgr->activateTableGroup(groupNameToSave,
3214  newGroupKey,
3215  accumulatedWarnings ? accumulatedWarnings : nullptr);
3216 
3217  __GEN_COUT__ << "Changes saved." << __E__;
3218 } // end GroupEditStruct::saveChanges()
3219 
3220 //==============================================================================
3223 {
3224  if(1)
3225  return; //if 0 to debug
3226  __GEN_COUTV__(runTimeSeconds());
3227 
3228  std::string accumulatedWarningsStr;
3229  std::string* accumulatedWarnings = &accumulatedWarningsStr;
3230 
3231  // get Group Info too!
3232  try
3233  {
3234  //test lookup of which groups a table is in
3235  {
3236  std::string documentNameToLoad = "XDAQApplicationTable";
3237  TableVersion documentVersionToLoad(
3238  (int)134); //1 is easy, 134 is hard on daq13 mongodb
3239 
3240  std::set<std::string> groupsContainingTable =
3241  theInterface_->findGroupsWithTable(documentNameToLoad,
3242  documentVersionToLoad);
3243  __GEN_COUT__ << "Groups containing " << documentNameToLoad << "-v"
3244  << documentVersionToLoad
3245  << " count: " << groupsContainingTable.size() << __E__;
3246  for(const auto& group : groupsContainingTable)
3247  {
3248  __GEN_COUT__ << "\t" << group << __E__;
3249  }
3250  }
3251 
3252  std::string debugGroupName = "Mu2eHWEmulatorContext";
3253 
3254  //final solution demo of getting latest group key:
3255  {
3256  TableGroupKey latestGroupKey =
3257  theInterface_->findLatestGroupKey(debugGroupName);
3258  __GEN_COUTV__(latestGroupKey);
3259 
3260  __GEN_COUTV__(runTimeSeconds());
3261  }
3262 
3263  //steps to do time comparison for getting last group key and table key:
3264 
3265  // build allGroupInfo_ for the ConfigurationManagerRW
3266 
3267  std::set<std::string /*name*/> tableGroups =
3268  theInterface_->getAllTableGroupNames();
3269  __GEN_COUT__ << "Number of Groups: " << tableGroups.size() << __E__;
3270 
3271  __GEN_COUTV__(runTimeSeconds());
3272  // return;
3273 
3274  TableGroupKey key;
3275  std::string name;
3276  for(const auto& fullName : tableGroups)
3277  {
3278  TableGroupKey::getGroupNameAndKey(fullName, name, key);
3279  allGroupInfo_[name].keys_.emplace(key);
3280 
3281  if(name == debugGroupName)
3282  {
3283  __GEN_COUTV__(key);
3284  }
3285  }
3286  __GEN_COUTV__(runTimeSeconds());
3287 
3288  std::set<std::string /*name*/> tableNames = theInterface_->getAllTableNames();
3289  __GEN_COUT__ << "Number of Tables: " << tableNames.size() << __E__;
3290 
3291  __GEN_COUTV__(runTimeSeconds());
3292 
3293  for(const auto& fullName : tableNames)
3294  {
3295  if(fullName.find(debugGroupName) != std::string::npos)
3296  {
3297  __GEN_COUTV__(fullName);
3298  }
3299  }
3300  __GEN_COUTV__(runTimeSeconds());
3301 
3302  TableGroupKey latestGroupKey = theInterface_->findLatestGroupKey(debugGroupName);
3303  __GEN_COUTV__(latestGroupKey);
3304 
3305  __GEN_COUTV__(runTimeSeconds());
3306 
3307  TableBase localGroupMemberCacheSaver(
3308  true /*special table*/
3309  , //special table only allows 1 view in cache and does not load schema (which is perfect for this temporary table),
3310  TableBase::GROUP_CACHE_PREPEND + debugGroupName);
3311  TableVersion lastestGroupCacheKey =
3312  theInterface_->findLatestVersion(&localGroupMemberCacheSaver);
3313  __GEN_COUTV__(lastestGroupCacheKey);
3314 
3315  __GEN_COUTV__(runTimeSeconds());
3316 
3317  //test a group save that already exists
3318  try
3319  {
3320  TableGroupKey groupKey(int(0));
3321  __GEN_COUT__ << "Testing group save of pre-existing " << debugGroupName << "("
3322  << groupKey << ")" << __E__;
3323  std::map<std::string, TableVersion> groupMembers;
3324  groupMembers["DesktopIconTable"] = TableVersion(123);
3325  theInterface_->saveTableGroup(
3326  groupMembers,
3327  TableGroupKey::getFullGroupString(debugGroupName, groupKey));
3328  }
3329  catch(...)
3330  {
3331  __GEN_COUT__ << "Exception during group save." << __E__;
3332  }
3333  __GEN_COUTV__(runTimeSeconds());
3334 
3335  //test a group save that does not already exists
3336  try
3337  {
3338  std::string debugGroupName = "testGroupSave";
3339  TableGroupKey groupKey(int(2));
3340  __GEN_COUT__ << "Testing group save of non-existing " << debugGroupName << "("
3341  << groupKey << ")" << __E__;
3342  std::map<std::string, TableVersion> groupMembers;
3343  groupMembers["DesktopIconTable"] = TableVersion(123);
3344  groupMembers["MessageFacilityTable"] = TableVersion(7);
3345  theInterface_->saveTableGroup(
3346  groupMembers,
3347  TableGroupKey::getFullGroupString(debugGroupName, groupKey));
3348  }
3349  catch(...)
3350  {
3351  __GEN_COUT__ << "Exception during new group save." << __E__;
3352  }
3353  __GEN_COUTV__(runTimeSeconds());
3354 
3355  //test a table save that already exists
3356  {
3357  std::string documentNameToLoad = "XDAQApplicationTable";
3358  TableVersion documentVersionToLoad(134);
3359 
3360  __GEN_COUT__ << "Testing table save of pre-existing " << documentNameToLoad
3361  << __E__;
3362 
3363  { //load to prove it exists
3364  TableBase localDocLoader(
3365  documentNameToLoad); //can not use special table when filling
3366  localDocLoader.changeVersionAndActivateView(
3367  localDocLoader.createTemporaryView(), documentVersionToLoad);
3368  theInterface_->fill(&localDocLoader, documentVersionToLoad);
3369  __SS__;
3370  localDocLoader.print(ss);
3371  __GEN_COUTV__(ss.str());
3372  }
3373  __GEN_COUTV__(runTimeSeconds());
3374 
3375  try
3376  { //attempt to save over existing version
3377  std::string documentNameToSave = documentNameToLoad;
3378  TableBase
3379  localDocSaver( //true /*special table*/, //special table only allows 1 view in cache and does not load schema (which is perfect for this check),
3380  documentNameToSave); //can not use special table when filling
3381  localDocSaver.changeVersionAndActivateView(
3382  localDocSaver.createTemporaryView(), documentVersionToLoad);
3383 
3384  std::string json = "{ }";
3385  localDocSaver.getViewP()->setCustomStorageData(json);
3386 
3387  __COUTT__ << "Saving JSON string: "
3388  << localDocSaver.getViewP()->getCustomStorageData() << __E__;
3389 
3390  __COUTT__ << "Saving JSON doc as "
3391  << localDocSaver.getView().getTableName() << "("
3392  << localDocSaver.getView().getVersion().toString() << ")"
3393  << __E__;
3394 
3395  // save to db, and do not allow overwrite
3396  theInterface_->saveActiveVersion(&localDocSaver, false /* overwrite */);
3397  }
3398  catch(...)
3399  {
3400  __GEN_COUT__ << "Exception during table save." << __E__;
3401  }
3402  __GEN_COUTV__(runTimeSeconds());
3403 
3404  { //load to prove it exists
3405  TableBase localDocLoader(
3406  documentNameToLoad); //can not use special table when filling
3407  localDocLoader.changeVersionAndActivateView(
3408  localDocLoader.createTemporaryView(), documentVersionToLoad);
3409  theInterface_->fill(&localDocLoader, documentVersionToLoad);
3410  __SS__;
3411  localDocLoader.print(ss);
3412  __GEN_COUTV__(ss.str());
3413  }
3414  __GEN_COUTV__(runTimeSeconds());
3415  }
3416  __GEN_COUTV__(runTimeSeconds());
3417 
3418  //test a table save that does not already exist
3419  {
3420  std::string documentNameToLoad = "MessageFacilityTable";
3421  TableVersion documentVersionToLoad(7);
3422  TableBase localDocLoader(
3423  documentNameToLoad); //can not use special table when filling
3424 
3425  __GEN_COUT__ << "Testing table save of non-existing " << documentNameToLoad
3426  << __E__;
3427 
3428  { //load to prove it exists
3429  localDocLoader.changeVersionAndActivateView(
3430  localDocLoader.createTemporaryView(), documentVersionToLoad);
3431  theInterface_->fill(&localDocLoader, documentVersionToLoad);
3432  __SS__;
3433  localDocLoader.print(ss);
3434  __GEN_COUTV__(ss.str());
3435  __GEN_COUTV__(runTimeSeconds());
3436  }
3437  __GEN_COUTV__(runTimeSeconds());
3438 
3439  try
3440  { //attempt to save new version
3441 
3442  // modify it
3444  theInterface_->findLatestVersion(&localDocLoader));
3445  localDocLoader.getViewP()->setVersion(newVersion);
3446 
3447  __GEN_COUTT__ << "Saving new table as "
3448  << localDocLoader.getView().getTableName() << "("
3449  << localDocLoader.getView().getVersion().toString() << ")"
3450  << __E__;
3451 
3452  localDocLoader.getViewP()->setValueAsString(
3453  "10.226.9.17", 0, 4); //modify value that is 10.226.9.16
3454 
3455  __SS__;
3456  localDocLoader.print(ss);
3457  __GEN_COUTV__(ss.str());
3458 
3459  // save to db, and do not allow overwrite
3460  theInterface_->saveActiveVersion(&localDocLoader, false /* overwrite */);
3461  }
3462  catch(...)
3463  {
3464  __GEN_COUT__ << "Exception during new table save." << __E__;
3465  }
3466  __GEN_COUTV__(runTimeSeconds());
3467  }
3468  __GEN_COUTV__(runTimeSeconds());
3469  return;
3470 
3471  // for each group get member map & comment, author, time, and type for latest key
3472  for(auto& groupInfo : allGroupInfo_)
3473  {
3474  try
3475  {
3476  groupInfo.second.latestKey_ = groupInfo.second.getLastKey();
3477  loadTableGroup(groupInfo.first /*groupName*/,
3478  groupInfo.second.latestKey_,
3479  false /*doActivate*/,
3480  &groupInfo.second.latestKeyMemberMap_ /*groupMembers*/,
3481  0 /*progressBar*/,
3482  0 /*accumulateErrors*/,
3483  &groupInfo.second.latestKeyGroupComment_,
3484  &groupInfo.second.latestKeyGroupAuthor_,
3485  &groupInfo.second.latestKeyGroupCreationTime_,
3486  true /*doNotLoadMember*/,
3487  &groupInfo.second.latestKeyGroupTypeString_);
3488  }
3489  catch(const std::runtime_error& e)
3490  {
3491  __GEN_COUT_WARN__
3492  << "Error occurred loading latest group info into cache for '"
3493  << groupInfo.first << "(" << groupInfo.second.latestKey_ << ")': \n"
3494  << e.what() << __E__;
3495 
3496  groupInfo.second.latestKey_ = TableGroupKey::INVALID;
3497  groupInfo.second.latestKeyGroupComment_ =
3498  ConfigurationManager::UNKNOWN_INFO;
3499  groupInfo.second.latestKeyGroupAuthor_ =
3500  ConfigurationManager::UNKNOWN_INFO;
3501  groupInfo.second.latestKeyGroupCreationTime_ =
3502  ConfigurationManager::UNKNOWN_TIME;
3503  groupInfo.second.latestKeyGroupTypeString_ =
3504  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
3505  groupInfo.second.latestKeyMemberMap_ = {};
3506  }
3507  catch(...)
3508  {
3509  __GEN_COUT_WARN__
3510  << "Error occurred loading latest group info into cache for '"
3511  << groupInfo.first << "(" << groupInfo.second.latestKey_ << ")'..."
3512  << __E__;
3513 
3514  groupInfo.second.latestKey_ = TableGroupKey::INVALID;
3515  groupInfo.second.latestKeyGroupComment_ =
3516  ConfigurationManager::UNKNOWN_INFO;
3517  groupInfo.second.latestKeyGroupAuthor_ =
3518  ConfigurationManager::UNKNOWN_INFO;
3519  groupInfo.second.latestKeyGroupCreationTime_ =
3520  ConfigurationManager::UNKNOWN_TIME;
3521  groupInfo.second.latestKeyGroupTypeString_ =
3522  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
3523  groupInfo.second.latestKeyMemberMap_ = {};
3524  }
3525  } // end group info loop
3526  __GEN_COUTV__(runTimeSeconds());
3527  } // end get group info
3528  catch(const std::runtime_error& e)
3529  {
3530  __SS__ << "A fatal error occurred reading the info for all table groups. Error: "
3531  << e.what() << __E__;
3532  __GEN_COUT_ERR__ << "\n" << ss.str();
3533  if(accumulatedWarnings)
3534  *accumulatedWarnings += ss.str();
3535  else
3536  throw;
3537  }
3538  catch(...)
3539  {
3540  __SS__ << "An unknown fatal error occurred reading the info for all table groups."
3541  << __E__;
3542  __GEN_COUT_ERR__ << "\n" << ss.str();
3543  if(accumulatedWarnings)
3544  *accumulatedWarnings += ss.str();
3545  else
3546  throw;
3547  } //end catch
3548 
3549  __GEN_COUT__ << "testXDAQContext() end runTimeSeconds()=" << runTimeSeconds()
3550  << __E__;
3551  return;
3552 
3553  try
3554  {
3555  __GEN_COUT__ << "Loading table..." << __E__;
3556  loadTableGroup("FETest", TableGroupKey(2)); // Context_1
3557  ConfigurationTree t = getNode("/FETable/DEFAULT/FrontEndType");
3558 
3559  std::string v;
3560 
3561  __GEN_COUT__ << __E__;
3562  t.getValue(v);
3563  __GEN_COUT__ << "Value: " << v << __E__;
3564  __GEN_COUT__ << "Value index: " << t.getValue<int>() << __E__;
3565 
3566  return;
3567  }
3568  catch(...)
3569  {
3570  __GEN_COUT__ << "Failed to load table..." << __E__;
3571  }
3572 } //end testXDAQContext()
TableVersion saveNewVersion(TableBase *table, TableVersion temporaryVersion, TableVersion newVersion=TableVersion())
TableVersion saveNewTable(const std::string &tableName, TableVersion temporaryVersion=TableVersion(), bool makeTemporary=false)
modifiers of generic TableBase
TableGroupKey findTableGroup(const std::string &groupName, const std::map< std::string, TableVersion > &groupMembers, const std::map< std::string, std::string > &groupAliases=std::map< std::string, std::string >())
TableVersion updateTableCells(const std::string &tableName, const std::map< std::string, std::map< std::string, std::string >> &cellUpdates, const std::string &author, TableVersion sourceVersion=TableVersion(), const std::string &versionAlias="", const std::string &sourceAlias="", const std::string &comment="")
void testXDAQContext(void)
for debugging
TableVersion saveNewBackbone(TableVersion temporaryVersion=TableVersion())
const GroupInfo & getGroupInfo(const std::string &groupName, bool attemptToReloadKeys=false)
public group cache handling
const std::map< std::string, TableInfo > & getAllTableInfo(bool refresh=false, std::string *accumulatedWarnings=0, const std::string &errorFilterName="", bool getGroupKeys=false, bool getGroupInfo=false, bool initializeActiveGroups=false)
void loadTableGroup(const std::string &tableGroupName, const TableGroupKey &tableGroupKey, bool doActivate=false, std::map< std::string, TableVersion > *groupMembers=0, ProgressBar *progressBar=0, std::string *accumulateWarnings=0, std::string *groupComment=0, std::string *groupAuthor=0, std::string *groupCreateTime=0, bool doNotLoadMember=false, std::string *groupTypeString=0, std::map< std::string, std::string > *groupAliases=0, ConfigurationManager::LoadGroupType groupTypeToLoad=ConfigurationManager::LoadGroupType::ALL_TYPES, bool ignoreVersionTracking=false)
TableVersion copyViewToCurrentColumns(const std::string &tableName, TableVersion sourceVersion)
copyViewToCurrentColumns
TableGroupKey saveNewTableGroup(const std::string &groupName, std::map< std::string, TableVersion > &groupMembers, const std::string &groupComment=TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT, std::map< std::string, std::string > *groupAliases=0)
modifiers of a table group based on alias, e.g. "Physics"
void activateTableGroup(const std::string &tableGroupName, TableGroupKey tableGroupKey, std::string *accumulatedTreeErrors=0, std::string *groupTypeString=0)
modifiers of table groups
TableBase * getMetadataTable(TableVersion fillVersion=TableVersion())
created for use in otsdaq_flatten_system_aliases and otsdaq_export_system_aliases,...
TableVersion saveModifiedVersion(const std::string &tableName, TableVersion originalVersion, bool makeTemporary, TableBase *config, TableVersion temporaryModifiedVersion, bool ignoreDuplicates=false, bool lookForEquivalent=false, bool *foundEquivalent=nullptr)
TableVersion createTemporaryBackboneView(TableVersion sourceViewVersion=TableVersion())
-1, from MockUp, else from valid backbone view version
void clearCachedVersions(const std::string &tableName)
std::map< std::string, std::map< std::string, TableVersion > > getVersionAliases(void) const
void eraseTemporaryVersion(const std::string &tableName, TableVersion targetVersion=TableVersion())
void preloadVersionCreationTimes(void)
parallel load of all version creation times into the process-wide cache
static void compareTableGroupThread(ConfigurationManagerRW *cfgMgr, std::string groupName, ots::TableGroupKey groupKeyToCompare, const std::map< std::string, TableVersion > &groupMemberMap, const std::map< std::string, std::string > &memberTableAliases, std::atomic< bool > *theFoundIdentical, ots::TableGroupKey *theIdenticalKey, std::mutex *theThreadMutex, std::shared_ptr< std::atomic< bool >> theThreadDone)
static void loadTableGroupThread(ConfigurationManagerRW *cfgMgr, std::string groupName, ots::TableGroupKey groupKey, std::shared_ptr< ots::GroupInfo > theGroupInfo, std::shared_ptr< std::atomic< bool >> theThreadDone)
loadTableGroupThread()
time_t getVersionCreationTime(const std::string &tableName, TableVersion version)
static void loadTableInfoThread(ConfigurationManagerRW *cfgMgr, std::string tableName, TableBase *existingTable, std::shared_ptr< ots::TableInfo > tableInfo, std::shared_ptr< std::atomic< bool >> threadDone)
loadTableInfoThread()
std::map< std::string, std::map< std::string, TableVersion > > getVersionAliases(void) const
static const std::string & convertGroupTypeToName(const ConfigurationManager::GroupType &groupTypeId)
void restoreActiveTableGroups(bool throwErrors=false, const std::string &pathToActiveGroupsFile="", ConfigurationManager::LoadGroupType onlyLoadIfBackboneOrContext=ConfigurationManager::LoadGroupType::ALL_TYPES, std::string *accumulatedWarnings=0)
std::map< std::string, TableVersion > getActiveVersions(void) const
getActiveVersions
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
"root/parent/parent/"
void loadTableGroup(const std::string &tableGroupName, const TableGroupKey &tableGroupKey, bool doActivate=false, std::map< std::string, TableVersion > *groupMembers=0, ProgressBar *progressBar=0, std::string *accumulateWarnings=0, std::string *groupComment=0, std::string *groupAuthor=0, std::string *groupCreateTime=0, bool doNotLoadMember=false, std::string *groupTypeString=0, std::map< std::string, std::string > *groupAliases=0, ConfigurationManager::LoadGroupType groupTypeToLoad=ConfigurationManager::LoadGroupType::ALL_TYPES, bool ignoreVersionTracking=false, std::map< std::string, TableVersion > mergeInTables={}, std::map< std::string, TableVersion > overrideTables={})
void init(std::string *accumulatedErrors=0, bool initForWriteAccess=false, std::string *accumulatedWarnings=0)
static const std::string & getTypeNameOfGroup(const std::map< std::string, TableVersion > &memberMap)
void destroyTableGroup(const std::string &theGroup="", bool onlyDeactivate=false)
static const std::string ACTIVE_GROUPS_FILENAME
added env check for otsdaq_flatten_active_to_version to function
const TableBase * getTableByName(const std::string &configurationName) const
void getValue(T &value) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
TableVersion createTemporaryView(TableVersion sourceViewVersion=TableVersion(), TableVersion destTemporaryViewVersion=TableVersion::getNextTemporaryVersion())
source of -1, from MockUp, else from valid view version
Definition: TableBase.cc:1773
void trimTemporary(TableVersion targetVersion=TableVersion())
Definition: TableBase.cc:362
unsigned int getNumberOfStoredViews(void) const
Definition: TableBase.cc:856
TableVersion checkForDuplicate(TableVersion needleVersion, TableVersion ignoreVersion=TableVersion()) const
Definition: TableBase.cc:404
bool isActive(void)
isActive
Definition: TableBase.cc:950
TableView * getTemporaryView(TableVersion temporaryVersion)
Definition: TableBase.cc:1884
const unsigned int MAX_VIEWS_IN_CACHE
Definition: TableBase.h:30
TableVersion getNextVersion(void) const
Definition: TableBase.cc:1860
TableVersion copyView(const TableView &sourceView, TableVersion destinationVersion, const std::string &author, bool looseColumnMatching=false)
Definition: TableBase.cc:1714
void print(std::ostream &out=std::cout) const
always prints active view
Definition: TableBase.cc:277
TableVersion getNextTemporaryVersion(void) const
Definition: TableBase.cc:1837
void trimCache(unsigned int trimSize=-1)
Definition: TableBase.cc:322
std::string toString(void) const
toString
static TableGroupKey getNextKey(const TableGroupKey &key=TableGroupKey())
static void getGroupNameAndKey(const std::string &fullGroupString, std::string &groupName, TableGroupKey &key)
requires fullGroupString created as name + "_v" + key + ""
bool isInvalid(void) const
isInvalid
static std::string getFullGroupString(const std::string &groupName, const TableGroupKey &key, const std::string &preKey="_v", const std::string &postKey="")
bool isMockupVersion(void) const
std::string toString(void) const
toString
Definition: TableVersion.cc:33
bool isInvalid(void) const
isInvalid
static TableVersion getNextVersion(const TableVersion &version=TableVersion())
bool isScratchVersion(void) const
bool isTemporaryVersion(void) const
static TableVersion getNextTemporaryVersion(const TableVersion &version=TableVersion())
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
< in included .icc source
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1090
unsigned int findColByType(const std::string &type, unsigned int startingCol=0) const
Definition: TableView.cc:1996
void setVersion(const T &version)
< in included .icc source
const std::string & getCustomStorageData(void) const
Getters.
Definition: TableView.h:72
unsigned int getColUID(void) const
Definition: TableView.cc:1322
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1973
void setValue(const T &value, unsigned int row, unsigned int col)
< in included .icc source
unsigned int addRow(const std::string &author="", unsigned char incrementUniqueData=false, const std::string &baseNameAutoUID="", unsigned int rowToAdd=(unsigned int) -1, std::string childLinkIndex="", std::string groupId="")
Definition: TableView.cc:3490
void setCustomStorageData(const std::string &storageData)
Definition: TableView.h:169
defines used also by OtsConfigurationWizardSupervisor
TableEditStruct & getTableEditStruct(const std::string &tableName, bool markModified=false)
Note: if markModified, and table not found in group, this function will try to add it to group.
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static std::string stackTrace(void)