otsdaq  3.06.00
TableBase.cc
1 #include "otsdaq/TableCore/TableBase.h"
2 
3 #include <iostream> // std::cout
4 #include <typeinfo>
5 
6 #include "otsdaq/TableCore/TableInfoReader.h"
7 
8 using namespace ots;
9 
10 #undef __MF_SUBJECT__
11 #define __MF_SUBJECT__ "TableBase"
12 #undef __COUT_HDR__
13 #define __COUT_HDR__ ("TableBase-" + getTableName() + "\t<> ")
14 
15 const std::string TableBase::GROUP_CACHE_PREPEND = "GroupCache_";
16 const std::string TableBase::JSON_DOC_PREPEND = "JSONDoc_";
17 const std::string TableBase::GROUP_METADATA_TABLE_NAME = "TableGroupMetadata";
18 
19 //==============================================================================
25 TableBase::TableBase(const std::string& tableName,
26  std::string* accumulatedExceptions)
27  : MAX_VIEWS_IN_CACHE(20) // This is done, so that inheriting table classes could have
28  // varying amounts of cache
29  , tableName_(tableName)
30  , activeTableView_(0)
31  , mockupTableView_(tableName)
32 {
33  // December 2021 started seeing an issue where traceTID is found to be cleared to 0
34  // which crashes TRACE if __COUT__ is used in a Table plugin constructor
35  // This check and re-initialization seems to cover up the issue for now.
36  // Why it is cleared to 0 after the constructor sets it to -1 is still unknown.
37  // Note: it seems to only happen on the first alphabetially ARTDAQ Configure Table plugin.
38  if(traceTID == 0)
39  {
40  std::cout << "TableBase Before traceTID=" << traceTID << __E__;
41  char buf[40];
42  traceInit(trace_name(TRACE_NAME, __TRACE_FILE__, buf, sizeof(buf)), 0);
43  std::cout << "TableBase After traceTID=" << traceTID << __E__;
44  __COUT__ << "TableBase TRACE reinit and Constructed." << __E__;
45  }
46 
47  if(tableName == "")
48  {
49  __SS__ << "Do not allow anonymous table view construction!" << __E__;
50  ss << StringMacros::stackTrace() << __E__;
51  __SS_THROW__;
52  }
53 
54  // initialize special group metadata table
55  if(tableName_ == TableBase::GROUP_METADATA_TABLE_NAME)
56  {
58  return;
59  }
60 
61  //if special GROUP CACHE table, handle construction in a special way
62  if(tableName.substr(0, TableBase::GROUP_CACHE_PREPEND.length()) ==
63  TableBase::GROUP_CACHE_PREPEND ||
64  tableName.substr(0, TableBase::JSON_DOC_PREPEND.length()) ==
65  TableBase::JSON_DOC_PREPEND)
66  {
67  __COUTT__ << "TableBase for special table '" << tableName << "' constructed."
68  << __E__;
69  return;
70  } //end special GROUP CACHE table construction
71 
72  bool dbg = false; // tableName == "ARTDAQEventBuilderTable";
73  if(dbg)
74  __COUTV__(tableName);
75  // info reader fills up the mockup view
76  TableInfoReader tableInfoReader(accumulatedExceptions);
77  if(dbg)
78  __COUT__ << "Reading..." << __E__;
79  try // to read info
80  {
81  std::string returnedExceptions = tableInfoReader.read(this);
82  if(dbg)
83  __COUT__ << "Read.";
84  if(returnedExceptions != "")
85  __COUT_ERR__ << returnedExceptions << __E__;
86 
87  if(accumulatedExceptions)
88  *accumulatedExceptions += std::string("\n") + returnedExceptions;
89  }
90  catch(...) // if accumulating exceptions, continue to and return, else throw
91  {
92  __SS__ << "Failure reading table schema info for table '" << tableName << "!' "
93  << "Perhaps the XML "
94  "table definition has moved or been corrupted? Check your "
95  "Table Info area."
96  << __E__;
97  try
98  {
99  throw;
100  }
101  catch(const std::runtime_error& e)
102  {
103  ss << "Here was the error:\n" << e.what() << __E__;
104  }
105  catch(...)
106  {
107  ;
108  } //ignore unknown types
109  __COUT_ERR__ << "\n" << ss.str();
110  if(accumulatedExceptions)
111  *accumulatedExceptions += std::string("\n") + ss.str();
112  else
113  throw;
114  return; // do not proceed with mockup check if this failed
115  }
116  if(dbg)
117  __COUT__ << "Initializing..." << __E__;
118  // call init on mockup view to verify columns
119  try
120  {
121  getMockupViewP()->init();
122  if(dbg)
123  __COUT__ << "Init." << __E__;
124  }
125  catch(std::runtime_error&
126  e) // if accumulating exceptions, continue to and return, else throw
127  {
128  if(accumulatedExceptions)
129  *accumulatedExceptions += std::string("\n") + e.what();
130  else
131  throw;
132  }
133 } // end constructor()
134 
135 //==============================================================================
140 TableBase::TableBase(bool specialTable, const std::string& specialTableName)
141  : MAX_VIEWS_IN_CACHE(1) // This is done, so that inheriting table classes could have
142  // varying amounts of cache
143  , tableName_(specialTableName)
144  , activeTableView_(0)
145  , mockupTableView_(specialTableName)
146 {
147  // December 2021 started seeing an issue where traceTID is found to be cleared to 0
148  // which crashes TRACE if __COUT__ is used in a Table plugin constructor
149  // This check and re-initialization seems to cover up the issue for now.
150  // Why it is cleared to 0 after the constructor sets it to -1 is still unknown.
151  // Note: it seems to only happen on the first table alphabetically (ARTDAQ Configure Table plugin).
152  if(traceTID == 0)
153  {
154  std::cout << "TableBase Before traceTID=" << traceTID << __E__;
155  char buf[40];
156  traceInit(trace_name(TRACE_NAME, __TRACE_FILE__, buf, sizeof(buf)), 0);
157  std::cout << "TableBase After traceTID=" << traceTID << __E__;
158  __COUT__ << "TableBase TRACE reinit and Constructed." << __E__;
159  }
160 
161  __COUTT__ << "Special table '" << tableName_ << "' constructed. " << specialTable
162  << __E__;
163 
164  // initialize special group metadata table
165  if(tableName_ == TableBase::GROUP_METADATA_TABLE_NAME)
167 
168 } // special table constructor()
169 
170 //==============================================================================
173 {
174  __COUTT__ << "Special table '" << tableName_ << "' constructing..." << __E__;
175  // Note: "TableGroupMetadata" should never be in conflict
176  // because all other tables end in "...Table"
177 
178  // This is a table called TableGroupMetadata
179  // with 4 fields:
180  // - GroupAliases
181  // - GroupAuthor
182  // - GroupCreationTime
183  // - CommentDescription
184 
185  // groupMetadataTable_.setTableName(
186  // TableBase::GROUP_METADATA_TABLE_NAME);
187  std::vector<TableViewColumnInfo>* colInfo = getMockupViewP()->getColumnsInfoP();
188  colInfo->push_back(
189  TableViewColumnInfo(TableViewColumnInfo::TYPE_UID, // just to make init() happy
190  "UnusedUID",
191  "UNUSED_UID",
193  0 /*Default*/,
194  "",
195  0 /*Min*/,
196  0 /*Max*/,
197  0));
198  colInfo->push_back(TableViewColumnInfo(TableViewColumnInfo::TYPE_DATA,
199  "GroupAliases",
200  "GROUP_ALIASES",
201  TableViewColumnInfo::DATATYPE_STRING,
202  0 /*Default*/,
203  "",
204  0 /*Min*/,
205  0 /*Max*/,
206  0));
207  colInfo->push_back(TableViewColumnInfo(
208  TableViewColumnInfo::TYPE_COMMENT, // just to make init() happy
209  TableViewColumnInfo::COL_NAME_COMMENT,
210  "COMMENT_DESCRIPTION",
211  TableViewColumnInfo::DATATYPE_STRING,
212  0 /*Default*/,
213  "",
214  0 /*Min*/,
215  0 /*Max*/,
216  0));
217  colInfo->push_back(TableViewColumnInfo(
218  TableViewColumnInfo::TYPE_AUTHOR, // just to make init() happy
219  "GroupAuthor",
220  "AUTHOR",
221  TableViewColumnInfo::DATATYPE_STRING,
222  0 /*Default*/,
223  "",
224  0 /*Min*/,
225  0 /*Max*/,
226  0));
227  colInfo->push_back(TableViewColumnInfo(TableViewColumnInfo::TYPE_TIMESTAMP,
228  "GroupCreationTime",
229  "GROUP_CREATION_TIME",
230  TableViewColumnInfo::DATATYPE_TIME,
231  0 /*Default*/,
232  "",
233  0 /*Min*/,
234  0 /*Max*/,
235  0));
236  auto tmpVersion = createTemporaryView();
237  setActiveView(tmpVersion);
238  // only need this one and only row for all time
239  getViewP()->addRow();
240 
241  __COUTT__ << "Special table '" << tableName_ << "' constructed." << __E__;
242 } //end specialMetaTableConstructor()
243 
253 //==============================================================================
255 
256 //==============================================================================
257 std::string TableBase::getTypeId() { return typeid(this).name(); }
258 
259 //==============================================================================
260 void TableBase::init(ConfigurationManager* /*tableManager*/)
261 {
262  //__COUT__ << "Default TableBase::init() called." << __E__;
263 }
264 
265 //==============================================================================
266 void TableBase::reset(bool keepTemporaryVersions)
267 {
268  // std::cout << __COUT_HDR_FL__ << "resetting" << __E__;
269  deactivate();
270  if(keepTemporaryVersions)
271  trimCache(0);
272  else // clear all
273  tableViews_.clear();
274 }
275 
276 //==============================================================================
277 void TableBase::print(std::ostream& out) const
278 {
279  // std::cout << __COUT_HDR_FL__ << "activeVersion_ " << activeVersion_ << "
280  // (INVALID_VERSION:=" << INVALID_VERSION << ")" << __E__;
281  if(!activeTableView_)
282  {
283  __COUT_ERR__ << "ERROR: No active view set" << __E__;
284  return;
285  }
286  activeTableView_->print(out);
287 }
288 
289 //==============================================================================
293 {
294  if(!isStored(version))
295  {
296  tableViews_.emplace(std::make_pair(version, TableView(tableName_)));
297  tableViews_.at(version).copy(
298  mockupTableView_, version, mockupTableView_.getAuthor());
299  trimCache();
300  if(!isStored(version)) // the trim cache is misbehaving!
301  {
302  __SS__ << "IMPOSSIBLE ERROR: trimCache() is deleting the "
303  "latest view version "
304  << version << "!" << __E__;
305  __SS_THROW__;
306  }
307  }
308  else
309  {
310  __SS__ << "View to fill with mockup already exists: " << version
311  << ". Cannot overwrite!" << __E__;
312  ss << StringMacros::stackTrace() << __E__;
313  __SS_THROW__;
314  }
315 } // end setupMockupView()
316 
317 //==============================================================================
322 void TableBase::trimCache(unsigned int trimSize)
323 {
324  // delete cached views, if necessary
325 
326  if(trimSize == (unsigned int)-1) // if -1, use MAX_VIEWS_IN_CACHE
327  trimSize = MAX_VIEWS_IN_CACHE;
328 
329  // int i = 0;
330  while(getNumberOfStoredViews() > trimSize)
331  {
332  TableVersion versionToDelete;
333  time_t stalestTime = -1;
334 
335  for(auto& viewPair : tableViews_)
336  if(!viewPair.first.isTemporaryVersion())
337  {
338  if(stalestTime == -1 || viewPair.second.getLastAccessTime() < stalestTime)
339  {
340  versionToDelete = viewPair.first;
341  stalestTime = viewPair.second.getLastAccessTime();
342  if(!trimSize)
343  break; // if trimSize is 0, then just take first found
344  }
345  }
346 
347  if(versionToDelete.isInvalid())
348  {
349  __SS__ << "Can NOT have a stored view with an invalid version!" << __E__;
350  __SS_THROW__;
351  }
352 
353  eraseView(versionToDelete);
354  }
355 }
356 
357 //==============================================================================
363 {
364  if(targetVersion.isInvalid()) // erase all temporary
365  {
366  for(auto it = tableViews_.begin(); it != tableViews_.end(); /*no increment*/)
367  {
368  if(it->first.isTemporaryVersion())
369  {
370  //__COUT__ << "Trimming temporary version: " << it->first << __E__;
371  if(activeTableView_ &&
372  getViewVersion() == it->first) // if activeVersion is being erased!
373  deactivate(); // deactivate active view, instead of guessing at next
374  // active view
375  tableViews_.erase(it++);
376  }
377  else
378  ++it;
379  }
380  }
381  else if(targetVersion.isTemporaryVersion()) // erase target
382  {
383  //__COUT__ << "Trimming temporary version: " << targetVersion << __E__;
384  eraseView(targetVersion);
385  }
386  else
387  {
388  // else this is a persistent version!
389  __SS__ << "Temporary trim target was a persistent version: " << targetVersion
390  << __E__;
391  __SS_THROW__;
392  }
393 }
394 
395 //==============================================================================
405  TableVersion ignoreVersion) const
406 {
407  auto needleIt = tableViews_.find(needleVersion);
408  if(needleIt == tableViews_.end())
409  {
410  // else this is a persistent version!
411  __SS__ << "needleVersion does not exist: " << needleVersion << __E__;
412  __SS_THROW__;
413  }
414 
415  const TableView* needleView = &(needleIt->second);
416  unsigned int rows = needleView->getNumberOfRows();
417  unsigned int cols = needleView->getNumberOfColumns();
418 
419  bool match;
420  unsigned int potentialMatchCount = 0;
421 
422  if(TTEST(9))
423  needleView->print();
424 
425  __COUTTV__(needleVersion);
426  __COUTTV__(ignoreVersion);
427  __COUTTV__(rows);
428  __COUTTV__(cols);
429 
430  // for each table in cache
431  // check each row,col
432  auto viewPairReverseIterator = tableViews_.rbegin();
433  for(; viewPairReverseIterator != tableViews_.rend(); ++viewPairReverseIterator)
434  {
435  __COUTTV__(viewPairReverseIterator->first);
436  if(viewPairReverseIterator->first == needleVersion)
437  continue; // skip needle version
438  __COUTVS__(2, viewPairReverseIterator->first);
439  if(viewPairReverseIterator->first == ignoreVersion)
440  continue; // skip ignore version
441  __COUTVS__(2, viewPairReverseIterator->first);
442  if(viewPairReverseIterator->first.isTemporaryVersion())
443  continue; // skip temporary versions
444  __COUTVS__(2, viewPairReverseIterator->first);
445  if(viewPairReverseIterator->second.getNumberOfRows() != rows)
446  continue; // row mismatch
447  __COUTVS__(2, viewPairReverseIterator->first);
448  if(viewPairReverseIterator->second.getDataColumnSize() != cols ||
449  viewPairReverseIterator->second.getSourceColumnMismatch() != 0)
450  continue; // col mismatch
451 
452  ++potentialMatchCount;
453  __COUTT__ << "Checking version... " << viewPairReverseIterator->first << __E__;
454 
455  // viewPairReverseIterator->second.print();
456 
457  // if column source names do not match then skip
458  // source names are potentially different from
459  // getColumnsInfo()/getColumnStorageNames
460 
461  match = viewPairReverseIterator->second.getSourceColumnNames().size() ==
462  needleView->getSourceColumnNames().size();
463  __COUTTV__(viewPairReverseIterator->second.getSourceColumnNames().size());
464  __COUTTV__(needleView->getSourceColumnNames().size());
465  if(match)
466  {
467  for(auto& haystackColName :
468  viewPairReverseIterator->second.getSourceColumnNames())
469  if(needleView->getSourceColumnNames().find(haystackColName) ==
470  needleView->getSourceColumnNames().end())
471  {
472  __COUT__ << "Found column name mismatch for '" << haystackColName
473  << "'... So allowing same data!" << __E__;
474 
475  match = false;
476  break;
477  }
478  }
479  else if(TTEST(1))
480  {
481  int i = 0;
482  for(auto& srcCol : viewPairReverseIterator->second.getSourceColumnNames())
483  __COUTT__ << "compare Col #" << i++ << " " << srcCol << __E__;
484  i = 0;
485  for(auto& srcCol : needleView->getSourceColumnNames())
486  __COUTT__ << "source Col #" << i++ << " " << srcCol << __E__;
487  }
488 
489  __COUTTV__(match);
490 
491  // checking columnsInfo seems to be wrong approach, use getSourceColumnNames
492  // (above) auto viewColInfoIt =
493  // viewPairReverseIterator->second.getColumnsInfo().begin(); for(unsigned
494  // int col=0; match && //note column size must already match
495  // viewPairReverseIterator->second.getColumnsInfo().size() > 3 &&
496  // col<viewPairReverseIterator->second.getColumnsInfo().size()-3;++col,viewColInfoIt++)
497  // if(viewColInfoIt->getName() !=
498  // needleView->getColumnsInfo()[col].getName())
499  // {
500  // match = false;
504  // }
505 
506  for(unsigned int row = 0; match && row < rows; ++row)
507  {
508  for(unsigned int col = 0; col < cols - 2;
509  ++col) // do not consider author and timestamp
510  if(viewPairReverseIterator->second.getDataView()[row][col] !=
511  needleView->getDataView()[row][col])
512  {
513  match = false;
514 
515  __COUTT__
516  << "Value name mismatch " << col << ":"
517  << viewPairReverseIterator->second.getDataView()[row][col] << "["
518  << viewPairReverseIterator->second.getDataView()[row][col].size()
519  << "]"
520  << " vs " << needleView->getDataView()[row][col] << "["
521  << needleView->getDataView()[row][col].size() << "]" << __E__;
522 
523  break;
524  }
525  }
526  if(match)
527  {
528  __COUT_INFO__ << "Duplicate version found: " << viewPairReverseIterator->first
529  << __E__;
530  return viewPairReverseIterator->first;
531  }
532  } // end table version loop
533 
534  __COUT__ << "No duplicates found in " << potentialMatchCount << " potential matches."
535  << __E__;
536  return TableVersion(); // return invalid if no matches
537 } // end checkForDuplicate()
538 
539 //==============================================================================
543  TableVersion v1,
544  TableVersion v2,
545  std::stringstream* diffReport /* = 0 */,
546  std::map<std::string /* uid */, std::vector<std::string /* colName */>>*
547  v1ModifiedRecords /* = 0 */) const
548 {
549  __COUTT__ << "Diffing version... " << v1 << " vs " << v2 << __E__;
550  auto v1It = tableViews_.find(v1);
551  if(v1It == tableViews_.end())
552  {
553  // else this is a persistent version!
554  __SS__ << "Version v" << v1 << " does not exist." << __E__;
555  __SS_THROW__;
556  }
557  auto v2It = tableViews_.find(v2);
558  if(v2It == tableViews_.end())
559  {
560  // else this is a persistent version!
561  __SS__ << "Version v" << v2 << " does not exist." << __E__;
562  __SS_THROW__;
563  }
564 
565  const TableView* view1 = &(v1It->second);
566  const TableView* view2 = &(v2It->second);
567  unsigned int rows1 = view1->getNumberOfRows();
568  unsigned int cols1 = view1->getNumberOfColumns();
569 
570  bool noDifference = true;
571 
572  // check each row,col
573 
574  // if column source names do not match then note
575  // source names are potentially different from
576  // getColumnsInfo()/getColumnStorageNames
577 
578  if(view1->getSourceColumnNames().size() != view2->getSourceColumnNames().size())
579  {
580  __COUT__ << "Found column count mismatch for '"
581  << view1->getSourceColumnNames().size() << " vs "
582  << view2->getSourceColumnNames().size() << __E__;
583 
584  if(diffReport)
585  *diffReport << "<li>Found column count mismatch. The v" << v1
586  << " column count is <b>'" << view1->getSourceColumnNames().size()
587  << "'</b> and the v" << v2 << " column count is <b>'"
588  << view2->getSourceColumnNames().size() << "'</b>." << __E__;
589 
590  noDifference = false;
591  if(!diffReport)
592  return noDifference; //do not need to continue to create report
593  }
594 
595  for(auto& colName1 : view1->getSourceColumnNames())
596  if(view2->getSourceColumnNames().find(colName1) ==
597  view2->getSourceColumnNames().end())
598  {
599  __COUT__ << "Found column name mismatch for '" << colName1 << __E__;
600 
601  if(diffReport)
602  *diffReport << "<li>Found column name mismatch. The v" << v1
603  << " column <b>'" << colName1 << "'</b> was not found in v"
604  << v2 << "." << __E__;
605 
606  noDifference = false;
607  if(!diffReport)
608  return noDifference; //do not need to continue to create report
609  }
610  for(auto& colName2 : view2->getSourceColumnNames())
611  if(view1->getSourceColumnNames().find(colName2) ==
612  view1->getSourceColumnNames().end())
613  {
614  __COUT__ << "Found column name mismatch for '" << colName2 << __E__;
615 
616  if(diffReport)
617  *diffReport << "<li>Found column name mismatch. The v" << v1
618  << " does not have column <b>'" << colName2
619  << "'</b> that was found in v" << v2 << "." << __E__;
620 
621  noDifference = false;
622  if(!diffReport)
623  return noDifference; //do not need to continue to create report
624  }
625 
626  if(rows1 != view2->getNumberOfRows())
627  {
628  __COUT__ << "Found row count mismatch for '" << rows1 << " vs "
629  << view2->getNumberOfRows() << __E__;
630 
631  if(diffReport)
632  *diffReport << "<li>Found row count mismatch. The v" << v1
633  << " row count is <b>'" << rows1 << "'</b> and the v" << v2
634  << " row count is <b>'" << view2->getNumberOfRows() << "'</b>."
635  << __E__;
636 
637  noDifference = false;
638  if(!diffReport)
639  return noDifference; //do not need to continue to create report
640  }
641 
642  //report on missing UIDs
643  std::set<std::string /*uid*/> uidSet1, uidSet2;
644  for(unsigned int row = 0; row < rows1; ++row)
645  uidSet1.insert(view1->getDataView()[row][view1->getColUID()]);
646  for(unsigned int row = 0; row < view2->getNumberOfRows(); ++row)
647  uidSet2.insert(view2->getDataView()[row][view2->getColUID()]);
648 
649  for(auto& uid1 : uidSet1)
650  if(uidSet2.find(uid1) == uidSet2.end())
651  {
652  __COUT__ << "Found record name mismatch for '" << uid1 << __E__;
653 
654  if(diffReport)
655  *diffReport << "<li>Found record name mismatch. The v" << v1
656  << " record <b>'" << uid1 << "'</b> was not found in v" << v2
657  << "." << __E__;
658 
659  noDifference = false;
660  if(!diffReport)
661  return noDifference; //do not need to continue to create report
662  }
663  for(auto& uid2 : uidSet2)
664  if(uidSet1.find(uid2) == uidSet1.end())
665  {
666  __COUT__ << "Found record name mismatch for '" << uid2 << __E__;
667 
668  if(diffReport)
669  *diffReport << "<li>Found record name mismatch. v" << v1
670  << " does not have record <b>'" << uid2
671  << "'</b> that was found in v" << v2 << "." << __E__;
672 
673  noDifference = false;
674  if(!diffReport)
675  return noDifference; //do not need to continue to create report
676  }
677 
678  unsigned int row2, col2;
679  for(unsigned int row = 0; row < rows1 && row < view2->getNumberOfRows(); ++row)
680  {
681  //do not evaluate if UIDs do not match
682  row2 = row;
683  if(view1->getDataView()[row][view1->getColUID()] !=
684  view2->getDataView()[row2][view2->getColUID()])
685  {
686  bool foundUid2 = false;
687 
688  for(row2 = 0; row2 < view2->getNumberOfRows(); ++row2)
689  if(view1->getDataView()[row][view1->getColUID()] ==
690  view2->getDataView()[row2][view2->getColUID()])
691  {
692  foundUid2 = true;
693  break;
694  }
695  __COUTT__ << "Found row ? '" << foundUid2 << " " << row << "," << row2
696  << __E__;
697  if(!foundUid2)
698  continue; //skip view1 record because no matching record found in view2
699  }
700 
701  __COUTT__ << "Found row "
702  << " " << row << "," << row2 << __E__;
703  for(unsigned int col = 0;
704  col < cols1 - 2 && col < view2->getNumberOfColumns() - 2;
705  ++col) // do not consider author and timestamp
706  {
707  //do not evaluate if column names do not match
708  col2 = col;
709  if(view1->getColumnInfo(col).getName() !=
710  view2->getColumnInfo(col2).getName())
711  {
712  bool foundCol2 = false;
713 
714  for(col2 = 0; col2 < view2->getNumberOfColumns() - 2; ++col2)
715  if(view1->getColumnInfo(col).getName() ==
716  view2->getColumnInfo(col2).getName())
717  {
718  foundCol2 = true;
719  break;
720  }
721 
722  __COUTT__ << "Found column ? '" << foundCol2 << " " << col << "," << col2
723  << __E__;
724  if(!foundCol2)
725  continue; //skip view1 column because no matching column name was found in view2
726  }
727 
728  __COUTT__ << "Found column "
729  << " " << col << "," << col2 << __E__;
730  if(view1->getDataView()[row][col] != view2->getDataView()[row2][col2])
731  {
732  __COUT__ << "Found column value mismatch for '" << row << "," << col
733  << " " << view1->getDataView()[row][col] << __E__;
734 
735  if(diffReport)
736  *diffReport << "<li><b>" << view1->getColumnInfo(col).getName()
737  << "</b> value mismatch at v" << v1 << " {UID,r,c}:{<b>"
738  << view1->getDataView()[row][view1->getColUID()]
739  << "</b>," << row << "," << col << "}: <b>'"
740  << view1->getDataView()[row][col] << "'</b> vs value in v"
741  << v2 << ": <b>'" << view2->getDataView()[row2][col2]
742  << "'</b>." << __E__;
743 
744  noDifference = false;
745  if(!diffReport)
746  return noDifference; //do not need to continue to create report
747 
748  if(v1ModifiedRecords) //add uid/colName difference
749  (*v1ModifiedRecords)[view1->getDataView()[row][view1->getColUID()]]
750  .push_back(view1->getColumnInfo(col).getName());
751  }
752  }
753  }
754 
755  if(noDifference && diffReport)
756  *diffReport << "<li>No difference found between v" << v1 << " and v" << v2 << "."
757  << __E__;
758 
759  return noDifference;
760 } // end diffTwoVersions()
761 
762 //==============================================================================
763 void TableBase::changeVersionAndActivateView(TableVersion temporaryVersion,
764  TableVersion version)
765 {
766  auto tmpIt = tableViews_.find(temporaryVersion);
767  if(tableViews_.find(temporaryVersion) == tableViews_.end())
768  {
769  __SS__ << "ERROR: Temporary view version " << temporaryVersion
770  << " doesn't exists!" << __E__;
771  __SS_THROW__;
772  }
773  if(version.isInvalid())
774  {
775  __SS__ << "ERROR: Attempting to create an invalid version " << version
776  << "! Did you really run out of versions? (this should never happen)"
777  << __E__;
778  __SS_THROW__;
779  }
780 
781  if(tableViews_.find(version) != tableViews_.end())
782  __COUT_WARN__ << "WARNING: View version " << version
783  << " already exists! Overwriting." << __E__;
784 
785  auto emplacePair /*it,bool*/ =
786  tableViews_.emplace(std::make_pair(version, TableView(tableName_)));
787  emplacePair.first->second.copy(tmpIt->second, version, tmpIt->second.getAuthor());
788  setActiveView(version);
789  eraseView(temporaryVersion); // delete temp version from tableViews_
790 } //emd changeVersionAndActivateView()
791 
792 //==============================================================================
793 bool TableBase::isStored(const TableVersion& version) const
794 {
795  return (tableViews_.find(version) != tableViews_.end());
796 } //end isStored()
797 
798 //==============================================================================
799 bool TableBase::eraseView(TableVersion version)
800 {
801  if(!isStored(version))
802  return false;
803 
804  if(activeTableView_ &&
805  getViewVersion() == version) // if activeVersion is being erased!
806  deactivate(); // deactivate active view, instead of guessing at next active view
807 
808  tableViews_.erase(version);
809 
810  return true;
811 } //end eraseView()
812 
813 //==============================================================================
814 const std::string& TableBase::getTableName(void) const { return tableName_; }
815 
816 //==============================================================================
817 const std::string& TableBase::getTableDescription(void) const
818 {
819  return tableDescription_;
820 } //end getTableDescription()
821 
822 //==============================================================================
824 {
825  return getView().getVersion();
826 } //end getViewVersion()
827 
828 //==============================================================================
832 {
833  std::set<TableVersion> retSet = getStoredVersions();
834  if(retSet.size() && !retSet.rbegin()->isTemporaryVersion())
835  {
836  return tableViews_.find(*(retSet.rbegin()))->second.getNumberOfColumns() !=
837  mockupTableView_.getNumberOfColumns();
838  }
839  // there are no latest non-temporary tables so there is a mismatch (by default)
840  return true;
841 } //end latestAndMockupColumnNumberMismatch()
842 
843 //==============================================================================
844 std::set<TableVersion> TableBase::getStoredVersions(void) const
845 {
846  std::set<TableVersion> retSet;
847  for(auto& configs : tableViews_)
848  retSet.emplace(configs.first);
849  return retSet;
850 } //end getStoredVersions()
851 
852 //==============================================================================
856 unsigned int TableBase::getNumberOfStoredViews(void) const
857 {
858  unsigned int sz = 0;
859  for(auto& viewPair : tableViews_)
860  if(viewPair.first.isTemporaryVersion())
861  continue;
862  else if(viewPair.first.isInvalid())
863  {
864  //__SS__ << "Can NOT have a stored view with an invalid version!" << __E__;
865  //__SS_THROW__;
866 
867  // NOTE: if this starts happening a lot, could just auto-correct and remove
868  // the invalid version
869  // but it would be better to fix the cause.
870 
871  // FIXME... for now just auto correcting
872  __COUT__ << "There is an invalid version now!.. where did it come from?"
873  << __E__;
874  }
875  else
876  ++sz;
877  return sz;
878 } // end getNumberOfStoredViews()
879 
880 //==============================================================================
881 const TableView& TableBase::getView(
882  TableVersion version /* = TableVersion::INVALID */) const
883 {
884  try
885  {
886  if(version != TableVersion::INVALID)
887  return tableViews_.at(version);
888  }
889  catch(...)
890  {
891  __SS__ << "Table '" << tableName_ << "' does not have version v" << version
892  << " in the cache." << __E__;
893  __SS_THROW__;
894  }
895 
896  if(!activeTableView_)
897  {
898  __SS__ << "There is no active table view setup! Please check your system "
899  "configuration."
900  << __E__;
901  __SS_ONLY_THROW__;
902  }
903  return *activeTableView_;
904 } //end getView()
905 
906 //==============================================================================
907 TableView* TableBase::getViewP(TableVersion version /* = TableVersion::INVALID */)
908 {
909  try
910  {
911  if(version != TableVersion::INVALID)
912  return &tableViews_.at(version);
913  }
914  catch(...)
915  {
916  __SS__ << "Table '" << tableName_ << "' does not have version v" << version
917  << " in the cache." << __E__;
918  __SS_THROW__;
919  }
920 
921  if(!activeTableView_)
922  {
923  __SS__ << "There is no active table view setup! Please check your system "
924  "configuration."
925  << __E__;
926  __SS_ONLY_THROW__;
927  }
928  return activeTableView_;
929 } //end getViewP()
930 
931 //==============================================================================
932 TableView* TableBase::getMockupViewP(void) { return &mockupTableView_; }
933 
934 //==============================================================================
935 void TableBase::setTableName(const std::string& tableName) { tableName_ = tableName; }
936 
937 //==============================================================================
938 void TableBase::setTableDescription(const std::string& tableDescription)
939 {
940  tableDescription_ = tableDescription;
941 } //end setTableDescription()
942 
943 //==============================================================================
946 void TableBase::deactivate() { activeTableView_ = 0; }
947 
948 //==============================================================================
950 bool TableBase::isActive() { return activeTableView_ ? true : false; }
951 
952 //==============================================================================
953 bool TableBase::setActiveView(TableVersion version)
954 {
955  if(!isStored(version))
956  { // we don't call else load for the user, because the table manager would lose
957  // track.. (I think?)
958  // so load new versions for the first time through the table manager only. (I
959  // think??)
960  __SS__ << "\nsetActiveView() ERROR: View with version " << version
961  << " has never been stored before!" << __E__;
962  __SS_THROW__;
963  return false;
964  }
965  activeTableView_ = &tableViews_.at(version);
966 
967  if(tableViews_.at(version).getVersion() != version)
968  {
969  __SS__ << "Something has gone very wrong with the version handling!" << __E__;
970  __SS_THROW__;
971  }
972 
973  return true;
974 } //end setActiveView()
975 
976 //==============================================================================
985  const TableView& sourceViewA,
986  const TableView& sourceViewB,
987  TableVersion destinationVersion,
988  const std::string& author,
989  const MergeApproach mergeApproach,
990  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
991  std::string /*converted uidB*/>& uidConversionMap,
992  std::map<
993  std::pair<std::string /*original table*/,
994  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>,
995  std::string /*converted gidB*/>& groupidConversionMap,
996  bool fillRecordConversionMaps,
997  bool applyRecordConversionMaps,
998  bool generateUniqueDataColumns /*=false*/,
999  std::stringstream* mergeReport /*=nullptr*/)
1000 {
1001  __COUT__ << "mergeViews starting..." << __E__;
1002 
1003  // clang-format off
1004  // There 3 modes:
1005  // rename -- All records from both groups are maintained, but conflicts from B are renamed.
1006  // Must maintain a map of UIDs that are remapped to new name for
1007  // because linkUID fields must be preserved.
1008  // replace -- Any UID conflicts for a record are replaced by the record from group B.
1009  // skip -- Any UID conflicts for a record are skipped so that group A record remains
1010  // clang-format on
1011 
1012  // check valid mode
1013  if(!(mergeApproach == MergeApproach::RENAME ||
1014  mergeApproach == MergeApproach::REPLACE || mergeApproach == MergeApproach::SKIP))
1015  {
1016  __SS__ << "Error! Invalid merge approach." << __E__;
1017  __SS_THROW__;
1018  }
1019 
1020  // check that column sizes match
1021  if(sourceViewA.getNumberOfColumns() != mockupTableView_.getNumberOfColumns())
1022  {
1023  __SS__ << "Error! Number of Columns of source view A must match destination "
1024  "mock-up view."
1025  << "Dimension of source is [" << sourceViewA.getNumberOfColumns()
1026  << "] and of destination mockup is ["
1027  << mockupTableView_.getNumberOfColumns() << "]." << __E__;
1028  __SS_THROW__;
1029  }
1030  // check that column sizes match
1031  if(sourceViewB.getNumberOfColumns() != mockupTableView_.getNumberOfColumns())
1032  {
1033  __SS__ << "Error! Number of Columns of source view B must match destination "
1034  "mock-up view."
1035  << "Dimension of source is [" << sourceViewB.getNumberOfColumns()
1036  << "] and of destination mockup is ["
1037  << mockupTableView_.getNumberOfColumns() << "]." << __E__;
1038  __SS_THROW__;
1039  }
1040 
1041  // fill conversion map based on merge approach
1042 
1043  if(TTEST(1))
1044  {
1045  {
1046  std::stringstream ss;
1047  sourceViewA.print(ss);
1048  __COUTT__ << "mergeViews() sourceViewA:\n" << ss.str() << __E__;
1049  }
1050  {
1051  std::stringstream ss;
1052  sourceViewB.print(ss);
1053  __COUTT__ << "mergeViews() sourceViewB:\n" << ss.str() << __E__;
1054  }
1055  }
1056 
1057  if(mergeReport)
1058  (*mergeReport) << "\n'"
1059  << (mergeApproach == MergeApproach::RENAME
1060  ? "RENAME"
1061  : (mergeApproach == MergeApproach::REPLACE ? "REPLACE"
1062  : "SKIP"))
1063  << "'-Merging table '" << getTableName() << "' A=v"
1064  << sourceViewA.getVersion() << " with B=v"
1065  << sourceViewB.getVersion() << __E__;
1066 
1067  if(fillRecordConversionMaps && mergeApproach == MergeApproach::RENAME)
1068  {
1069  __COUT__ << "Filling record conversion map." << __E__;
1070 
1071  // rename -- All records from both groups are maintained, but conflicts from
1072  // B are renamed.
1073  // Must maintain a map of UIDs that are remapped to new name for
1074  // groupB, because linkUID fields must be preserved.
1075 
1076  // for each B record
1077  // if there is a conflict, rename
1078 
1079  unsigned int uniqueId;
1080  std::string uniqueIdString, uniqueIdBase;
1081  char indexString[1000];
1082  unsigned int ra;
1083  unsigned int numericStartIndex;
1084  bool found;
1085 
1086  for(unsigned int cb = 0; cb < sourceViewB.getNumberOfColumns(); ++cb)
1087  {
1088  // skip columns that are not UID or GroupID columns
1089  if(!(sourceViewA.getColumnInfo(cb).isUID() ||
1090  sourceViewA.getColumnInfo(cb).isGroupID()))
1091  continue;
1092 
1093  __COUT__ << "Have an ID column: " << cb << " "
1094  << sourceViewA.getColumnInfo(cb).getType() << __E__;
1095 
1096  // at this point we have an ID column, verify B and mockup are the same
1097  if(sourceViewA.getColumnInfo(cb).getType() !=
1098  sourceViewB.getColumnInfo(cb).getType() ||
1099  sourceViewA.getColumnInfo(cb).getType() !=
1100  mockupTableView_.getColumnInfo(cb).getType())
1101  {
1102  __SS__ << "Error! " << sourceViewA.getColumnInfo(cb).getType()
1103  << " column " << cb
1104  << " of source view A must match source B and destination mock-up "
1105  "view."
1106  << " Column of source B is ["
1107  << sourceViewA.getColumnInfo(cb).getType()
1108  << "] and of destination mockup is ["
1109  << mockupTableView_.getColumnInfo(cb).getType() << "]." << __E__;
1110  __SS_THROW__;
1111  }
1112 
1113  // getLinkGroupIDColumn(childLinkIndex)
1114 
1115  std::vector<std::string /*converted uidB*/>
1116  localConvertedIds; // used for conflict completeness check
1117 
1118  if(sourceViewA.getColumnInfo(cb).isGroupID())
1119  {
1120  std::set<std::string> aGroupids = sourceViewA.getSetOfGroupIDs(cb);
1121  std::set<std::string> bGroupids = sourceViewB.getSetOfGroupIDs(cb);
1122 
1123  for(const auto& bGroupid : bGroupids)
1124  {
1125  if(aGroupids.find(bGroupid) == aGroupids.end())
1126  continue;
1127 
1128  // if here, found conflict
1129  __COUT__ << "found conflict: " << getTableName() << "/" << bGroupid
1130  << __E__;
1131 
1132  // extract starting uniqueId number
1133  {
1134  const std::string& str = bGroupid;
1135  numericStartIndex = str.size();
1136 
1137  // find first non-numeric character
1138  while(numericStartIndex - 1 < str.size() &&
1139  str[numericStartIndex - 1] >= '0' &&
1140  str[numericStartIndex - 1] <= '9')
1141  --numericStartIndex;
1142 
1143  if(numericStartIndex < str.size())
1144  {
1145  uniqueId = atoi(str.substr(numericStartIndex).c_str()) + 1;
1146  uniqueIdBase = str.substr(0, numericStartIndex);
1147  }
1148  else
1149  {
1150  uniqueId = 0;
1151  uniqueIdBase = str;
1152  }
1153 
1154  __COUTV__(uniqueIdBase);
1155  __COUTV__(uniqueId);
1156  } // end //extract starting uniqueId number
1157 
1158  // find unique id string
1159  {
1160  sprintf(indexString, "%u", uniqueId);
1161  uniqueIdString = uniqueIdBase + indexString;
1162  __COUTV__(uniqueIdString);
1163 
1164  found = false;
1165  // check converted records and source A and B for conflicts
1166  if(aGroupids.find(uniqueIdString) != aGroupids.end())
1167  found = true;
1168  if(!found && bGroupids.find(uniqueIdString) != bGroupids.end())
1169  found = true;
1170  if(!found && bGroupids.find(uniqueIdString) != bGroupids.end())
1171  found = true;
1172  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
1173  if(localConvertedIds[ra] == uniqueIdString)
1174  found = true;
1175 
1176  while(found) // while conflict, change id
1177  {
1178  ++uniqueId;
1179  sprintf(indexString, "%u", uniqueId);
1180  uniqueIdString = uniqueIdBase + indexString;
1181  __COUTV__(uniqueIdString);
1182 
1183  found = false;
1184  // check converted records and source A and B for conflicts
1185  if(aGroupids.find(uniqueIdString) != aGroupids.end())
1186  found = true;
1187  if(!found &&
1188  bGroupids.find(uniqueIdString) != bGroupids.end())
1189  found = true;
1190  if(!found &&
1191  bGroupids.find(uniqueIdString) != bGroupids.end())
1192  found = true;
1193  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
1194  if(localConvertedIds[ra] == uniqueIdString)
1195  found = true;
1196  }
1197  } // end find unique id string
1198 
1199  // have unique id string now
1200  __COUTV__(uniqueIdString);
1201 
1202  groupidConversionMap
1203  [std::pair<std::string /*original table*/,
1204  std::pair<std::string /*group linkid*/,
1205  std::string /*original gidB*/>>(
1206  getTableName(),
1207  std::pair<std::string /*group linkid*/,
1208  std::string /*original gidB*/>(
1209  sourceViewB.getColumnInfo(cb).getChildLinkIndex(),
1210  bGroupid))] = uniqueIdString;
1211  localConvertedIds.push_back(uniqueIdString); // save to vector for
1212  // future conflict
1213  // checking within table
1214 
1215  if(mergeReport)
1216  (*mergeReport)
1217  << "\t"
1218  << "Found conflicting B groupID for linkIndex '"
1219  << sourceViewB.getColumnInfo(cb).getChildLinkIndex()
1220  << "' and renamed '" << bGroupid << "' to '" << uniqueIdString
1221  << "'" << __E__;
1222 
1223  } // end row find unique id string loop for groupid
1224 
1225  // done creating conversion map
1226  __COUTV__(StringMacros::mapToString(groupidConversionMap));
1227 
1228  } // end group id conversion map fill
1229  else // start uid conversion map fill
1230  {
1231  for(unsigned int rb = 0; rb < sourceViewB.getNumberOfRows(); ++rb)
1232  {
1233  found = false;
1234 
1235  for(ra = 0; ra < sourceViewA.getDataView().size(); ++ra)
1236  if(sourceViewA.getValueAsString(ra, cb) ==
1237  sourceViewB.getValueAsString(rb, cb))
1238  {
1239  found = true;
1240  break;
1241  }
1242 
1243  if(!found)
1244  continue;
1245 
1246  // found conflict
1247  __COUT__ << "found conflict: " << getTableName() << "/"
1248  << sourceViewB.getDataView()[rb][cb] << __E__;
1249 
1250  // extract starting uniqueId number
1251  {
1252  const std::string& str = sourceViewB.getDataView()[rb][cb];
1253  numericStartIndex = str.size();
1254 
1255  // find first non-numeric character
1256  while(numericStartIndex - 1 < str.size() &&
1257  str[numericStartIndex - 1] >= '0' &&
1258  str[numericStartIndex - 1] <= '9')
1259  --numericStartIndex;
1260 
1261  if(numericStartIndex < str.size())
1262  {
1263  uniqueId = atoi(str.substr(numericStartIndex).c_str()) + 1;
1264  uniqueIdBase = str.substr(0, numericStartIndex);
1265  }
1266  else
1267  {
1268  uniqueId = 0;
1269  uniqueIdBase = str;
1270  }
1271 
1272  __COUTV__(uniqueIdBase);
1273  __COUTV__(uniqueId);
1274  } // end //extract starting uniqueId number
1275 
1276  // find unique id string
1277  {
1278  sprintf(indexString, "%u", uniqueId);
1279  uniqueIdString = uniqueIdBase + indexString;
1280  __COUTV__(uniqueIdString);
1281 
1282  found = false;
1283  // check converted records and source A and B for conflicts
1284  for(ra = 0; !found && ra < sourceViewA.getDataView().size(); ++ra)
1285  if(sourceViewA.getValueAsString(ra, cb) == uniqueIdString)
1286  found = true;
1287  for(ra = 0; !found && ra < sourceViewB.getDataView().size(); ++ra)
1288  if(ra == rb)
1289  continue; // skip record in question
1290  else if(sourceViewB.getValueAsString(ra, cb) ==
1291  uniqueIdString)
1292  found = true;
1293  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
1294  if(localConvertedIds[ra] == uniqueIdString)
1295  found = true;
1296 
1297  while(found) // while conflict, change id
1298  {
1299  ++uniqueId;
1300  sprintf(indexString, "%u", uniqueId);
1301  uniqueIdString = uniqueIdBase + indexString;
1302  __COUTV__(uniqueIdString);
1303 
1304  found = false;
1305  // check converted records and source A and B for conflicts
1306  for(ra = 0; !found && ra < sourceViewA.getDataView().size();
1307  ++ra)
1308  if(sourceViewA.getValueAsString(ra, cb) == uniqueIdString)
1309  found = true;
1310  for(ra = 0; !found && ra < sourceViewB.getDataView().size();
1311  ++ra)
1312  if(ra == rb)
1313  continue; // skip record in question
1314  else if(sourceViewB.getValueAsString(ra, cb) ==
1315  uniqueIdString)
1316  found = true;
1317  for(ra = 0; !found && ra < localConvertedIds.size(); ++ra)
1318  if(localConvertedIds[ra] == uniqueIdString)
1319  found = true;
1320  }
1321  } // end find unique id string
1322 
1323  // have unique id string now
1324  __COUTV__(uniqueIdString);
1325 
1326  uidConversionMap[std::pair<std::string /*original table*/,
1327  std::string /*original uidB*/>(
1328  getTableName(), sourceViewB.getValueAsString(rb, cb))] =
1329  uniqueIdString;
1330  localConvertedIds.push_back(uniqueIdString); // save to vector for
1331  // future conflict
1332  // checking within table
1333 
1334  if(mergeReport)
1335  (*mergeReport) << "\t"
1336  << "Found conflicting B UID and renamed '"
1337  << sourceViewB.getValueAsString(rb, cb) << "' to '"
1338  << uniqueIdString << "'" << __E__;
1339  } // end row find unique id string loop
1340 
1341  // done creating conversion map
1342  __COUTV__(StringMacros::mapToString(uidConversionMap));
1343  }
1344 
1345  } // end column find unique id string loop
1346 
1347  } // end rename conversion map create
1348  else
1349  __COUT__ << "Not filling record conversion map." << __E__;
1350 
1351  if(!applyRecordConversionMaps)
1352  {
1353  __COUT__ << "Not applying record conversion map." << __E__;
1354  return TableVersion(); // return invalid
1355  }
1356  else
1357  {
1358  __COUT__ << "Applying record conversion map." << __E__;
1359  __COUTV__(StringMacros::mapToString(uidConversionMap));
1360  __COUTV__(StringMacros::mapToString(groupidConversionMap));
1361  }
1362 
1363  // if destinationVersion is INVALID, creates next available temporary version
1364  destinationVersion = createTemporaryView(TableVersion(), destinationVersion);
1365 
1366  __COUT__ << "Merging from (A) " << sourceViewA.getTableName() << "_v"
1367  << sourceViewA.getVersion() << " and (B) " << sourceViewB.getTableName()
1368  << "_v" << sourceViewB.getVersion() << " to " << getTableName() << "_v"
1369  << destinationVersion << " with approach '"
1370  << (mergeApproach == MergeApproach::RENAME
1371  ? "RENAME"
1372  : (mergeApproach == MergeApproach::REPLACE ? "REPLACE" : "SKIP"))
1373  << "'" << __E__;
1374 
1375  // if the merge fails then delete the destinationVersion view
1376  try
1377  {
1378  // start with a copy of source view A
1379 
1380  tableViews_.emplace(
1381  std::make_pair(destinationVersion, TableView(getTableName())));
1382  TableView* destinationView =
1383  &(tableViews_.at(destinationVersion)
1384  .copy(sourceViewA, destinationVersion, author));
1385 
1386  unsigned int destRow, destSize = destinationView->getDataView().size();
1387  unsigned int cb;
1388  bool found;
1389  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
1390  std::string /*converted uidB*/>::iterator uidConversionIt;
1391  std::map<std::pair<std::string /*original table*/,
1392  std::pair<std::string /*group linkid*/,
1393  std::string /*original gidB*/>>,
1394  std::string /*converted uidB*/>::iterator groupidConversionIt;
1395 
1396  bool linkIsGroup;
1397  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
1398  std::string strb;
1399  size_t stri;
1400 
1401  unsigned int colUID = mockupTableView_.getColUID(); // setup UID column
1402 
1403  // handle merger with conflicts consideration
1404  for(unsigned int rb = 0; rb < sourceViewB.getNumberOfRows(); ++rb)
1405  {
1406  if(mergeApproach == MergeApproach::RENAME)
1407  {
1408  // rename -- All records from both groups are maintained, but
1409  // conflicts from B are renamed. Must maintain a map of
1410  // UIDs that are remapped to new name for groupB,
1411  // because linkUID fields must be preserved.
1412 
1413  // conflict does not matter (because record conversion map is already
1414  // created, always take and append the B record copy row from B to new
1415  // row
1416  destRow = destinationView->copyRows(
1417  author,
1418  sourceViewB,
1419  rb,
1420  1 /*srcRowsToCopy*/,
1421  -1 /*destOffsetRow*/,
1422  generateUniqueDataColumns /*generateUniqueDataColumns*/);
1423 
1424  // check every column and remap conflicting names
1425 
1426  for(cb = 0; cb < sourceViewB.getNumberOfColumns(); ++cb)
1427  {
1428  if(sourceViewB.getColumnInfo(cb).isChildLink())
1429  continue; // skip link columns that have table name
1430  else if(sourceViewB.getColumnInfo(cb).isChildLinkUID())
1431  {
1432  __COUT__ << "Checking UID link... col=" << cb << __E__;
1433  sourceViewB.getChildLink(cb, linkIsGroup, linkPair);
1434 
1435  // if table and uid are in conversion map, convert
1436  if((uidConversionIt = uidConversionMap.find(
1437  std::pair<std::string /*original table*/,
1438  std::string /*original uidB*/>(
1439  sourceViewB.getValueAsString(rb, linkPair.first),
1440  sourceViewB.getValueAsString(
1441  rb, linkPair.second)))) != uidConversionMap.end())
1442  {
1443  __COUT__ << "Found entry to remap: "
1444  << sourceViewB.getDataView()[rb][linkPair.second]
1445  << " ==> " << uidConversionIt->second << __E__;
1446 
1447  if(mergeReport)
1448  (*mergeReport)
1449  << "\t\t"
1450  << "Found entry to remap [r,c]=[" << rb << "," << cb
1451  << "]"
1452  << ": "
1453  << sourceViewB.getDataView()[rb][linkPair.second]
1454  << " ==> [" << destRow << "," << linkPair.second
1455  << uidConversionIt->second << __E__;
1456  destinationView->setValueAsString(
1457  uidConversionIt->second, destRow, linkPair.second);
1458  }
1459  }
1460  else if(sourceViewB.getColumnInfo(cb).isChildLinkGroupID())
1461  {
1462  __COUT__ << "Checking GroupID link... col=" << cb << __E__;
1463  sourceViewB.getChildLink(cb, linkIsGroup, linkPair);
1464 
1465  // if table and uid are in conversion map, convert
1466  if((groupidConversionIt = groupidConversionMap.find(
1467  std::pair<std::string /*original table*/,
1468  std::pair<std::string /*group linkid*/,
1469  std::string /*original gidB*/>>(
1470  sourceViewB.getValueAsString(rb, linkPair.first),
1471  std::pair<std::string /*group linkid*/,
1472  std::string /*original gidB*/>(
1473  sourceViewB.getColumnInfo(cb).getChildLinkIndex(),
1474  sourceViewB.getValueAsString(
1475  rb, linkPair.second))))) !=
1476  groupidConversionMap.end())
1477  {
1478  __COUT__ << "Found entry to remap: "
1479  << sourceViewB.getDataView()[rb][linkPair.second]
1480  << " ==> " << groupidConversionIt->second << __E__;
1481 
1482  if(mergeReport)
1483  (*mergeReport)
1484  << "\t\t"
1485  << "Found entry to remap [r,c]=[" << rb << "," << cb
1486  << "]"
1487  << ": "
1488  << sourceViewB.getDataView()[rb][linkPair.second]
1489  << " ==> [" << destRow << "," << linkPair.second
1490  << "] " << groupidConversionIt->second << __E__;
1491  destinationView->setValueAsString(
1492  groupidConversionIt->second, destRow, linkPair.second);
1493  }
1494  }
1495  else if(sourceViewB.getColumnInfo(cb).isUID())
1496  {
1497  __COUT__ << "Checking UID... col=" << cb << __E__;
1498  if((uidConversionIt = uidConversionMap.find(
1499  std::pair<std::string /*original table*/,
1500  std::string /*original uidB*/>(
1501  getTableName(),
1502  sourceViewB.getValueAsString(rb, cb)))) !=
1503  uidConversionMap.end())
1504  {
1505  __COUT__ << "Found entry to remap: "
1506  << sourceViewB.getDataView()[rb][cb] << " ==> "
1507  << uidConversionIt->second << __E__;
1508 
1509  if(mergeReport)
1510  (*mergeReport)
1511  << "\t\t"
1512  << "Found entry to remap [r,c]=[" << rb << "," << cb
1513  << "]"
1514  << ": " << sourceViewB.getDataView()[rb][cb]
1515  << " ==> [" << destRow << "," << cb << "] "
1516  << uidConversionIt->second << __E__;
1517  destinationView->setValueAsString(
1518  uidConversionIt->second, destRow, cb);
1519  }
1520  }
1521  else if(sourceViewB.getColumnInfo(cb).isGroupID())
1522  {
1523  __COUT__ << "Checking GroupID... col=" << cb << __E__;
1524  if((groupidConversionIt = groupidConversionMap.find(
1525  std::pair<std::string /*original table*/,
1526  std::pair<std::string /*group linkid*/,
1527  std::string /*original gidB*/>>(
1528  getTableName(),
1529  std::pair<std::string /*group linkid*/,
1530  std::string /*original gidB*/>(
1531  sourceViewB.getColumnInfo(cb).getChildLinkIndex(),
1532  sourceViewB.getValueAsString(rb, cb))))) !=
1533  groupidConversionMap.end())
1534  {
1535  __COUT__ << "Found entry to remap: "
1536  << sourceViewB.getDataView()[rb][cb] << " ==> "
1537  << groupidConversionIt->second << __E__;
1538 
1539  if(mergeReport)
1540  (*mergeReport)
1541  << "\t\t"
1542  << "Found entry to remap [r,c]=[" << rb << "," << cb
1543  << "]" << sourceViewB.getDataView()[rb][cb]
1544  << " ==> [" << destRow << "," << cb << "] "
1545  << groupidConversionIt->second << __E__;
1546  destinationView->setValueAsString(
1547  groupidConversionIt->second, destRow, cb);
1548  }
1549  }
1550  else
1551  {
1552  // look for text link to a Table/UID in the map
1553  strb = sourceViewB.getValueAsString(rb, cb);
1554  if(strb.size() > getTableName().size() + 2 && strb[0] == '/')
1555  {
1556  // check for linked name
1557  __COUT__ << "Checking col" << cb << " " << strb << __E__;
1558 
1559  // see if there is an entry in p
1560  for(const auto& mapPairToPair : uidConversionMap)
1561  {
1562  if((stri = strb.find(mapPairToPair.first.first + "/" +
1563  mapPairToPair.first.second)) !=
1564  std::string::npos)
1565  {
1566  __COUT__ << "Found a text link match (stri=" << stri
1567  << ")! "
1568  << (mapPairToPair.first.first + "/" +
1569  mapPairToPair.first.second)
1570  << " ==> " << mapPairToPair.second << __E__;
1571 
1572  // insert mapped substitution into string
1573  destinationView->setValueAsString(
1574  strb.substr(0, stri) +
1575  (mapPairToPair.first.first + "/" +
1576  mapPairToPair.first.second) +
1577  strb.substr(stri +
1578  (mapPairToPair.first.first + "/" +
1579  mapPairToPair.first.second)
1580  .size()),
1581  destRow,
1582  cb);
1583 
1584  __COUT__
1585  << "Found entry to remap: "
1586  << sourceViewB.getDataView()[rb][cb] << " ==> "
1587  << destinationView->getDataView()[destRow][cb]
1588  << __E__;
1589 
1590  if(mergeReport)
1591  (*mergeReport)
1592  << "\t\t"
1593  << "Found entry to remap [r,c]=[" << rb << ","
1594  << cb << "] "
1595  << sourceViewB.getDataView()[rb][cb]
1596  << " ==> [" << destRow << "," << cb << "] "
1597  << destinationView->getDataView()[destRow][cb]
1598  << __E__;
1599  break;
1600  }
1601  } // end uid conversion map loop
1602  }
1603  }
1604  } // end column loop over B record
1605 
1606  continue;
1607  } // end rename, no-conflict handling
1608 
1609  // if here, then not doing rename, so conflicts matter
1610 
1611  found = false;
1612 
1613  for(destRow = 0; destRow < destSize; ++destRow)
1614  if(destinationView->getValueAsString(destRow, colUID) ==
1615  sourceViewB.getValueAsString(rb, colUID))
1616  {
1617  found = true;
1618  break;
1619  }
1620  if(!found) // no conflict
1621  {
1622  __COUT__ << "No "
1623  << (mergeApproach == MergeApproach::RENAME
1624  ? "RENAME"
1625  : (mergeApproach == MergeApproach::REPLACE ? "REPLACE"
1626  : "SKIP"))
1627  << " conflict: " << __E__;
1628 
1629  if(mergeApproach == MergeApproach::REPLACE ||
1630  mergeApproach == MergeApproach::SKIP)
1631  {
1632  // no conflict so append the B record
1633  // copy row from B to new row
1634  destinationView->copyRows(
1635  author, sourceViewB, rb, 1 /*srcRowsToCopy*/);
1636  }
1637  else
1638 
1639  continue;
1640  } // end no-conflict handling
1641 
1642  // if here, then there was a conflict
1643 
1644  __COUT__ << "found "
1645  << (mergeApproach == MergeApproach::RENAME
1646  ? "RENAME"
1647  : (mergeApproach == MergeApproach::REPLACE ? "REPLACE"
1648  : "SKIP"))
1649  << " conflict: " << sourceViewB.getDataView()[rb][colUID] << __E__;
1650 
1651  if(mergeApproach == MergeApproach::REPLACE)
1652  {
1653  if(mergeReport)
1654  (*mergeReport)
1655  << "\t\t"
1656  << "Found UID conflict, replacing A with B record row=" << rb
1657  << " " << sourceViewB.getDataView()[rb][colUID] << __E__;
1658  // replace -- Any UID conflicts for a record are replaced by the
1659  // record from group B.
1660 
1661  // delete row in destination
1662  destinationView->deleteRow(destRow--); // delete row and back up pointer
1663  --destSize;
1664 
1665  // append the B record now
1666  // copy row from B to new row
1667  destinationView->copyRows(author, sourceViewB, rb, 1 /*srcRowsToCopy*/);
1668  }
1669  else if(mergeApproach ==
1670  MergeApproach::SKIP) // then do nothing with conflicting B record
1671  {
1672  if(mergeReport)
1673  (*mergeReport)
1674  << "\t\t"
1675  << "Found UID conflict, skipping B record row=" << rb << " "
1676  << sourceViewB.getDataView()[rb][colUID] << __E__;
1677  }
1678  }
1679 
1680  if(TTEST(1))
1681  {
1682  std::stringstream ss;
1683  destinationView->print(ss);
1684  __COUTT__ << "mergeViews() destinationView:\n" << ss.str() << __E__;
1685  }
1686  }
1687  catch(...) // if the copy fails then delete the destinationVersion view
1688  {
1689  __COUT_ERR__ << "Failed to merge " << sourceViewA.getTableName() << "_v"
1690  << sourceViewA.getVersion() << " and " << sourceViewB.getTableName()
1691  << "_v" << sourceViewB.getVersion() << " into " << getTableName()
1692  << "_v" << destinationVersion << __E__;
1693  __COUT_WARN__ << "Deleting the failed destination version " << destinationVersion
1694  << __E__;
1695  eraseView(destinationVersion);
1696  throw; // and rethrow
1697  }
1698 
1699  return destinationVersion;
1700 } // end mergeViews
1701 
1702 //==============================================================================
1711  TableVersion destinationVersion,
1712  const std::string& author,
1713  bool looseColumnMatching /* = false */)
1714 {
1715  // check that column sizes match
1716  if(!looseColumnMatching &&
1717  sourceView.getNumberOfColumns() != mockupTableView_.getNumberOfColumns())
1718  {
1719  __SS__ << "Error! Number of Columns of source view must match destination "
1720  "mock-up view."
1721  << "Dimension of source is [" << sourceView.getNumberOfColumns()
1722  << "] and of destination mockup is ["
1723  << mockupTableView_.getNumberOfColumns() << "]." << __E__;
1724  __SS_THROW__;
1725  }
1726 
1727  // check for destination version confict
1728  if(!destinationVersion.isInvalid() &&
1729  tableViews_.find(destinationVersion) != tableViews_.end())
1730  {
1731  __SS__ << "Error! Asked to copy a view with a conflicting version: "
1732  << destinationVersion << __E__;
1733  __SS_THROW__;
1734  }
1735 
1736  // if destinationVersion is INVALID, creates next available temporary version
1737  destinationVersion = createTemporaryView(TableVersion(), destinationVersion);
1738 
1739  __COUT__ << "Copying from " << sourceView.getTableName() << "_v"
1740  << sourceView.getVersion() << " to " << getTableName() << "_v"
1741  << destinationVersion << __E__;
1742 
1743  try
1744  {
1745  tableViews_.emplace(std::make_pair(destinationVersion, TableView(tableName_)));
1746  tableViews_.at(destinationVersion).copy(sourceView, destinationVersion, author);
1747  }
1748  catch(...) // if the copy fails then delete the destinationVersion view
1749  {
1750  __COUT_ERR__ << "Failed to copy from " << sourceView.getTableName() << "_v"
1751  << sourceView.getVersion() << " to " << getTableName() << "_v"
1752  << destinationVersion << __E__;
1753  __COUT_WARN__ << "Deleting the failed destination version " << destinationVersion
1754  << __E__;
1755  eraseView(destinationVersion);
1756  throw; // and rethrow
1757  }
1758 
1759  return destinationVersion;
1760 } // end copyView()
1761 
1762 //==============================================================================
1770  TableVersion destTemporaryViewVersion)
1771 {
1772  __COUTT__ << "Table: " << getTableName() << __E__
1773  << "Num of Views: " << tableViews_.size()
1774  << " (Temporary Views: " << (tableViews_.size() - getNumberOfStoredViews())
1775  << ")" << __E__;
1776 
1777  TableVersion tmpVersion = destTemporaryViewVersion;
1778  if(tmpVersion.isInvalid())
1780  while(isStored(tmpVersion) && // find a new valid temporary version
1781  !(tmpVersion = TableVersion::getNextTemporaryVersion(tmpVersion)).isInvalid())
1782  ;
1783  if(isStored(tmpVersion) || tmpVersion.isInvalid())
1784  {
1785  __SS__ << "Invalid destination temporary version: " << destTemporaryViewVersion
1786  << ". Expected next temporary version < " << tmpVersion << __E__;
1787  __SS_THROW__;
1788  }
1789 
1790  if(sourceViewVersion ==
1791  TableVersion::INVALID || // use mockup if sourceVersion is -1 or not found
1792  tableViews_.find(sourceViewVersion) == tableViews_.end())
1793  {
1794  if(sourceViewVersion != -1)
1795  {
1796  __SS__ << "ERROR: sourceViewVersion " << sourceViewVersion << " not found. "
1797  << "Invalid source version. Version requested is not stored (yet?) or "
1798  "does not exist."
1799  << __E__;
1800  __SS_THROW__;
1801  }
1802  __COUTT__ << "Using Mock-up view" << __E__;
1803  tableViews_.emplace(std::make_pair(tmpVersion, TableView(tableName_)));
1804  tableViews_.at(tmpVersion)
1805  .copy(mockupTableView_, tmpVersion, mockupTableView_.getAuthor());
1806  }
1807  else
1808  {
1809  try // do not allow init to throw an exception here..
1810  { // it's ok to copy invalid data, the user may be trying to change it
1811  tableViews_.emplace(std::make_pair(tmpVersion, TableView(tableName_)));
1812  tableViews_.at(tmpVersion)
1813  .copy(tableViews_.at(sourceViewVersion),
1814  tmpVersion,
1815  tableViews_.at(sourceViewVersion).getAuthor());
1816  }
1817  catch(...)
1818  {
1819  __COUT_WARN__
1820  << "createTemporaryView() Source view failed init(). "
1821  << "This is being ignored (hopefully the new copy is being fixed)."
1822  << __E__;
1823  }
1824  }
1825 
1826  return tmpVersion;
1827 } // end createTemporaryView()
1828 
1829 //==============================================================================
1834 {
1835  TableVersion tmpVersion;
1836 
1837  // std::map guarantees versions are in increasing order!
1838  if(tableViews_.size() != 0 && tableViews_.begin()->first.isTemporaryVersion())
1839  tmpVersion = TableVersion::getNextTemporaryVersion(tableViews_.begin()->first);
1840  else
1842 
1843  // verify tmpVersion is ok
1844  if(isStored(tmpVersion) || tmpVersion.isInvalid() || !tmpVersion.isTemporaryVersion())
1845  {
1846  __SS__ << "Invalid destination temporary version: " << tmpVersion << __E__;
1847  __SS_THROW__;
1848  }
1849  return tmpVersion;
1850 } //end getNextTemporaryVersion()
1851 
1852 //==============================================================================
1857 {
1858  TableVersion tmpVersion;
1859 
1860  // std::map guarantees versions are in increasing order!
1861  if(tableViews_.size() != 0 && !tableViews_.rbegin()->first.isTemporaryVersion())
1862  tmpVersion = TableVersion::getNextVersion(tableViews_.rbegin()->first);
1863  else
1864  tmpVersion = TableVersion::getNextVersion();
1865 
1866  // verify tmpVersion is ok
1867  if(isStored(tmpVersion) || tmpVersion.isInvalid() || tmpVersion.isTemporaryVersion())
1868  {
1869  __SS__ << "Invalid destination next version: " << tmpVersion << __E__;
1870  __SS_THROW__;
1871  }
1872  return tmpVersion;
1873 } //end getNextVersion()
1874 
1875 //==============================================================================
1881 {
1882  if(!temporaryVersion.isTemporaryVersion() || !isStored(temporaryVersion))
1883  {
1884  __SS__ << getTableName() << ":: Error! Temporary version not found!" << __E__;
1885  __SS_THROW__;
1886  }
1887  return &tableViews_.at(temporaryVersion);
1888 } //end getTemporaryView()
1889 
1890 //==============================================================================
1894 std::string TableBase::convertToCaps(std::string& str, bool isTableName)
1895 {
1896  // append Table to be nice to user
1897  unsigned int tablePos = (unsigned int)std::string::npos;
1898  if(isTableName &&
1899  ((tablePos = str.find("Table")) != str.size() - strlen("Table") ||
1900  tablePos ==
1901  (unsigned int)std::string::npos)) //avoid case when tableName is length 4
1902  str += "Table";
1903 
1904  // create all caps name and validate
1905  // only allow alpha names with Table at end
1906  std::string capsStr = "";
1907  for(unsigned int c = 0; c < str.size(); ++c)
1908  if(str[c] >= 'A' && str[c] <= 'Z')
1909  {
1910  // add _ before table and if lower case to uppercase
1911  if(c == tablePos ||
1912  (c && str[c - 1] >= 'a' &&
1913  str[c - 1] <= 'z') || // if this is a new start of upper case
1914  (c && str[c - 1] >= 'A' &&
1915  str[c - 1] <= 'Z' && // if this is a new start from running caps
1916  c + 1 < str.size() && str[c + 1] >= 'a' && str[c + 1] <= 'z') ||
1917  (c && str[c - 1] >= '0' &&
1918  str[c - 1] <= '9' && // if this is a new start from numbers
1919  c + 1 < str.size() && str[c + 1] >= 'a' && str[c + 1] <= 'z'))
1920  capsStr += "_";
1921  capsStr += str[c];
1922  }
1923  else if(str[c] >= 'a' && str[c] <= 'z')
1924  capsStr += char(str[c] - 32); // capitalize
1925  else if(str[c] >= '0' && str[c] <= '9')
1926  capsStr += str[c]; // allow numbers
1927  else // error! non-alpha
1928  {
1929  //allow underscores for group cache document name
1930  if((str.substr(0, TableBase::GROUP_CACHE_PREPEND.length()) ==
1931  TableBase::GROUP_CACHE_PREPEND ||
1932  str.substr(0, TableBase::JSON_DOC_PREPEND.length()) ==
1933  TableBase::JSON_DOC_PREPEND) &&
1934  str[c] == '_')
1935  {
1936  capsStr += '-';
1937  continue;
1938  }
1939 
1940  std::stringstream ss;
1941  ss << __COUT_HDR_FL__
1942  << "TableBase::convertToCaps: Invalid character found in name (allowed: "
1943  "A-Z, a-z, 0-9) '"
1944  << str << "'" << __E__;
1945  TLOG(TLVL_ERROR) << ss.str();
1946  __SS_ONLY_THROW__;
1947  }
1948 
1949  return capsStr;
1950 } //end convertToCaps()
const std::string & getTableName(void) const
Getters.
Definition: TableBase.cc:814
std::map< TableVersion, TableView > tableViews_
Definition: TableBase.h:133
TableVersion createTemporaryView(TableVersion sourceViewVersion=TableVersion(), TableVersion destTemporaryViewVersion=TableVersion::getNextTemporaryVersion())
source of -1, from MockUp, else from valid view version
Definition: TableBase.cc:1769
void setupMockupView(TableVersion version)
Definition: TableBase.cc:292
bool diffTwoVersions(TableVersion v1, TableVersion v2, std::stringstream *diffReport=0, std::map< std::string, std::vector< std::string >> *v1ModifiedRecords=0) const
Definition: TableBase.cc:542
MergeApproach
Merge approach enumeration for mergeViews function.
Definition: TableBase.h:24
TableBase(bool specialTable, const std::string &specialTableName)
Definition: TableBase.cc:140
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
TableVersion mergeViews(const TableView &sourceViewA, const TableView &sourceViewB, TableVersion destinationVersion, const std::string &author, MergeApproach mergeApproach, std::map< std::pair< std::string, std::string >, std::string > &uidConversionMap, std::map< std::pair< std::string, std::pair< std::string, std::string > >, std::string > &groupidConversionMap, bool fillRecordConversionMaps, bool applyRecordConversionMaps, bool generateUniqueDataColumns=false, std::stringstream *mergeRepoert=nullptr)
Definition: TableBase.cc:984
static std::string convertToCaps(std::string &str, bool isConfigName=false)
Definition: TableBase.cc:1894
TableView * getTemporaryView(TableVersion temporaryVersion)
Definition: TableBase.cc:1880
const unsigned int MAX_VIEWS_IN_CACHE
Definition: TableBase.h:30
TableVersion getNextVersion(void) const
Definition: TableBase.cc:1856
const TableVersion & getViewVersion(void) const
always the active one
Definition: TableBase.cc:823
TableVersion copyView(const TableView &sourceView, TableVersion destinationVersion, const std::string &author, bool looseColumnMatching=false)
Definition: TableBase.cc:1710
bool latestAndMockupColumnNumberMismatch(void) const
Definition: TableBase.cc:831
void setTableName(const std::string &tableName)
Setters.
Definition: TableBase.cc:935
virtual ~TableBase(void)
Definition: TableBase.cc:254
void print(std::ostream &out=std::cout) const
always prints active view
Definition: TableBase.cc:277
void deactivate(void)
Definition: TableBase.cc:946
void specialMetaTableConstructor(void)
Methods.
Definition: TableBase.cc:172
TableVersion getNextTemporaryVersion(void) const
Definition: TableBase.cc:1833
void trimCache(unsigned int trimSize=-1)
Definition: TableBase.cc:322
bool isInvalid(void) const
isInvalid
static TableVersion getNextVersion(const TableVersion &version=TableVersion())
bool isTemporaryVersion(void) const
static TableVersion getNextTemporaryVersion(const TableVersion &version=TableVersion())
static const std::string DATATYPE_NUMBER
std::string getChildLinkIndex(void) const
getChildLinkIndex
bool isUID(void) const
isUID
static const std::string TYPE_UID
NOTE: Do NOT put '-' in static const TYPEs because it will mess up javascript handling in the web gui...
bool isChildLinkGroupID(void) const
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1081
void deleteRow(int r)
Definition: TableView.cc:3564
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
Definition: TableView.cc:3594
unsigned int copyRows(const std::string &author, const TableView &src, unsigned int srcOffsetRow=0, unsigned int srcRowsToCopy=(unsigned int) -1, unsigned int destOffsetRow=(unsigned int) -1, unsigned char generateUniqueDataColumns=false, const std::string &baseNameAutoUID="")
Definition: TableView.cc:126
void init(void)
Definition: TableView.cc:195
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
Definition: TableView.cc:966
std::set< std::string > getSetOfGroupIDs(const std::string &childLinkIndex, unsigned int row=-1) const
Definition: TableView.cc:1746
unsigned int getColUID(void) const
Definition: TableView.cc:1313
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:3479
defines used also by OtsConfigurationWizardSupervisor
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static std::string stackTrace(void)