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