$treeview $search $mathjax $extrastylesheet
otsdaq
v2_03_00
$projectbrief
|
$projectbrief
|
$searchbox |
00001 #include "otsdaq-core/TableCore/TableBase.h" 00002 00003 #include <iostream> // std::cout 00004 #include <typeinfo> 00005 00006 #include "otsdaq-core/TableCore/TableInfoReader.h" 00007 00008 using namespace ots; 00009 00010 #undef __MF_SUBJECT__ 00011 #define __MF_SUBJECT__ "TableBase-" + getTableName() 00012 00013 //============================================================================== 00014 // TableBase 00015 // If a valid string pointer is passed in accumulatedExceptions 00016 // then allowIllegalColumns is set for InfoReader 00017 // If accumulatedExceptions pointer = 0, then illegal columns throw std::runtime_error 00018 // exception 00019 TableBase::TableBase(std::string tableName, 00020 std::string* accumulatedExceptions) 00021 : MAX_VIEWS_IN_CACHE(20) // This is done, so that inheriting table classes could have 00022 // varying amounts of cache 00023 , tableName_(tableName) 00024 , activeTableView_(0) 00025 { 00026 // info reader fills up the mockup view 00027 TableInfoReader tableInfoReader(accumulatedExceptions); 00028 try // to read info 00029 { 00030 std::string returnedExceptions = tableInfoReader.read(this); 00031 00032 if(returnedExceptions != "") 00033 __COUT_ERR__ << returnedExceptions << __E__; 00034 00035 if(accumulatedExceptions) 00036 *accumulatedExceptions += std::string("\n") + returnedExceptions; 00037 } 00038 catch(...) // if accumulating exceptions, continue to and return, else throw 00039 { 00040 __SS__ << "Failure in tableInfoReader.read(this). " 00041 << "Perhaps you need to run otsdaq_convert_config_to_table ?" << __E__; 00042 __COUT_ERR__ << "\n" << ss.str(); 00043 if(accumulatedExceptions) 00044 *accumulatedExceptions += std::string("\n") + ss.str(); 00045 else 00046 throw; 00047 return; // do not proceed with mockup check if this failed 00048 } 00049 00050 // call init on mockup view to verify columns 00051 try 00052 { 00053 getMockupViewP()->init(); 00054 } 00055 catch(std::runtime_error& 00056 e) // if accumulating exceptions, continue to and return, else throw 00057 { 00058 if(accumulatedExceptions) 00059 *accumulatedExceptions += std::string("\n") + e.what(); 00060 else 00061 throw; 00062 } 00063 } 00064 //============================================================================== 00065 // TableBase 00066 // Default constructor is only used to create special tables 00067 // not based on an ...Info.xml file 00068 // e.g. the TableGroupMetadata table in ConfigurationManager 00069 TableBase::TableBase(void) 00070 : MAX_VIEWS_IN_CACHE(1) // This is done, so that inheriting table classes could have 00071 // varying amounts of cache 00072 , tableName_("") 00073 , activeTableView_(0) 00074 { 00075 } 00076 00077 //============================================================================== 00078 TableBase::~TableBase(void) {} 00079 00080 //============================================================================== 00081 std::string TableBase::getTypeId() { return typeid(this).name(); } 00082 00083 //============================================================================== 00084 void TableBase::init(ConfigurationManager* tableManager) 00085 { 00086 //__COUT__ << "Default TableBase::init() called." << __E__; 00087 } 00088 00089 //============================================================================== 00090 void TableBase::reset(bool keepTemporaryVersions) 00091 { 00092 // std::cout << __COUT_HDR_FL__ << "resetting" << __E__; 00093 deactivate(); 00094 if(keepTemporaryVersions) 00095 trimCache(0); 00096 else // clear all 00097 tableViews_.clear(); 00098 } 00099 00100 //============================================================================== 00101 void TableBase::print(std::ostream& out) const 00102 { 00103 // std::cout << __COUT_HDR_FL__ << "activeVersion_ " << activeVersion_ << " 00104 // (INVALID_VERSION:=" << INVALID_VERSION << ")" << __E__; 00105 if(!activeTableView_) 00106 { 00107 __COUT_ERR__ << "ERROR: No active view set" << __E__; 00108 return; 00109 } 00110 activeTableView_->print(out); 00111 } 00112 00113 //============================================================================== 00114 // makes active version the specified table view version 00115 // if the version is not already stored, then creates a mockup version 00116 void TableBase::setupMockupView(TableVersion version) 00117 { 00118 if(!isStored(version)) 00119 { 00120 tableViews_[version].copy( 00121 mockupTableView_, version, mockupTableView_.getAuthor()); 00122 trimCache(); 00123 if(!isStored(version)) // the trim cache is misbehaving! 00124 { 00125 __SS__ << "\nsetupMockupView() IMPOSSIBLE ERROR: trimCache() is deleting the " 00126 "latest view version " 00127 << version << "!" << __E__; 00128 __COUT_ERR__ << "\n" << ss.str(); 00129 __SS_THROW__; 00130 } 00131 } 00132 else 00133 { 00134 __SS__ << "\nsetupMockupView() ERROR: View to fill with mockup already exists: " 00135 << version << ". Cannot overwrite!" << __E__; 00136 __COUT_ERR__ << "\n" << ss.str(); 00137 __SS_THROW__; 00138 } 00139 } 00140 00141 //============================================================================== 00142 // trimCache 00143 // if there are more views than MAX_VIEWS_IN_CACHE, erase them. 00144 // choose wisely the view to delete 00145 // (by access time) 00146 void TableBase::trimCache(unsigned int trimSize) 00147 { 00148 // delete cached views, if necessary 00149 00150 if(trimSize == (unsigned int)-1) // if -1, use MAX_VIEWS_IN_CACHE 00151 trimSize = MAX_VIEWS_IN_CACHE; 00152 00153 int i = 0; 00154 while(getNumberOfStoredViews() > trimSize) 00155 { 00156 TableVersion versionToDelete; 00157 time_t stalestTime = -1; 00158 00159 for(auto& viewPair : tableViews_) 00160 if(!viewPair.first.isTemporaryVersion()) 00161 { 00162 if(stalestTime == -1 || viewPair.second.getLastAccessTime() < stalestTime) 00163 { 00164 versionToDelete = viewPair.first; 00165 stalestTime = viewPair.second.getLastAccessTime(); 00166 if(!trimSize) 00167 break; // if trimSize is 0, then just take first found 00168 } 00169 } 00170 00171 if(versionToDelete.isInvalid()) 00172 { 00173 __SS__ << "Can NOT have a stored view with an invalid version!"; 00174 __SS_THROW__; 00175 } 00176 00177 eraseView(versionToDelete); 00178 } 00179 } 00180 00181 //============================================================================== 00182 // trimCache 00183 // if there are more views than MAX_VIEWS_IN_CACHE, erase them. 00184 // choose wisely the view to delete 00185 // (by access time) 00186 void TableBase::trimTemporary(TableVersion targetVersion) 00187 { 00188 if(targetVersion.isInvalid()) // erase all temporary 00189 { 00190 for(auto it = tableViews_.begin(); it != tableViews_.end(); /*no increment*/) 00191 { 00192 if(it->first.isTemporaryVersion()) 00193 { 00194 __COUT__ << "Trimming temporary version: " << it->first << __E__; 00195 if(activeTableView_ && 00196 getViewVersion() == it->first) // if activeVersion is being erased! 00197 deactivate(); // deactivate active view, instead of guessing at next 00198 // active view 00199 tableViews_.erase(it++); 00200 } 00201 else 00202 ++it; 00203 } 00204 } 00205 else if(targetVersion.isTemporaryVersion()) // erase target 00206 { 00207 __COUT__ << "Trimming temporary version: " << targetVersion << __E__; 00208 eraseView(targetVersion); 00209 } 00210 else 00211 { 00212 // else this is a persistent version! 00213 __SS__ << "Temporary trim target was a persistent version: " << targetVersion 00214 << __E__; 00215 __COUT_ERR__ << "\n" << ss.str(); 00216 __SS_THROW__; 00217 } 00218 } 00219 00220 //============================================================================== 00221 // checkForDuplicate 00222 // look for a duplicate of the needleVersion in the haystack 00223 // which is the cached views in tableViews_ 00224 // 00225 // Note: ignoreVersion is useful if you know another view is already identical 00226 // like when converting from temporary to persistent 00227 // 00228 // Return invalid if no matches 00229 TableVersion TableBase::checkForDuplicate(TableVersion needleVersion, 00230 TableVersion ignoreVersion) const 00231 { 00232 auto needleIt = tableViews_.find(needleVersion); 00233 if(needleIt == tableViews_.end()) 00234 { 00235 // else this is a persistent version! 00236 __SS__ << "needleVersion does not exist: " << needleVersion << __E__; 00237 __COUT_ERR__ << "\n" << ss.str(); 00238 __SS_THROW__; 00239 } 00240 00241 const TableView* needleView = &(needleIt->second); 00242 unsigned int rows = needleView->getNumberOfRows(); 00243 unsigned int cols = needleView->getNumberOfColumns(); 00244 00245 bool match; 00246 unsigned int potentialMatchCount = 0; 00247 00248 // needleView->print(); 00249 00250 // for each table in cache 00251 // check each row,col 00252 auto viewPairReverseIterator = tableViews_.rbegin(); 00253 for(; viewPairReverseIterator != tableViews_.rend(); ++viewPairReverseIterator) 00254 { 00255 if(viewPairReverseIterator->first == needleVersion) 00256 continue; // skip needle version 00257 if(viewPairReverseIterator->first == ignoreVersion) 00258 continue; // skip ignore version 00259 if(viewPairReverseIterator->first.isTemporaryVersion()) 00260 continue; // skip temporary versions 00261 00262 if(viewPairReverseIterator->second.getNumberOfRows() != rows) 00263 continue; // row mismatch 00264 00265 if(viewPairReverseIterator->second.getDataColumnSize() != cols || 00266 viewPairReverseIterator->second.getSourceColumnMismatch() != 0) 00267 continue; // col mismatch 00268 00269 ++potentialMatchCount; 00270 __COUT__ << "Checking version... " << viewPairReverseIterator->first << __E__; 00271 00272 // viewPairReverseIterator->second.print(); 00273 00274 // match = true; 00275 00276 // if column source names do not match then skip 00277 // source names are potentially different from 00278 // getColumnsInfo()/getColumnStorageNames 00279 00280 match = viewPairReverseIterator->second.getSourceColumnNames().size() == 00281 needleView->getSourceColumnNames().size(); 00282 if(match) 00283 { 00284 for(auto& haystackColName : 00285 viewPairReverseIterator->second.getSourceColumnNames()) 00286 if(needleView->getSourceColumnNames().find(haystackColName) == 00287 needleView->getSourceColumnNames().end()) 00288 { 00289 __COUT__ << "Found column name mismach for '" << haystackColName 00290 << "'... So allowing same data!" << __E__; 00291 00292 match = false; 00293 break; 00294 } 00295 } 00296 00297 // checking columnsInfo seems to be wrong approach, use getSourceColumnNames 00298 // (above) auto viewColInfoIt = 00299 // viewPairReverseIterator->second.getColumnsInfo().begin(); for(unsigned 00300 // int col=0; match && //note column size must already match 00301 // viewPairReverseIterator->second.getColumnsInfo().size() > 3 && 00302 // col<viewPairReverseIterator->second.getColumnsInfo().size()-3;++col,viewColInfoIt++) 00303 // if(viewColInfoIt->getName() != 00304 // needleView->getColumnsInfo()[col].getName()) 00305 // { 00306 // match = false; 00310 // } 00311 00312 for(unsigned int row = 0; match && row < rows; ++row) 00313 { 00314 for(unsigned int col = 0; col < cols - 2; 00315 ++col) // do not consider author and timestamp 00316 if(viewPairReverseIterator->second.getDataView()[row][col] != 00317 needleView->getDataView()[row][col]) 00318 { 00319 match = false; 00320 00321 // __COUT__ << "Value name mismatch " << col << ":" 00322 //<< 00323 // viewPairReverseIterator->second.getDataView()[row][col] 00324 //<< "[" << 00325 // viewPairReverseIterator->second.getDataView()[row][col].size() 00326 //<< "]" << " vs " << 00327 // needleView->getDataView()[row][col] << "[" 00328 //<< 00329 // needleView->getDataView()[row][col].size() 00330 //<< 00331 //"]" 00332 //<< 00333 // __E__; 00334 00335 break; 00336 } 00337 } 00338 if(match) 00339 { 00340 __COUT_INFO__ << "Duplicate version found: " << viewPairReverseIterator->first 00341 << __E__; 00342 return viewPairReverseIterator->first; 00343 } 00344 } 00345 00346 __COUT__ << "No duplicates found in " << potentialMatchCount << " potential matches." 00347 << __E__; 00348 return TableVersion(); // return invalid if no matches 00349 } 00350 00351 //============================================================================== 00352 void TableBase::changeVersionAndActivateView(TableVersion temporaryVersion, 00353 TableVersion version) 00354 { 00355 if(tableViews_.find(temporaryVersion) == tableViews_.end()) 00356 { 00357 __SS__ << "ERROR: Temporary view version " << temporaryVersion 00358 << " doesn't exists!" << __E__; 00359 __COUT_ERR__ << "\n" << ss.str(); 00360 __SS_THROW__; 00361 } 00362 if(version.isInvalid()) 00363 { 00364 __SS__ << "ERROR: Attempting to create an invalid version " << version 00365 << "! Did you really run out of versions? (this should never happen)" 00366 << __E__; 00367 __COUT_ERR__ << "\n" << ss.str(); 00368 __SS_THROW__; 00369 } 00370 00371 if(tableViews_.find(version) != tableViews_.end()) 00372 __COUT_WARN__ << "WARNING: View version " << version 00373 << " already exists! Overwriting." << __E__; 00374 00375 tableViews_[version].copy(tableViews_[temporaryVersion], 00376 version, 00377 tableViews_[temporaryVersion].getAuthor()); 00378 setActiveView(version); 00379 eraseView(temporaryVersion); // delete temp version from tableViews_ 00380 } 00381 00382 //============================================================================== 00383 bool TableBase::isStored(const TableVersion& version) const 00384 { 00385 return (tableViews_.find(version) != tableViews_.end()); 00386 } 00387 00388 //============================================================================== 00389 bool TableBase::eraseView(TableVersion version) 00390 { 00391 if(!isStored(version)) 00392 return false; 00393 00394 if(activeTableView_ && 00395 getViewVersion() == version) // if activeVersion is being erased! 00396 deactivate(); // deactivate active view, instead of guessing at next active view 00397 00398 tableViews_.erase(version); 00399 00400 return true; 00401 } 00402 00403 //============================================================================== 00404 const std::string& TableBase::getTableName(void) const { return tableName_; } 00405 00406 //============================================================================== 00407 const std::string& TableBase::getTableDescription(void) const 00408 { 00409 return tableDescription_; 00410 } 00411 00412 //============================================================================== 00413 const TableVersion& TableBase::getViewVersion(void) const 00414 { 00415 return getView().getVersion(); 00416 } 00417 00418 //============================================================================== 00419 // latestAndMockupColumnNumberMismatch 00420 // intended to check if the column count was recently changed 00421 bool TableBase::latestAndMockupColumnNumberMismatch(void) const 00422 { 00423 std::set<TableVersion> retSet = getStoredVersions(); 00424 if(retSet.size() && !retSet.rbegin()->isTemporaryVersion()) 00425 { 00426 return tableViews_.find(*(retSet.rbegin()))->second.getNumberOfColumns() != 00427 mockupTableView_.getNumberOfColumns(); 00428 } 00429 // there are no latest non-temporary tables so there is a mismatch (by default) 00430 return true; 00431 } 00432 00433 //============================================================================== 00434 std::set<TableVersion> TableBase::getStoredVersions(void) const 00435 { 00436 std::set<TableVersion> retSet; 00437 for(auto& configs : tableViews_) 00438 retSet.emplace(configs.first); 00439 return retSet; 00440 } 00441 00442 //============================================================================== 00443 // getNumberOfStoredViews 00444 // count number of stored views, not including temporary views 00445 // (invalid views should be impossible) 00446 unsigned int TableBase::getNumberOfStoredViews(void) const 00447 { 00448 unsigned int sz = 0; 00449 for(auto& viewPair : tableViews_) 00450 if(viewPair.first.isTemporaryVersion()) 00451 continue; 00452 else if(viewPair.first.isInvalid()) 00453 { 00454 //__SS__ << "Can NOT have a stored view with an invalid version!"; 00455 //__SS_THROW__; 00456 00457 // NOTE: if this starts happening a lot, could just auto-correct and remove 00458 // the invalid version 00459 // but it would be better to fix the cause. 00460 00461 // FIXME... for now just auto correcting 00462 __COUT__ << "There is an invalid version now!.. where did it come from?" 00463 << __E__; 00464 } 00465 else 00466 ++sz; 00467 return sz; 00468 } 00469 00470 //============================================================================== 00471 const TableView& TableBase::getView(void) const 00472 { 00473 if(!activeTableView_) 00474 { 00475 __SS__ << "activeTableView_ pointer is null! (...likely the active view was not " 00476 "setup properly. Check your system setup.)"; 00477 __SS_THROW__; 00478 } 00479 return *activeTableView_; 00480 } 00481 00482 //============================================================================== 00483 TableView* TableBase::getViewP(void) 00484 { 00485 if(!activeTableView_) 00486 { 00487 __SS__ << "activeTableView_ pointer is null! (...likely the active view was not " 00488 "setup properly. Check your system setup.)"; 00489 __SS_THROW__; 00490 } 00491 return activeTableView_; 00492 } 00493 00494 //============================================================================== 00495 TableView* TableBase::getMockupViewP(void) { return &mockupTableView_; } 00496 00497 //============================================================================== 00498 void TableBase::setTableName(const std::string& tableName) { tableName_ = tableName; } 00499 00500 //============================================================================== 00501 void TableBase::setTableDescription(const std::string& tableDescription) 00502 { 00503 tableDescription_ = tableDescription; 00504 } 00505 00506 //============================================================================== 00507 // deactivate 00508 // reset the active view 00509 void TableBase::deactivate() { activeTableView_ = 0; } 00510 00511 //============================================================================== 00512 // isActive 00513 bool TableBase::isActive() { return activeTableView_ ? true : false; } 00514 00515 //============================================================================== 00516 bool TableBase::setActiveView(TableVersion version) 00517 { 00518 if(!isStored(version)) 00519 { // we don't call else load for the user, because the table manager would lose 00520 // track.. (I think?) 00521 // so load new versions for the first time through the table manager only. (I 00522 // think??) 00523 __SS__ << "\nsetActiveView() ERROR: View with version " << version 00524 << " has never been stored before!" << __E__; 00525 __SS_THROW__; 00526 return false; 00527 } 00528 activeTableView_ = &tableViews_[version]; 00529 00530 if(tableViews_[version].getVersion() != version) 00531 { 00532 __SS__ << "Something has gone very wrong with the version handling!" << __E__; 00533 __SS_THROW__; 00534 } 00535 00536 return true; 00537 } 00538 00539 //============================================================================== 00540 // mergeViews 00541 // merges source view A and B and places in 00542 // destination temporary version. 00543 // if destination version is invalid, then next available temporary version is chosen 00544 // one error, throw exception 00545 // 00546 // Returns version of new temporary view that was created. 00547 TableVersion TableBase::mergeViews( 00548 const TableView& sourceViewA, 00549 const TableView& sourceViewB, 00550 TableVersion destinationVersion, 00551 const std::string& author, 00552 const std::string& mergeApproach /*Rename,Replace,Skip*/, 00553 std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>, 00554 std::string /*converted uidB*/>& uidConversionMap, 00555 std::map<std::pair< 00556 std::string /*original table*/, 00557 std::pair<std::string /*group linkid*/, std::string /*original gidB*/> >, 00558 std::string /*converted gidB*/>& groupidConversionMap, 00559 bool fillRecordConversionMaps, 00560 bool applyRecordConversionMaps, 00561 bool generateUniqueDataColumns) 00562 { 00563 __COUT__ << "mergeViews starting..." << __E__; 00564 00565 // There 3 modes: 00566 // rename -- All records from both groups are maintained, but conflicts from B 00567 // are renamed. 00568 // Must maintain a map of UIDs that are remapped to new name for 00569 // groupB, because linkUID fields must be preserved. replace -- 00570 // Any UID conflicts for a record are replaced by the record from group B. 00571 // skip -- Any UID conflicts for a record are skipped so that group A record 00572 // remains 00573 00574 // check valid mode 00575 if(!(mergeApproach == "Rename" || mergeApproach == "Replace" || 00576 mergeApproach == "Skip")) 00577 { 00578 __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__; 00579 __SS_THROW__; 00580 } 00581 00582 // check that column sizes match 00583 if(sourceViewA.getNumberOfColumns() != mockupTableView_.getNumberOfColumns()) 00584 { 00585 __SS__ << "Error! Number of Columns of source view A must match destination " 00586 "mock-up view." 00587 << "Dimension of source is [" << sourceViewA.getNumberOfColumns() 00588 << "] and of destination mockup is [" 00589 << mockupTableView_.getNumberOfColumns() << "]." << __E__; 00590 __SS_THROW__; 00591 } 00592 // check that column sizes match 00593 if(sourceViewB.getNumberOfColumns() != mockupTableView_.getNumberOfColumns()) 00594 { 00595 __SS__ << "Error! Number of Columns of source view B must match destination " 00596 "mock-up view." 00597 << "Dimension of source is [" << sourceViewB.getNumberOfColumns() 00598 << "] and of destination mockup is [" 00599 << mockupTableView_.getNumberOfColumns() << "]." << __E__; 00600 __SS_THROW__; 00601 } 00602 00603 // fill conversion map based on merge approach 00604 00605 sourceViewA.print(); 00606 sourceViewB.print(); 00607 00608 if(fillRecordConversionMaps && mergeApproach == "Rename") 00609 { 00610 __COUT__ << "Filling record conversion map." << __E__; 00611 00612 // rename -- All records from both groups are maintained, but conflicts from 00613 // B are renamed. 00614 // Must maintain a map of UIDs that are remapped to new name for 00615 // groupB, because linkUID fields must be preserved. 00616 00617 // for each B record 00618 // if there is a conflict, rename 00619 00620 unsigned int uniqueId; 00621 std::string uniqueIdString, uniqueIdBase; 00622 char indexString[1000]; 00623 unsigned int ra; 00624 unsigned int numericStartIndex; 00625 bool found; 00626 00627 for(unsigned int cb = 0; cb < sourceViewB.getNumberOfColumns(); ++cb) 00628 { 00629 // skip columns that are not UID or GroupID columns 00630 if(!(sourceViewA.getColumnInfo(cb).isUID() || 00631 sourceViewA.getColumnInfo(cb).isGroupID())) 00632 continue; 00633 00634 __COUT__ << "Have an ID column: " << cb << " " 00635 << sourceViewA.getColumnInfo(cb).getType() << __E__; 00636 00637 // at this point we have an ID column, verify B and mockup are the same 00638 if(sourceViewA.getColumnInfo(cb).getType() != 00639 sourceViewB.getColumnInfo(cb).getType() || 00640 sourceViewA.getColumnInfo(cb).getType() != 00641 mockupTableView_.getColumnInfo(cb).getType()) 00642 { 00643 __SS__ << "Error! " << sourceViewA.getColumnInfo(cb).getType() 00644 << " column " << cb 00645 << " of source view A must match source B and destination mock-up " 00646 "view." 00647 << " Column of source B is [" 00648 << sourceViewA.getColumnInfo(cb).getType() 00649 << "] and of destination mockup is [" 00650 << mockupTableView_.getColumnInfo(cb).getType() << "]." << __E__; 00651 __SS_THROW__; 00652 } 00653 00654 // getColLinkGroupID(childLinkIndex) 00655 00656 std::vector<std::string /*converted uidB*/> 00657 localConvertedIds; // used for conflict completeness check 00658 00659 if(sourceViewA.getColumnInfo(cb).isGroupID()) 00660 { 00661 std::set<std::string> aGroupids = sourceViewA.getSetOfGroupIDs(cb); 00662 std::set<std::string> bGroupids = sourceViewB.getSetOfGroupIDs(cb); 00663 00664 for(const auto& bGroupid : bGroupids) 00665 { 00666 if(aGroupids.find(bGroupid) == aGroupids.end()) 00667 continue; 00668 00669 // if here, found conflict 00670 __COUT__ << "found conflict: " << getTableName() << "/" << bGroupid 00671 << __E__; 00672 00673 // extract starting uniqueId number 00674 { 00675 const std::string& str = bGroupid; 00676 numericStartIndex = str.size(); 00677 00678 // find first non-numeric character 00679 while(numericStartIndex - 1 < str.size() && 00680 str[numericStartIndex - 1] >= '0' && 00681 str[numericStartIndex - 1] <= '9') 00682 --numericStartIndex; 00683 00684 if(numericStartIndex < str.size()) 00685 { 00686 uniqueId = atoi(str.substr(numericStartIndex).c_str()) + 1; 00687 uniqueIdBase = str.substr(0, numericStartIndex); 00688 } 00689 else 00690 { 00691 uniqueId = 0; 00692 uniqueIdBase = str; 00693 } 00694 00695 __COUTV__(uniqueIdBase); 00696 __COUTV__(uniqueId); 00697 } // end //extract starting uniqueId number 00698 00699 // find unique id string 00700 { 00701 sprintf(indexString, "%u", uniqueId); 00702 uniqueIdString = uniqueIdBase + indexString; 00703 __COUTV__(uniqueIdString); 00704 00705 found = false; 00706 // check converted records and source A and B for conflicts 00707 if(aGroupids.find(uniqueIdString) != aGroupids.end()) 00708 found = true; 00709 if(!found && bGroupids.find(uniqueIdString) != bGroupids.end()) 00710 found = true; 00711 if(!found && bGroupids.find(uniqueIdString) != bGroupids.end()) 00712 found = true; 00713 for(ra = 0; !found && ra < localConvertedIds.size(); ++ra) 00714 if(localConvertedIds[ra] == uniqueIdString) 00715 found = true; 00716 00717 while(found) // while conflict, change id 00718 { 00719 ++uniqueId; 00720 sprintf(indexString, "%u", uniqueId); 00721 uniqueIdString = uniqueIdBase + indexString; 00722 __COUTV__(uniqueIdString); 00723 00724 found = false; 00725 // check converted records and source A and B for conflicts 00726 if(aGroupids.find(uniqueIdString) != aGroupids.end()) 00727 found = true; 00728 if(!found && 00729 bGroupids.find(uniqueIdString) != bGroupids.end()) 00730 found = true; 00731 if(!found && 00732 bGroupids.find(uniqueIdString) != bGroupids.end()) 00733 found = true; 00734 for(ra = 0; !found && ra < localConvertedIds.size(); ++ra) 00735 if(localConvertedIds[ra] == uniqueIdString) 00736 found = true; 00737 } 00738 } // end find unique id string 00739 00740 // have unique id string now 00741 __COUTV__(uniqueIdString); 00742 00743 groupidConversionMap 00744 [std::pair<std::string /*original table*/, 00745 std::pair<std::string /*group linkid*/, 00746 std::string /*original gidB*/> >( 00747 getTableName(), 00748 std::pair<std::string /*group linkid*/, 00749 std::string /*original gidB*/>( 00750 sourceViewB.getColumnInfo(cb).getChildLinkIndex(), 00751 bGroupid))] = uniqueIdString; 00752 localConvertedIds.push_back(uniqueIdString); // save to vector for 00753 // future conflict 00754 // checking within table 00755 00756 } // end row find unique id string loop for groupid 00757 00758 // done creating conversion map 00759 __COUTV__(StringMacros::mapToString(groupidConversionMap)); 00760 00761 } // end group id conversion map fill 00762 else // start uid conversion map fill 00763 { 00764 for(unsigned int rb = 0; rb < sourceViewB.getNumberOfRows(); ++rb) 00765 { 00766 found = false; 00767 00768 for(ra = 0; ra < sourceViewA.getDataView().size(); ++ra) 00769 if(sourceViewA.getValueAsString(ra, cb) == 00770 sourceViewB.getValueAsString(rb, cb)) 00771 { 00772 found = true; 00773 break; 00774 } 00775 00776 if(!found) 00777 continue; 00778 00779 // found conflict 00780 __COUT__ << "found conflict: " << getTableName() << "/" 00781 << sourceViewB.getDataView()[rb][cb] << __E__; 00782 00783 // extract starting uniqueId number 00784 { 00785 const std::string& str = sourceViewB.getDataView()[rb][cb]; 00786 numericStartIndex = str.size(); 00787 00788 // find first non-numeric character 00789 while(numericStartIndex - 1 < str.size() && 00790 str[numericStartIndex - 1] >= '0' && 00791 str[numericStartIndex - 1] <= '9') 00792 --numericStartIndex; 00793 00794 if(numericStartIndex < str.size()) 00795 { 00796 uniqueId = atoi(str.substr(numericStartIndex).c_str()) + 1; 00797 uniqueIdBase = str.substr(0, numericStartIndex); 00798 } 00799 else 00800 { 00801 uniqueId = 0; 00802 uniqueIdBase = str; 00803 } 00804 00805 __COUTV__(uniqueIdBase); 00806 __COUTV__(uniqueId); 00807 } // end //extract starting uniqueId number 00808 00809 // find unique id string 00810 { 00811 sprintf(indexString, "%u", uniqueId); 00812 uniqueIdString = uniqueIdBase + indexString; 00813 __COUTV__(uniqueIdString); 00814 00815 found = false; 00816 // check converted records and source A and B for conflicts 00817 for(ra = 0; !found && ra < sourceViewA.getDataView().size(); ++ra) 00818 if(sourceViewA.getValueAsString(ra, cb) == uniqueIdString) 00819 found = true; 00820 for(ra = 0; !found && ra < sourceViewB.getDataView().size(); ++ra) 00821 if(ra == rb) 00822 continue; // skip record in question 00823 else if(sourceViewB.getValueAsString(ra, cb) == 00824 uniqueIdString) 00825 found = true; 00826 for(ra = 0; !found && ra < localConvertedIds.size(); ++ra) 00827 if(localConvertedIds[ra] == uniqueIdString) 00828 found = true; 00829 00830 while(found) // while conflict, change id 00831 { 00832 ++uniqueId; 00833 sprintf(indexString, "%u", uniqueId); 00834 uniqueIdString = uniqueIdBase + indexString; 00835 __COUTV__(uniqueIdString); 00836 00837 found = false; 00838 // check converted records and source A and B for conflicts 00839 for(ra = 0; !found && ra < sourceViewA.getDataView().size(); 00840 ++ra) 00841 if(sourceViewA.getValueAsString(ra, cb) == uniqueIdString) 00842 found = true; 00843 for(ra = 0; !found && ra < sourceViewB.getDataView().size(); 00844 ++ra) 00845 if(ra == rb) 00846 continue; // skip record in question 00847 else if(sourceViewB.getValueAsString(ra, cb) == 00848 uniqueIdString) 00849 found = true; 00850 for(ra = 0; !found && ra < localConvertedIds.size(); ++ra) 00851 if(localConvertedIds[ra] == uniqueIdString) 00852 found = true; 00853 } 00854 } // end find unique id string 00855 00856 // have unique id string now 00857 __COUTV__(uniqueIdString); 00858 00859 uidConversionMap[std::pair<std::string /*original table*/, 00860 std::string /*original uidB*/>( 00861 getTableName(), sourceViewB.getValueAsString(rb, cb))] = 00862 uniqueIdString; 00863 localConvertedIds.push_back(uniqueIdString); // save to vector for 00864 // future conflict 00865 // checking within table 00866 00867 } // end row find unique id string loop 00868 00869 // done creating conversion map 00870 __COUTV__(StringMacros::mapToString(uidConversionMap)); 00871 } 00872 00873 } // end column find unique id string loop 00874 00875 } // end rename conversion map create 00876 else 00877 __COUT__ << "Not filling record conversion map." << __E__; 00878 00879 if(!applyRecordConversionMaps) 00880 { 00881 __COUT__ << "Not applying record conversion map." << __E__; 00882 return TableVersion(); // return invalid 00883 } 00884 else 00885 { 00886 __COUT__ << "Applying record conversion map." << __E__; 00887 __COUTV__(StringMacros::mapToString(uidConversionMap)); 00888 __COUTV__(StringMacros::mapToString(groupidConversionMap)); 00889 } 00890 00891 // if destinationVersion is INVALID, creates next available temporary version 00892 destinationVersion = createTemporaryView(TableVersion(), destinationVersion); 00893 00894 __COUT__ << "Merging from (A) " << sourceViewA.getTableName() << "_v" 00895 << sourceViewA.getVersion() << " and (B) " << sourceViewB.getTableName() 00896 << "_v" << sourceViewB.getVersion() << " to " << getTableName() << "_v" 00897 << destinationVersion << " with approach '" << mergeApproach << ".'" 00898 << __E__; 00899 00900 // if the merge fails then delete the destinationVersion view 00901 try 00902 { 00903 // start with a copy of source view A 00904 00905 TableView* destinationView = &(tableViews_[destinationVersion].copy( 00906 sourceViewA, destinationVersion, author)); 00907 00908 unsigned int destRow, destSize = destinationView->getDataView().size(); 00909 unsigned int cb; 00910 bool found; 00911 std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>, 00912 std::string /*converted uidB*/>::iterator uidConversionIt; 00913 std::map<std::pair<std::string /*original table*/, 00914 std::pair<std::string /*group linkid*/, 00915 std::string /*original gidB*/> >, 00916 std::string /*converted uidB*/>::iterator groupidConversionIt; 00917 00918 bool linkIsGroup; 00919 std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair; 00920 std::string strb; 00921 size_t stri; 00922 00923 unsigned int colUID = mockupTableView_.getColUID(); // setup UID column 00924 00925 // handle merger with conflicts consideration 00926 for(unsigned int rb = 0; rb < sourceViewB.getNumberOfRows(); ++rb) 00927 { 00928 if(mergeApproach == "Rename") 00929 { 00930 // rename -- All records from both groups are maintained, but 00931 // conflicts from B are renamed. Must maintain a map of 00932 // UIDs that are remapped to new name for groupB, 00933 // because linkUID fields must be preserved. 00934 00935 // conflict does not matter (because record conversion map is already 00936 // created, always take and append the B record copy row from B to new 00937 // row 00938 destRow = destinationView->copyRows( 00939 author, 00940 sourceViewB, 00941 rb, 00942 1 /*srcRowsToCopy*/, 00943 -1 /*destOffsetRow*/, 00944 generateUniqueDataColumns /*generateUniqueDataColumns*/); 00945 00946 // check every column and remap conflicting names 00947 00948 for(cb = 0; cb < sourceViewB.getNumberOfColumns(); ++cb) 00949 { 00950 if(sourceViewB.getColumnInfo(cb).isChildLink()) 00951 continue; // skip link columns that have table name 00952 else if(sourceViewB.getColumnInfo(cb).isChildLinkUID()) 00953 { 00954 __COUT__ << "Checking UID link... col=" << cb << __E__; 00955 sourceViewB.getChildLink(cb, linkIsGroup, linkPair); 00956 00957 // if table and uid are in conversion map, convert 00958 if((uidConversionIt = uidConversionMap.find( 00959 std::pair<std::string /*original table*/, 00960 std::string /*original uidB*/>( 00961 sourceViewB.getValueAsString(rb, linkPair.first), 00962 sourceViewB.getValueAsString( 00963 rb, linkPair.second)))) != uidConversionMap.end()) 00964 { 00965 __COUT__ << "Found entry to remap: " 00966 << sourceViewB.getDataView()[rb][linkPair.second] 00967 << " ==> " << uidConversionIt->second << __E__; 00968 destinationView->setValueAsString( 00969 uidConversionIt->second, destRow, linkPair.second); 00970 } 00971 } 00972 else if(sourceViewB.getColumnInfo(cb).isChildLinkGroupID()) 00973 { 00974 __COUT__ << "Checking GroupID link... col=" << cb << __E__; 00975 sourceViewB.getChildLink(cb, linkIsGroup, linkPair); 00976 00977 // if table and uid are in conversion map, convert 00978 if((groupidConversionIt = groupidConversionMap.find( 00979 std::pair<std::string /*original table*/, 00980 std::pair<std::string /*group linkid*/, 00981 std::string /*original gidB*/> >( 00982 sourceViewB.getValueAsString(rb, linkPair.first), 00983 std::pair<std::string /*group linkid*/, 00984 std::string /*original gidB*/>( 00985 sourceViewB.getColumnInfo(cb).getChildLinkIndex(), 00986 sourceViewB.getValueAsString( 00987 rb, linkPair.second))))) != 00988 groupidConversionMap.end()) 00989 { 00990 __COUT__ << "Found entry to remap: " 00991 << sourceViewB.getDataView()[rb][linkPair.second] 00992 << " ==> " << groupidConversionIt->second << __E__; 00993 destinationView->setValueAsString( 00994 groupidConversionIt->second, destRow, linkPair.second); 00995 } 00996 } 00997 else if(sourceViewB.getColumnInfo(cb).isUID()) 00998 { 00999 __COUT__ << "Checking UID... col=" << cb << __E__; 01000 if((uidConversionIt = uidConversionMap.find( 01001 std::pair<std::string /*original table*/, 01002 std::string /*original uidB*/>( 01003 getTableName(), 01004 sourceViewB.getValueAsString(rb, cb)))) != 01005 uidConversionMap.end()) 01006 { 01007 __COUT__ << "Found entry to remap: " 01008 << sourceViewB.getDataView()[rb][cb] << " ==> " 01009 << uidConversionIt->second << __E__; 01010 destinationView->setValueAsString( 01011 uidConversionIt->second, destRow, cb); 01012 } 01013 } 01014 else if(sourceViewB.getColumnInfo(cb).isGroupID()) 01015 { 01016 __COUT__ << "Checking GroupID... col=" << cb << __E__; 01017 if((groupidConversionIt = groupidConversionMap.find( 01018 std::pair<std::string /*original table*/, 01019 std::pair<std::string /*group linkid*/, 01020 std::string /*original gidB*/> >( 01021 getTableName(), 01022 std::pair<std::string /*group linkid*/, 01023 std::string /*original gidB*/>( 01024 sourceViewB.getColumnInfo(cb).getChildLinkIndex(), 01025 sourceViewB.getValueAsString(rb, cb))))) != 01026 groupidConversionMap.end()) 01027 { 01028 __COUT__ << "Found entry to remap: " 01029 << sourceViewB.getDataView()[rb][cb] << " ==> " 01030 << groupidConversionIt->second << __E__; 01031 destinationView->setValueAsString( 01032 groupidConversionIt->second, destRow, cb); 01033 } 01034 } 01035 else 01036 { 01037 // look for text link to a Table/UID in the map 01038 strb = sourceViewB.getValueAsString(rb, cb); 01039 if(strb.size() > getTableName().size() + 2 && strb[0] == '/') 01040 { 01041 // check for linked name 01042 __COUT__ << "Checking col" << cb << " " << strb << __E__; 01043 01044 // see if there is an entry in p 01045 for(const auto& mapPairToPair : uidConversionMap) 01046 { 01047 if((stri = strb.find(mapPairToPair.first.first + "/" + 01048 mapPairToPair.first.second)) != 01049 std::string::npos) 01050 { 01051 __COUT__ << "Found a text link match (stri=" << stri 01052 << ")! " 01053 << (mapPairToPair.first.first + "/" + 01054 mapPairToPair.first.second) 01055 << " ==> " << mapPairToPair.second << __E__; 01056 01057 // insert mapped substitution into string 01058 destinationView->setValueAsString( 01059 strb.substr(0, stri) + 01060 (mapPairToPair.first.first + "/" + 01061 mapPairToPair.first.second) + 01062 strb.substr(stri + 01063 (mapPairToPair.first.first + "/" + 01064 mapPairToPair.first.second) 01065 .size()), 01066 destRow, 01067 cb); 01068 01069 __COUT__ 01070 << "Found entry to remap: " 01071 << sourceViewB.getDataView()[rb][cb] << " ==> " 01072 << destinationView->getDataView()[destRow][cb] 01073 << __E__; 01074 break; 01075 } 01076 } // end uid conversion map loop 01077 } 01078 } 01079 } // end column loop over B record 01080 01081 continue; 01082 } // end rename, no-conflict handling 01083 01084 // if here, then not doing rename, so conflicts matter 01085 01086 found = false; 01087 01088 for(destRow = 0; destRow < destSize; ++destRow) 01089 if(destinationView->getValueAsString(destRow, colUID) == 01090 sourceViewB.getValueAsString(rb, colUID)) 01091 { 01092 found = true; 01093 break; 01094 } 01095 if(!found) // no conflict 01096 { 01097 __COUT__ << "No " << mergeApproach << " conflict: " << __E__; 01098 01099 if(mergeApproach == "replace" || mergeApproach == "skip") 01100 { 01101 // no conflict so append the B record 01102 // copy row from B to new row 01103 destinationView->copyRows( 01104 author, sourceViewB, rb, 1 /*srcRowsToCopy*/); 01105 } 01106 else 01107 01108 continue; 01109 } // end no-conflict handling 01110 01111 // if here, then there was a conflict 01112 01113 __COUT__ << "found " << mergeApproach 01114 << " conflict: " << sourceViewB.getDataView()[rb][colUID] << __E__; 01115 01116 if(mergeApproach == "replace") 01117 { 01118 // replace -- Any UID conflicts for a record are replaced by the 01119 // record from group B. 01120 01121 // delete row in destination 01122 destinationView->deleteRow(destRow--); // delete row and back up pointer 01123 --destSize; 01124 01125 // append the B record now 01126 // copy row from B to new row 01127 destinationView->copyRows(author, sourceViewB, rb, 1 /*srcRowsToCopy*/); 01128 } 01129 // else if (mergeApproach == "skip") then do nothing with conflicting B record 01130 } 01131 01132 destinationView->print(); 01133 } 01134 catch(...) // if the copy fails then delete the destinationVersion view 01135 { 01136 __COUT_ERR__ << "Failed to merge " << sourceViewA.getTableName() << "_v" 01137 << sourceViewA.getVersion() << " and " << sourceViewB.getTableName() 01138 << "_v" << sourceViewB.getVersion() << " into " << getTableName() 01139 << "_v" << destinationVersion << __E__; 01140 __COUT_WARN__ << "Deleting the failed destination version " << destinationVersion 01141 << __E__; 01142 eraseView(destinationVersion); 01143 throw; // and rethrow 01144 } 01145 01146 return destinationVersion; 01147 } // end mergeViews 01148 01149 //============================================================================== 01150 // copyView 01151 // copies source view (including version) and places in self 01152 // as destination temporary version. 01153 // if destination version is invalid, then next available temporary version is chosen 01154 // if conflict, throw exception 01155 // 01156 // Returns version of new temporary view that was created. 01157 TableVersion TableBase::copyView(const TableView& sourceView, 01158 TableVersion destinationVersion, 01159 const std::string& author) 01160 { 01161 // check that column sizes match 01162 if(sourceView.getNumberOfColumns() != mockupTableView_.getNumberOfColumns()) 01163 { 01164 __SS__ << "Error! Number of Columns of source view must match destination " 01165 "mock-up view." 01166 << "Dimension of source is [" << sourceView.getNumberOfColumns() 01167 << "] and of destination mockup is [" 01168 << mockupTableView_.getNumberOfColumns() << "]." << __E__; 01169 __SS_THROW__; 01170 } 01171 01172 // check for destination version confict 01173 if(!destinationVersion.isInvalid() && 01174 tableViews_.find(destinationVersion) != tableViews_.end()) 01175 { 01176 __SS__ << "Error! Asked to copy a view with a conflicting version: " 01177 << destinationVersion << __E__; 01178 __SS_THROW__; 01179 } 01180 01181 // if destinationVersion is INVALID, creates next available temporary version 01182 destinationVersion = createTemporaryView(TableVersion(), destinationVersion); 01183 01184 __COUT__ << "Copying from " << sourceView.getTableName() << "_v" 01185 << sourceView.getVersion() << " to " << getTableName() << "_v" 01186 << destinationVersion << __E__; 01187 01188 try 01189 { 01190 tableViews_[destinationVersion].copy(sourceView, destinationVersion, author); 01191 } 01192 catch(...) // if the copy fails then delete the destinationVersion view 01193 { 01194 __COUT_ERR__ << "Failed to copy from " << sourceView.getTableName() << "_v" 01195 << sourceView.getVersion() << " to " << getTableName() << "_v" 01196 << destinationVersion << __E__; 01197 __COUT_WARN__ << "Deleting the failed destination version " << destinationVersion 01198 << __E__; 01199 eraseView(destinationVersion); 01200 throw; // and rethrow 01201 } 01202 01203 return destinationVersion; 01204 } // end copyView() 01205 01206 //============================================================================== 01207 // createTemporaryView 01208 // -1, from MockUp, else from valid view version 01209 // destTemporaryViewVersion is starting point for search for available temporary 01210 // versions. if destTemporaryViewVersion is invalid, starts search at 01211 // TableVersion::getNextTemporaryVersion(). 01212 // returns new temporary version number (which is always negative) 01213 TableVersion TableBase::createTemporaryView(TableVersion sourceViewVersion, 01214 TableVersion destTemporaryViewVersion) 01215 { 01216 __COUT__ << "Table: " << getTableName() << __E__; 01217 01218 __COUT__ << "Num of Views: " << tableViews_.size() 01219 << " (Temporary Views: " << (tableViews_.size() - getNumberOfStoredViews()) 01220 << ")" << __E__; 01221 01222 TableVersion tmpVersion = destTemporaryViewVersion; 01223 if(tmpVersion.isInvalid()) 01224 tmpVersion = TableVersion::getNextTemporaryVersion(); 01225 while(isStored(tmpVersion) && // find a new valid temporary version 01226 !(tmpVersion = TableVersion::getNextTemporaryVersion(tmpVersion)).isInvalid()) 01227 ; 01228 if(isStored(tmpVersion) || tmpVersion.isInvalid()) 01229 { 01230 __SS__ << "Invalid destination temporary version: " << destTemporaryViewVersion 01231 << ". Expected next temporary version < " << tmpVersion << __E__; 01232 __COUT_ERR__ << ss.str(); 01233 __SS_THROW__; 01234 } 01235 01236 if(sourceViewVersion == 01237 TableVersion::INVALID || // use mockup if sourceVersion is -1 or not found 01238 tableViews_.find(sourceViewVersion) == tableViews_.end()) 01239 { 01240 if(sourceViewVersion != -1) 01241 { 01242 __SS__ << "ERROR: sourceViewVersion " << sourceViewVersion << " not found. " 01243 << "Invalid source version. Version requested is not stored (yet?) or " 01244 "does not exist." 01245 << __E__; 01246 __COUT_ERR__ << ss.str(); 01247 __SS_THROW__; 01248 } 01249 __COUT__ << "Using Mock-up view" << __E__; 01250 tableViews_[tmpVersion].copy( 01251 mockupTableView_, tmpVersion, mockupTableView_.getAuthor()); 01252 } 01253 else 01254 { 01255 try // do not allow init to throw an exception here.. 01256 { // it's ok to copy invalid data, the user may be trying to change it 01257 tableViews_[tmpVersion].copy(tableViews_[sourceViewVersion], 01258 tmpVersion, 01259 tableViews_[sourceViewVersion].getAuthor()); 01260 } 01261 catch(...) 01262 { 01263 __COUT_WARN__ 01264 << "createTemporaryView() Source view failed init(). " 01265 << "This is being ignored (hopefully the new copy is being fixed)." 01266 << __E__; 01267 } 01268 } 01269 01270 return tmpVersion; 01271 } // end createTemporaryView() 01272 01273 //============================================================================== 01274 // getNextAvailableTemporaryView 01275 // TableVersion::INVALID is always MockUp 01276 // returns next available temporary version number (which is always negative) 01277 TableVersion TableBase::getNextTemporaryVersion() const 01278 { 01279 TableVersion tmpVersion; 01280 01281 // std::map guarantees versions are in increasing order! 01282 if(tableViews_.size() != 0 && tableViews_.begin()->first.isTemporaryVersion()) 01283 tmpVersion = TableVersion::getNextTemporaryVersion(tableViews_.begin()->first); 01284 else 01285 tmpVersion = TableVersion::getNextTemporaryVersion(); 01286 01287 // verify tmpVersion is ok 01288 if(isStored(tmpVersion) || tmpVersion.isInvalid() || !tmpVersion.isTemporaryVersion()) 01289 { 01290 __SS__ << "Invalid destination temporary version: " << tmpVersion << __E__; 01291 __COUT_ERR__ << ss.str(); 01292 __SS_THROW__; 01293 } 01294 return tmpVersion; 01295 } 01296 01297 //============================================================================== 01298 // getNextVersion 01299 // returns next available new version 01300 // the implication is any version number equal or greater is available. 01301 TableVersion TableBase::getNextVersion() const 01302 { 01303 TableVersion tmpVersion; 01304 01305 // std::map guarantees versions are in increasing order! 01306 if(tableViews_.size() != 0 && !tableViews_.rbegin()->first.isTemporaryVersion()) 01307 tmpVersion = TableVersion::getNextVersion(tableViews_.rbegin()->first); 01308 else 01309 tmpVersion = TableVersion::getNextVersion(); 01310 01311 // verify tmpVersion is ok 01312 if(isStored(tmpVersion) || tmpVersion.isInvalid() || tmpVersion.isTemporaryVersion()) 01313 { 01314 __SS__ << "Invalid destination next version: " << tmpVersion << __E__; 01315 __COUT_ERR__ << ss.str(); 01316 __SS_THROW__; 01317 } 01318 return tmpVersion; 01319 } 01320 01321 //============================================================================== 01322 // getTemporaryView 01323 // must be a valid temporary version, and the view must be stored in table. 01324 // temporary version indicates it has not been saved to database and assigned a version 01325 // number 01326 TableView* TableBase::getTemporaryView(TableVersion temporaryVersion) 01327 { 01328 if(!temporaryVersion.isTemporaryVersion() || !isStored(temporaryVersion)) 01329 { 01330 __SS__ << getTableName() << ":: Error! Temporary version not found!" << __E__; 01331 __COUT_ERR__ << ss.str(); 01332 __SS_THROW__; 01333 } 01334 return &tableViews_[temporaryVersion]; 01335 } 01336 01337 //============================================================================== 01338 // convertToCaps 01339 // static utility for converting table and column names to the caps version 01340 // throw std::runtime_error if not completely alpha-numeric input 01341 std::string TableBase::convertToCaps(std::string& str, bool isConfigName) 01342 { 01343 // append Table to be nice to user 01344 unsigned int configPos = (unsigned int)std::string::npos; 01345 if(isConfigName && (configPos = str.find("Table")) != str.size() - strlen("Table")) 01346 str += "Table"; 01347 01348 // create all caps name and validate 01349 // only allow alpha names with Table at end 01350 std::string capsStr = ""; 01351 for(unsigned int c = 0; c < str.size(); ++c) 01352 if(str[c] >= 'A' && str[c] <= 'Z') 01353 { 01354 // add _ before table and if lower case to uppercase 01355 if(c == configPos || 01356 (c && str[c - 1] >= 'a' && 01357 str[c - 1] <= 'z') || // if this is a new start of upper case 01358 (c && str[c - 1] >= 'A' && 01359 str[c - 1] <= 'Z' && // if this is a new start from running caps 01360 c + 1 < str.size() && str[c + 1] >= 'a' && str[c + 1] <= 'z')) 01361 capsStr += "_"; 01362 capsStr += str[c]; 01363 } 01364 else if(str[c] >= 'a' && str[c] <= 'z') 01365 capsStr += char(str[c] - 32); // capitalize 01366 else if(str[c] >= '0' && str[c] <= '9') 01367 capsStr += str[c]; // allow numbers 01368 else // error! non-alpha 01369 __THROW__(std::string("TableBase::convertToCaps::") + 01370 "Invalid character found in name (allowed: A-Z, a-z, 0-9):" + str); 01371 01372 return capsStr; 01373 }