bool FieldPropertiesDialog::getNotNullConstraintName(const wxString& fieldName, wxString& constraintName) { if (DatabasePtr db = tableM->getDatabase()) { wxMBConv* conv = db->getCharsetConverter(); MetadataLoader* loader = db->getMetadataLoader(); MetadataLoaderTransaction tr(loader); IBPP::Statement& st1 = loader->getStatement( "SELECT rc.RDB$CONSTRAINT_NAME FROM RDB$RELATION_CONSTRAINTS rc " "JOIN RDB$CHECK_CONSTRAINTS cc " "ON rc.RDB$CONSTRAINT_NAME = cc.RDB$CONSTRAINT_NAME " "WHERE rc.RDB$CONSTRAINT_TYPE = 'NOT NULL' " "AND rc.RDB$RELATION_NAME = ?" "AND cc.RDB$TRIGGER_NAME = ?"); st1->Set(1, wx2std(tableM->getName_(), conv)); st1->Set(2, wx2std(fieldName, conv)); st1->Execute(); if (st1->Fetch()) { std::string s; st1->Get(1, s); constraintName = std2wxIdentifier(s, conv); return true; } } return false; }
void Exceptions::load(ProgressIndicator* progressIndicator) { DatabasePtr db = getDatabase(); MetadataLoader* loader = db->getMetadataLoader(); MetadataLoaderTransaction tr(loader); wxMBConv* converter = db->getCharsetConverter(); IBPP::Statement& st1 = loader->getStatement( Exception::getLoadStatement(true)); CollectionType exceptions; st1->Execute(); checkProgressIndicatorCanceled(progressIndicator); while (st1->Fetch()) { if (!st1->IsNull(1)) { std::string s; st1->Get(1, s); wxString name(std2wxIdentifier(s, converter)); ExceptionPtr exception = findByName(name); if (!exception) { exception.reset(new Exception(db, name)); initializeLockCount(exception, getLockCount()); } exceptions.push_back(exception); exception->loadProperties(st1, converter); } checkProgressIndicatorCanceled(progressIndicator); } setItems(exceptions); }
//! reads checks info from database void Table::loadCheckConstraints() { if (checkConstraintsLoadedM) return; checkConstraintsM.clear(); DatabasePtr db = getDatabase(); wxMBConv* conv = db->getCharsetConverter(); MetadataLoader* loader = db->getMetadataLoader(); // first start a transaction for metadata loading, then lock the table // when objects go out of scope and are destroyed, table will be unlocked // before the transaction is committed - any update() calls on observers // can possibly use the same transaction MetadataLoaderTransaction tr(loader); SubjectLocker lock(this); IBPP::Statement& st1 = loader->getStatement( "select r.rdb$constraint_name, t.rdb$trigger_source, d.rdb$field_name " " from rdb$relation_constraints r " " join rdb$check_constraints c on r.rdb$constraint_name=c.rdb$constraint_name and r.rdb$constraint_type = 'CHECK'" " join rdb$triggers t on c.rdb$trigger_name=t.rdb$trigger_name and t.rdb$trigger_type = 1 " " left join rdb$dependencies d on t.rdb$trigger_name = d.rdb$dependent_name " " and d.rdb$depended_on_name = r.rdb$relation_name " " and d.rdb$depended_on_type = 0 " " where r.rdb$relation_name=? " " order by 1 " ); st1->Set(1, wx2std(getName_(), conv)); st1->Execute(); CheckConstraint *cc = 0; while (st1->Fetch()) { std::string s; st1->Get(1, s); wxString cname(std2wxIdentifier(s, conv)); if (!cc || cname != cc->getName_()) // new constraint { wxString source; readBlob(st1, 2, source, conv); CheckConstraint c; c.setParent(this); c.setName_(cname); c.sourceM = source; checkConstraintsM.push_back(c); cc = &checkConstraintsM.back(); } if (!st1->IsNull(3)) { st1->Get(3, s); wxString fname(std2wxIdentifier(s, conv)); cc->columnsM.push_back(fname); } } checkConstraintsLoadedM = true; }
//! reads uniques from database void Table::loadUniqueConstraints() { if (uniqueConstraintsLoadedM) return; uniqueConstraintsM.clear(); DatabasePtr db = getDatabase(); wxMBConv* conv = db->getCharsetConverter(); MetadataLoader* loader = db->getMetadataLoader(); // first start a transaction for metadata loading, then lock the table // when objects go out of scope and are destroyed, table will be unlocked // before the transaction is committed - any update() calls on observers // can possibly use the same transaction MetadataLoaderTransaction tr(loader); SubjectLocker lock(this); IBPP::Statement& st1 = loader->getStatement( "select r.rdb$constraint_name, i.rdb$field_name, r.rdb$index_name " "from rdb$relation_constraints r, rdb$index_segments i " "where r.rdb$relation_name=? and r.rdb$index_name=i.rdb$index_name and " "(r.rdb$constraint_type='UNIQUE') order by r.rdb$constraint_name, i.rdb$field_position" ); st1->Set(1, wx2std(getName_(), conv)); st1->Execute(); UniqueConstraint *cc = 0; while (st1->Fetch()) { std::string s; st1->Get(1, s); wxString cname(std2wxIdentifier(s, conv)); st1->Get(2, s); wxString fname(std2wxIdentifier(s, conv)); st1->Get(3, s); wxString ixname(std2wxIdentifier(s, conv)); if (cc && cc->getName_() == cname) cc->columnsM.push_back(fname); else { UniqueConstraint c; uniqueConstraintsM.push_back(c); cc = &uniqueConstraintsM.back(); cc->indexNameM = ixname; cc->setName_(cname); cc->columnsM.push_back(fname); cc->setParent(this); } } uniqueConstraintsLoadedM = true; }
void Exception::loadProperties() { setPropertiesLoaded(false); DatabasePtr db = getDatabase(); MetadataLoader* loader = db->getMetadataLoader(); MetadataLoaderTransaction tr(loader); wxMBConv* converter = db->getCharsetConverter(); IBPP::Statement& st1 = loader->getStatement(getLoadStatement(false)); st1->Set(1, wx2std(getName_(), converter)); st1->Execute(); if (!st1->Fetch()) throw FRError(_("Exception not found: ") + getName_()); loadProperties(st1, converter); }
//----------------------------------------------------------------------------- void Exception::loadProperties() { setPropertiesLoaded(false); DatabasePtr db = getDatabase(); MetadataLoader* loader = db->getMetadataLoader(); MetadataLoaderTransaction tr(loader); IBPP::Statement& st1 = loader->getStatement( "select RDB$MESSAGE, RDB$EXCEPTION_NUMBER from RDB$EXCEPTIONS" " where RDB$EXCEPTION_NAME = ?"); st1->Set(1, wx2std(getName_(), db->getCharsetConverter())); st1->Execute(); st1->Fetch(); std::string message; st1->Get(1, message); messageM = std2wx(message, db->getCharsetConverter()); st1->Get(2, numberM); setPropertiesLoaded(true); }
//----------------------------------------------------------------------------- void Generator::loadProperties() { setPropertiesLoaded(false); DatabasePtr db = getDatabase(); MetadataLoader* loader = db->getMetadataLoader(); MetadataLoaderTransaction tr(loader); // IMPORTANT: for all other loading where the name of the db object is // Set() into a parameter getName_() is used, but for dynamically // building the SQL statement getQuotedName() must be used! std::string sqlName(wx2std(getQuotedName(), db->getCharsetConverter())); // do not use cached statements, because this can not be reused IBPP::Statement st1 = loader->createStatement( "select gen_id(" + sqlName + ", 0) from rdb$database"); st1->Execute(); st1->Fetch(); st1->Get(1, &valueM); setPropertiesLoaded(true); notifyObservers(); }
DomainPtr DomainCollectionBase::getDomain(const wxString& name) { DomainPtr domain = findByName(name); if (!domain) { SubjectLocker lock(this); DatabasePtr db = getDatabase(); MetadataLoader* loader = db->getMetadataLoader(); MetadataLoaderTransaction tr(loader); wxMBConv* converter = db->getCharsetConverter(); IBPP::Statement& st1 = loader->getStatement( Domain::getLoadStatement(false)); st1->Set(1, wx2std(name, converter)); st1->Execute(); if (st1->Fetch()) { domain = insert(name); domain->loadProperties(st1, converter); } } return domain; }
Status ShardingState::doRefreshMetadata(OperationContext* txn, const string& ns, const ChunkVersion& reqShardVersion, bool useRequestedVersion, ChunkVersion* latestShardVersion) { // The idea here is that we're going to reload the metadata from the config server, but // we need to do so outside any locks. When we get our result back, if the current metadata // has changed, we may not be able to install the new metadata. // // Get the initial metadata // No DBLock is needed since the metadata is expected to change during reload. // shared_ptr<CollectionMetadata> beforeMetadata; { stdx::lock_guard<stdx::mutex> lk(_mutex); // We can't reload if sharding is not enabled - i.e. without a config server location if (!_enabled) { string errMsg = str::stream() << "cannot refresh metadata for " << ns << " before sharding has been enabled"; warning() << errMsg; return Status(ErrorCodes::NotYetInitialized, errMsg); } // We also can't reload if a shard name has not yet been set. if (_shardName.empty()) { string errMsg = str::stream() << "cannot refresh metadata for " << ns << " before shard name has been set"; warning() << errMsg; return Status(ErrorCodes::NotYetInitialized, errMsg); } CollectionMetadataMap::iterator it = _collMetadata.find(ns); if (it != _collMetadata.end()) { beforeMetadata = it->second; } } ChunkVersion beforeShardVersion; ChunkVersion beforeCollVersion; if (beforeMetadata) { beforeShardVersion = beforeMetadata->getShardVersion(); beforeCollVersion = beforeMetadata->getCollVersion(); } *latestShardVersion = beforeShardVersion; // // Determine whether we need to diff or fully reload // bool fullReload = false; if (!beforeMetadata) { // We don't have any metadata to reload from fullReload = true; } else if (useRequestedVersion && reqShardVersion.epoch() != beforeShardVersion.epoch()) { // It's not useful to use the metadata as a base because we think the epoch will differ fullReload = true; } // // Load the metadata from the remote server, start construction // LOG(0) << "remotely refreshing metadata for " << ns << (useRequestedVersion ? string(" with requested shard version ") + reqShardVersion.toString() : "") << (fullReload ? ", current shard version is " : " based on current shard version ") << beforeShardVersion << ", current metadata version is " << beforeCollVersion; string errMsg; MetadataLoader mdLoader; CollectionMetadata* remoteMetadataRaw = new CollectionMetadata(); shared_ptr<CollectionMetadata> remoteMetadata(remoteMetadataRaw); Timer refreshTimer; Status status = mdLoader.makeCollectionMetadata(grid.catalogManager(), ns, getShardName(), fullReload ? NULL : beforeMetadata.get(), remoteMetadataRaw); long long refreshMillis = refreshTimer.millis(); if (status.code() == ErrorCodes::NamespaceNotFound) { remoteMetadata.reset(); remoteMetadataRaw = NULL; } else if (!status.isOK()) { warning() << "could not remotely refresh metadata for " << ns << causedBy(status.reason()); return status; } ChunkVersion remoteShardVersion; ChunkVersion remoteCollVersion; if (remoteMetadata) { remoteShardVersion = remoteMetadata->getShardVersion(); remoteCollVersion = remoteMetadata->getCollVersion(); } // // Get ready to install loaded metadata if needed // shared_ptr<CollectionMetadata> afterMetadata; ChunkVersion afterShardVersion; ChunkVersion afterCollVersion; ChunkVersion::VersionChoice choice; // If we choose to install the new metadata, this describes the kind of install enum InstallType { InstallType_New, InstallType_Update, InstallType_Replace, InstallType_Drop, InstallType_None } installType = InstallType_None; // compiler complains otherwise { // Exclusive collection lock needed since we're now potentially changing the metadata, // and don't want reads/writes to be ongoing. ScopedTransaction transaction(txn, MODE_IX); Lock::DBLock dbLock(txn->lockState(), nsToDatabaseSubstring(ns), MODE_IX); Lock::CollectionLock collLock(txn->lockState(), ns, MODE_X); // // Get the metadata now that the load has completed // stdx::lock_guard<stdx::mutex> lk(_mutex); // Don't reload if our config server has changed or sharding is no longer enabled if (!_enabled) { string errMsg = str::stream() << "could not refresh metadata for " << ns << ", sharding is no longer enabled"; warning() << errMsg; return Status(ErrorCodes::NotYetInitialized, errMsg); } CollectionMetadataMap::iterator it = _collMetadata.find(ns); if (it != _collMetadata.end()) afterMetadata = it->second; if (afterMetadata) { afterShardVersion = afterMetadata->getShardVersion(); afterCollVersion = afterMetadata->getCollVersion(); } *latestShardVersion = afterShardVersion; // // Resolve newer pending chunks with the remote metadata, finish construction // status = mdLoader.promotePendingChunks(afterMetadata.get(), remoteMetadataRaw); if (!status.isOK()) { warning() << "remote metadata for " << ns << " is inconsistent with current pending chunks" << causedBy(status.reason()); return status; } // // Compare the 'before', 'after', and 'remote' versions/epochs and choose newest // Zero-epochs (sentinel value for "dropped" collections), are tested by // !epoch.isSet(). // choice = ChunkVersion::chooseNewestVersion( beforeCollVersion, afterCollVersion, remoteCollVersion); if (choice == ChunkVersion::VersionChoice_Remote) { dassert(!remoteCollVersion.epoch().isSet() || remoteShardVersion >= beforeShardVersion); if (!afterCollVersion.epoch().isSet()) { // First metadata load installType = InstallType_New; dassert(it == _collMetadata.end()); _collMetadata.insert(make_pair(ns, remoteMetadata)); } else if (remoteCollVersion.epoch().isSet() && remoteCollVersion.epoch() == afterCollVersion.epoch()) { // Update to existing metadata installType = InstallType_Update; // Invariant: If CollMetadata was not found, version should be have been 0. dassert(it != _collMetadata.end()); it->second = remoteMetadata; } else if (remoteCollVersion.epoch().isSet()) { // New epoch detected, replacing metadata installType = InstallType_Replace; // Invariant: If CollMetadata was not found, version should be have been 0. dassert(it != _collMetadata.end()); it->second = remoteMetadata; } else { dassert(!remoteCollVersion.epoch().isSet()); // Drop detected installType = InstallType_Drop; _collMetadata.erase(it); } *latestShardVersion = remoteShardVersion; } } // End _mutex // End DBWrite // // Do messaging based on what happened above // string localShardVersionMsg = beforeShardVersion.epoch() == afterShardVersion.epoch() ? afterShardVersion.toString() : beforeShardVersion.toString() + " / " + afterShardVersion.toString(); if (choice == ChunkVersion::VersionChoice_Unknown) { string errMsg = str::stream() << "need to retry loading metadata for " << ns << ", collection may have been dropped or recreated during load" << " (loaded shard version : " << remoteShardVersion.toString() << ", stored shard versions : " << localShardVersionMsg << ", took " << refreshMillis << "ms)"; warning() << errMsg; return Status(ErrorCodes::RemoteChangeDetected, errMsg); } if (choice == ChunkVersion::VersionChoice_Local) { LOG(0) << "metadata of collection " << ns << " already up to date (shard version : " << afterShardVersion.toString() << ", took " << refreshMillis << "ms)"; return Status::OK(); } dassert(choice == ChunkVersion::VersionChoice_Remote); switch (installType) { case InstallType_New: LOG(0) << "collection " << ns << " was previously unsharded" << ", new metadata loaded with shard version " << remoteShardVersion; break; case InstallType_Update: LOG(0) << "updating metadata for " << ns << " from shard version " << localShardVersionMsg << " to shard version " << remoteShardVersion; break; case InstallType_Replace: LOG(0) << "replacing metadata for " << ns << " at shard version " << localShardVersionMsg << " with a new epoch (shard version " << remoteShardVersion << ")"; break; case InstallType_Drop: LOG(0) << "dropping metadata for " << ns << " at shard version " << localShardVersionMsg << ", took " << refreshMillis << "ms"; break; default: verify(false); break; } if (installType != InstallType_Drop) { LOG(0) << "collection version was loaded at version " << remoteCollVersion << ", took " << refreshMillis << "ms"; } return Status::OK(); }
void Function::loadProperties() { setPropertiesLoaded(false); wxString mechanismNames[] = { "value", "reference", "descriptor", "blob descriptor", "scalar array", "null", wxEmptyString }; wxString mechanismDDL[] = { " BY VALUE ", wxEmptyString, " BY DESCRIPTOR ", wxEmptyString, " BY SCALAR ARRAY ", " NULL ", wxEmptyString }; bool first = true; wxString retstr; definitionM = getName_() + "(" + wxTextBuffer::GetEOL(); paramListM = wxEmptyString; DatabasePtr db = getDatabase(); MetadataLoader* loader = db->getMetadataLoader(); wxMBConv* converter = db->getCharsetConverter(); MetadataLoaderTransaction tr(loader); IBPP::Statement& st1 = loader->getStatement( "SELECT f.RDB$RETURN_ARGUMENT, a.RDB$MECHANISM," " a.RDB$ARGUMENT_POSITION, a.RDB$FIELD_TYPE, a.RDB$FIELD_SCALE," " a.RDB$FIELD_LENGTH, a.RDB$FIELD_SUB_TYPE, a.RDB$FIELD_PRECISION," " f.RDB$MODULE_NAME, f.RDB$ENTRYPOINT, c.RDB$CHARACTER_SET_NAME " " FROM RDB$FUNCTIONS f" " LEFT OUTER JOIN RDB$FUNCTION_ARGUMENTS a" " ON f.RDB$FUNCTION_NAME = a.RDB$FUNCTION_NAME" " LEFT OUTER JOIN RDB$CHARACTER_SETS c" " ON a.RDB$CHARACTER_SET_ID = c.RDB$CHARACTER_SET_ID" " WHERE f.RDB$FUNCTION_NAME = ? " " ORDER BY a.RDB$ARGUMENT_POSITION" ); st1->Set(1, wx2std(getName_(), converter)); st1->Execute(); while (st1->Fetch()) { short returnarg, mechanism, type, scale, length, subtype, precision, retpos; std::string libraryName, entryPoint, charset; st1->Get(1, returnarg); st1->Get(2, mechanism); st1->Get(3, retpos); st1->Get(4, type); st1->Get(5, scale); st1->Get(6, length); st1->Get(7, subtype); st1->Get(8, precision); st1->Get(9, libraryName); libraryNameM = wxString(libraryName.c_str(), *converter).Strip(); st1->Get(10, entryPoint); entryPointM = wxString(entryPoint.c_str(), *converter).Strip(); wxString datatype = Domain::dataTypeToString(type, scale, precision, subtype, length); if (!st1->IsNull(11)) { st1->Get(11, charset); wxString chset = wxString(charset.c_str(), *converter).Strip(); if (db->getDatabaseCharset() != chset) { datatype += " " + SqlTokenizer::getKeyword(kwCHARACTER) + " " + SqlTokenizer::getKeyword(kwSET) + " " + chset; } } if (type == 261) // avoid subtype information for BLOB datatype = SqlTokenizer::getKeyword(kwBLOB); int mechIndex = (mechanism < 0 ? -mechanism : mechanism); if (mechIndex >= (sizeof(mechanismNames)/sizeof(wxString))) mechIndex = (sizeof(mechanismNames)/sizeof(wxString)) - 1; wxString param = " " + datatype + " " + SqlTokenizer::getKeyword(kwBY) + " " + mechanismNames[mechIndex]; if (mechanism < 0) param += wxString(" ") + SqlTokenizer::getKeyword(kwFREE_IT); if (returnarg == retpos) // output { retstr = param; retstrM = datatype + mechanismDDL[mechIndex]; if (retpos != 0) { retstrM = SqlTokenizer::getKeyword(kwPARAMETER) + " "; retstrM << retpos; if (!paramListM.IsEmpty()) paramListM += ", "; paramListM += datatype + mechanismDDL[mechIndex]; } } else { if (first) first = false; else definitionM += wxString(",") + wxTextBuffer::GetEOL(); definitionM += param; if (!paramListM.empty()) paramListM += ", "; paramListM += datatype + mechanismDDL[mechIndex]; } } definitionM += wxString(wxTextBuffer::GetEOL()) + ")" + wxTextBuffer::GetEOL() + SqlTokenizer::getKeyword(kwRETURNS) + ":" + wxTextBuffer::GetEOL() + retstr; setPropertiesLoaded(true); }
//! reads indices from database void Table::loadIndices() { if (indicesLoadedM) return; indicesM.clear(); DatabasePtr db = getDatabase(); wxMBConv* conv = db->getCharsetConverter(); MetadataLoader* loader = db->getMetadataLoader(); // first start a transaction for metadata loading, then lock the table // when objects go out of scope and are destroyed, table will be unlocked // before the transaction is committed - any update() calls on observers // can possibly use the same transaction MetadataLoaderTransaction tr(loader); SubjectLocker lock(this); IBPP::Statement& st1 = loader->getStatement( "SELECT i.rdb$index_name, i.rdb$unique_flag, i.rdb$index_inactive, " " i.rdb$index_type, i.rdb$statistics, " " s.rdb$field_name, rc.rdb$constraint_name, i.rdb$expression_source " " from rdb$indices i " " left join rdb$index_segments s on i.rdb$index_name = s.rdb$index_name " " left join rdb$relation_constraints rc " " on rc.rdb$index_name = i.rdb$index_name " " where i.rdb$relation_name = ? " " order by i.rdb$index_name, s.rdb$field_position " ); st1->Set(1, wx2std(getName_(), conv)); st1->Execute(); Index* i = 0; while (st1->Fetch()) { std::string s; st1->Get(1, s); wxString ixname(std2wxIdentifier(s, conv)); short unq, inactive, type; if (st1->IsNull(2)) // null = non-unique unq = 0; else st1->Get(2, unq); if (st1->IsNull(3)) // null = active inactive = 0; else st1->Get(3, inactive); if (st1->IsNull(4)) // null = ascending type = 0; else st1->Get(4, type); double statistics; if (st1->IsNull(5)) // this can happen, see bug #1825725 statistics = -1; else st1->Get(5, statistics); st1->Get(6, s); wxString fname(std2wxIdentifier(s, conv)); wxString expression; readBlob(st1, 8, expression, conv); if (i && i->getName_() == ixname) i->getSegments()->push_back(fname); else { Index x( unq == 1, inactive == 0, type == 0, statistics, !st1->IsNull(7), expression ); indicesM.push_back(x); i = &indicesM.back(); i->setName_(ixname); i->getSegments()->push_back(fname); i->setParent(this); } } indicesLoadedM = true; }
//! reads foreign keys info from database void Table::loadForeignKeys() { if (foreignKeysLoadedM) return; foreignKeysM.clear(); DatabasePtr db = getDatabase(); wxMBConv* conv = db->getCharsetConverter(); MetadataLoader* loader = db->getMetadataLoader(); // first start a transaction for metadata loading, then lock the table // when objects go out of scope and are destroyed, table will be unlocked // before the transaction is committed - any update() calls on observers // can possibly use the same transaction MetadataLoaderTransaction tr(loader); SubjectLocker lock(this); IBPP::Statement& st1 = loader->getStatement( "select r.rdb$constraint_name, i.rdb$field_name, c.rdb$update_rule, " " c.rdb$delete_rule, c.RDB$CONST_NAME_UQ, r.rdb$index_name " "from rdb$relation_constraints r, rdb$index_segments i, rdb$ref_constraints c " "where r.rdb$relation_name=? and r.rdb$index_name=i.rdb$index_name " "and r.rdb$constraint_name = c.rdb$constraint_name " "and (r.rdb$constraint_type='FOREIGN KEY') order by 1, i.rdb$field_position" ); IBPP::Statement& st2 = loader->getStatement( "select r.rdb$relation_name, i.rdb$field_name" " from rdb$relation_constraints r" " join rdb$index_segments i on i.rdb$index_name = r.rdb$index_name " " where r.rdb$constraint_name = ?" " order by i.rdb$field_position " ); st1->Set(1, wx2std(getName_(), conv)); st1->Execute(); ForeignKey *fkp = 0; while (st1->Fetch()) { std::string s; st1->Get(1, s); wxString cname(std2wxIdentifier(s, conv)); st1->Get(2, s); wxString fname(std2wxIdentifier(s, conv)); st1->Get(3, s); wxString update_rule(std2wxIdentifier(s, conv)); st1->Get(4, s); wxString delete_rule(std2wxIdentifier(s, conv)); std::string ref_constraint; st1->Get(5, ref_constraint); st1->Get(6, s); wxString ixname(std2wxIdentifier(s, conv)); if (fkp && fkp->getName_() == cname) // add column fkp->columnsM.push_back(fname); else { ForeignKey fk; foreignKeysM.push_back(fk); fkp = &foreignKeysM.back(); fkp->setName_(cname); fkp->setParent(this); fkp->updateActionM = update_rule; fkp->deleteActionM = delete_rule; fkp->indexNameM = ixname; st2->Set(1, ref_constraint); st2->Execute(); std::string rtable; while (st2->Fetch()) { st2->Get(1, rtable); st2->Get(2, s); fkp->referencedColumnsM.push_back(std2wxIdentifier(s, conv)); } fkp->referencedTableM = std2wxIdentifier(rtable, conv); fkp->columnsM.push_back(fname); } } foreignKeysLoadedM = true; }