diff --git a/examples/replication/fbSampleReplicator.cpp b/examples/replication/fbSampleReplicator.cpp index 8eac456b053..4f015ba736c 100644 --- a/examples/replication/fbSampleReplicator.cpp +++ b/examples/replication/fbSampleReplicator.cpp @@ -31,12 +31,28 @@ class ReplPlugin : public IReplicatedSessionImpl IReferenceCounted* getOwner() override; // IReplicatedSession implementation - IStatus* getStatus() override; - void setAttachment(IAttachment* attachment) override; - IReplicatedTransaction* startTransaction(ITransaction* transaction, ISC_INT64 number) override; - FB_BOOLEAN cleanupTransaction(ISC_INT64 number) override; - FB_BOOLEAN deprecatedSetSequence(const char* name, ISC_INT64 value) override; - FB_BOOLEAN setSequence2(const char* schemaName, const char* genName, ISC_INT64 value) override; + FB_BOOLEAN init(Firebird::CheckStatusWrapper* /*status*/, Firebird::IAttachment* attachment) override + { + WriteLog(log, "%p\tAssigned attachment %p\n", this, attachment); + att = attachment; + att->addRef(); + CheckStatusWrapper ExtStatus(status); + const unsigned char items[] = { fb_info_db_guid }; + unsigned char response[80]; + att->getInfo(&ExtStatus, sizeof(items), items, sizeof(response), response); + if (status->getState() == 0) + { + dumpInfo(response, sizeof(response)); + } + return FB_TRUE; + } + Firebird::IReplicatedTransaction* startTransaction(Firebird::CheckStatusWrapper* status, + Firebird::ITransaction* trans, ISC_INT64 number) override; + void cleanupTransaction(Firebird::CheckStatusWrapper* status, ISC_INT64 number) override; + void deprecatedSetSequence(Firebird::CheckStatusWrapper* status, const char* name, ISC_INT64 value) override; + void setSequence2(Firebird::CheckStatusWrapper* status, const char* schemaName, const char* genName, + ISC_INT64 value) override; + private: friend class ReplTransaction; @@ -59,23 +75,26 @@ class ReplTransaction: public IReplicatedTransactionImpladdRef(); - CheckStatusWrapper ExtStatus(status); - const unsigned char items[] = { fb_info_db_guid }; - unsigned char response[80]; - att->getInfo(&ExtStatus, sizeof(items), items, sizeof(response), response); - if (status->getState() == 0) - { - dumpInfo(response, sizeof(response)); - } -} - -IReplicatedTransaction* ReplPlugin::startTransaction(ITransaction* transaction, ISC_INT64 number) +IReplicatedTransaction* ReplPlugin::startTransaction(CheckStatusWrapper* status, + ITransaction* transaction, ISC_INT64 number) { WriteLog(log, "%p\tstartTransaction(%p, %lld)\n", this, transaction, number); return new ReplTransaction(this, transaction, number); } -FB_BOOLEAN ReplPlugin::cleanupTransaction(ISC_INT64 number) +void ReplPlugin::cleanupTransaction(CheckStatusWrapper* status, ISC_INT64 number) { WriteLog(log, "%p\tcleanupTransaction(%lld)\n", this, number); - return FB_TRUE; } -FB_BOOLEAN ReplPlugin::deprecatedSetSequence(const char* name, ISC_INT64 value) +void ReplPlugin::deprecatedSetSequence(CheckStatusWrapper* status, const char* genName, ISC_INT64 value) { - WriteLog(log, "%p\tdeprecatedSetSequence(%s, %lld)\n", this, name, value); - return FB_TRUE; + WriteLog(log, "%p\tdeprecatedSetSequence(%s, %lld)\n", this, genName, value); } -FB_BOOLEAN ReplPlugin::setSequence2(const char* schemaName, const char* genName, ISC_INT64 value) +void ReplPlugin::setSequence2(Firebird::CheckStatusWrapper* status, const char* schemaName, + const char* genName, ISC_INT64 value) { - WriteLog(log, "%p\tsetSequence2(%s.%s, %lld)\n", this, schemaName, genName, value); - return FB_TRUE; + WriteLog(log, "%p\tdeprecatedSetSequence(%s, %s, %lld)\n", this, schemaName, genName, value); } ReplTransaction::ReplTransaction(ReplPlugin* session, ITransaction* transaction, ISC_INT64 number): @@ -285,41 +284,35 @@ void ReplTransaction::dispose() delete this; } -FB_BOOLEAN ReplTransaction::prepare() +void ReplTransaction::prepare(CheckStatusWrapper* status) { WriteLog(parent->log, "%p\tprepare()\n", this); - return FB_TRUE; } -FB_BOOLEAN ReplTransaction::commit() +void ReplTransaction::commit(CheckStatusWrapper* status) { WriteLog(parent->log, "%p\tcommit()\n", this); - return FB_TRUE; } -FB_BOOLEAN ReplTransaction::rollback() +void ReplTransaction::rollback(CheckStatusWrapper* status) { WriteLog(parent->log, "%p\trollback()\n", this); parent->status->setWarnings(wrn); - return FB_FALSE; } -FB_BOOLEAN ReplTransaction::startSavepoint() +void ReplTransaction::startSavepoint(CheckStatusWrapper* status) { WriteLog(parent->log, "%p\tstartSavepoint()\n", this); - return FB_TRUE; } -FB_BOOLEAN ReplTransaction::releaseSavepoint() +void ReplTransaction::releaseSavepoint(CheckStatusWrapper* status) { WriteLog(parent->log, "%p\treleaseSavepoint()\n", this); - return FB_TRUE; } -FB_BOOLEAN ReplTransaction::rollbackSavepoint() +void ReplTransaction::rollbackSavepoint(CheckStatusWrapper* status) { WriteLog(parent->log, "%p\trollbackSavepoint()\n", this); - return FB_TRUE; } bool ReplTransaction::dumpData(IReplicatedRecord* record) @@ -336,6 +329,17 @@ bool ReplTransaction::dumpData(IReplicatedRecord* record) WriteLog(parent->log, "\tfield %u (%s), type %u:\n", i, field->getName(), fieldType); + unsigned int position; + FB_BOOLEAN isPrimary = field->isPrimary(&position); + + WriteLog(parent->log, "\tfield %u (%s), is primary %u, field position in the key %d\n", + i, field->getName(), isPrimary, position); + + FB_BOOLEAN isUnique = field->isUnique(&position); + + WriteLog(parent->log, "\tfield %u (%s), is unique %u, field position in the key %d\n", + i, field->getName(), isUnique, position); + const void* fieldData = field->getData(); if (fieldData == nullptr) { @@ -359,7 +363,8 @@ bool ReplTransaction::dumpData(IReplicatedRecord* record) WriteLog(parent->log, "\n"); } else - WriteLog(parent->log, "\t\tTEXT with charset %u, length %u: \"%.*s\"\n", charSet, length, length, reinterpret_cast(fieldData)); + WriteLog(parent->log, "\t\tTEXT with charset %u, length %u: \"%.*s\"\n", charSet, + length, length, reinterpret_cast(fieldData)); break; } @@ -376,13 +381,15 @@ bool ReplTransaction::dumpData(IReplicatedRecord* record) WriteLog(parent->log, "\n"); } else - WriteLog(parent->log, "\t\tVARCHAR with charset %u, length %u: \"%.*s\"\n", charSet, data->vary_length, data->vary_length, data->vary_string); + WriteLog(parent->log, "\t\tVARCHAR with charset %u, length %u: \"%.*s\"\n", charSet, + data->vary_length, data->vary_length, data->vary_string); break; } case SQL_SHORT: { - WriteLog(parent->log, "\t\tSMALLINT with scale %u: %d\n", field->getScale(), *reinterpret_cast(fieldData)); + WriteLog(parent->log, "\t\tSMALLINT with scale %u: %d\n", field->getScale(), + *reinterpret_cast(fieldData)); break; } case SQL_LONG: @@ -415,7 +422,7 @@ bool ReplTransaction::dumpData(IReplicatedRecord* record) if (ExtStatus.getState() & IStatus::STATE_ERRORS) return false; - char buffer[USHRT_MAX]; + char buffer[1024]; do { unsigned length; @@ -483,12 +490,14 @@ bool ReplTransaction::dumpData(IReplicatedRecord* record) unsigned year, month, day, hours, minutes, seconds, fractions; utl->decodeDate(value.timestamp_date, &year, &month, &day); utl->decodeTime(value.timestamp_time, &hours, &minutes, &seconds, &fractions); - WriteLog(parent->log, "\t\tTIMESTAMP: %04u-%02u-%02u %02u:%02u:%02u.%04u\n", year, month, day, hours, minutes, seconds, fractions); + WriteLog(parent->log, "\t\tTIMESTAMP: %04u-%02u-%02u %02u:%02u:%02u.%04u\n", + year, month, day, hours, minutes, seconds, fractions); break; } case SQL_INT64: { - WriteLog(parent->log, "\t\tBIGINT with scale %u: %lld\n", field->getScale(), *reinterpret_cast(fieldData)); + WriteLog(parent->log, "\t\tBIGINT with scale %u: %lld\n", field->getScale(), + *reinterpret_cast(fieldData)); break; } case SQL_BOOLEAN: @@ -519,114 +528,109 @@ bool ReplTransaction::dumpData(IReplicatedRecord* record) return true; } -FB_BOOLEAN ReplTransaction::deprecatedInsertRecord(const char* name, IReplicatedRecord* record) +void ReplTransaction::deprecatedInsertRecord(CheckStatusWrapper* status, const char* name, IReplicatedRecord* record) { - WriteLog(parent->log, "%p\tdeprecated Insert record into %s\n", this, name); + WriteLog(parent->log, "%p\tInsert record into %s\n", this, name); try { - return dumpData(record) ? FB_TRUE : FB_FALSE; + dumpData(record); } catch (const int) { parent->status->setErrors(err); - return FB_FALSE; } } -FB_BOOLEAN ReplTransaction::insertRecord2(const char* schemaName, const char* tableName, IReplicatedRecord* record) +void ReplTransaction::deprecatedUpdateRecord(CheckStatusWrapper* status, const char* name, + IReplicatedRecord* orgRecord, IReplicatedRecord* newRecord) { - WriteLog(parent->log, "%p\tInsert record into %s.%s\n", this, schemaName, tableName); + WriteLog(parent->log, "%p\tUpdate %s\nOldData:\n", this, name); try { - return dumpData(record) ? FB_TRUE : FB_FALSE; + if (!dumpData(orgRecord)) + return; + + WriteLog(parent->log, "NewData:\n"); + dumpData(newRecord); } catch (const int) { parent->status->setErrors(err); - return FB_FALSE; } } -FB_BOOLEAN ReplTransaction::deprecatedUpdateRecord(const char* name, - IReplicatedRecord* orgRecord, IReplicatedRecord* newRecord) +void ReplTransaction::deprecatedDeleteRecord(CheckStatusWrapper* status, const char* name, IReplicatedRecord* record) { - WriteLog(parent->log, "%p\tdeprecated Update %s\nOldData:\n", this, name); + WriteLog(parent->log, "%p\tDelete from %s\n", this, name); try { - if (!dumpData(orgRecord)) - return FB_FALSE; - - WriteLog(parent->log, "NewData:\n"); - return dumpData(newRecord) ? FB_TRUE : FB_FALSE; + dumpData(record); } catch (const int) { parent->status->setErrors(err); - return FB_FALSE; } } -FB_BOOLEAN ReplTransaction::updateRecord2(const char* schemaName, const char* tableName, - IReplicatedRecord* orgRecord, IReplicatedRecord* newRecord) +void ReplTransaction::deprecatedExecuteSql(CheckStatusWrapper* status, const char* sql) { - WriteLog(parent->log, "%p\tUpdate %s.%s\nOldData:\n", this, schemaName, tableName); + WriteLog(parent->log, "%p\tExecuteSql(%s)\n", this, sql); +} + +void ReplTransaction::deprecatedExecuteSqlIntl(CheckStatusWrapper* status, unsigned charset, const char* sql) +{ + WriteLog(parent->log, "%p\tExecuteSqlIntl(%u, %s)\n", this, charset, sql); +} + +void ReplTransaction::insertRecord2(CheckStatusWrapper* status, const char* schemaName, const char* tableName, + IReplicatedRecord* record) +{ + WriteLog(parent->log, "%p\tInsert record into %s.%s\n", this, schemaName, tableName); try { - if (!dumpData(orgRecord)) - return FB_FALSE; - - WriteLog(parent->log, "NewData:\n"); - return dumpData(newRecord) ? FB_TRUE : FB_FALSE; + dumpData(record); } catch (const int) { parent->status->setErrors(err); - return FB_FALSE; } } -FB_BOOLEAN ReplTransaction::deprecatedDeleteRecord(const char* name, IReplicatedRecord* record) + +void ReplTransaction::updateRecord2(CheckStatusWrapper* status, const char* schemaName, const char* tableName, + IReplicatedRecord* orgRecord, IReplicatedRecord* newRecord) { - WriteLog(parent->log, "%p\tdeprecated Delete from %s\n", this, name); + WriteLog(parent->log, "%p\tUpdate %s.%s\nOldData:\n", this, schemaName, tableName); try { - return dumpData(record) ? FB_TRUE : FB_FALSE; + if (!dumpData(orgRecord)) + return; + + WriteLog(parent->log, "NewData:\n"); + dumpData(newRecord); } catch (const int) { parent->status->setErrors(err); - return FB_FALSE; } } -FB_BOOLEAN ReplTransaction::deleteRecord2(const char* schemaName, const char* tableName, IReplicatedRecord* record) +void ReplTransaction::deleteRecord2(CheckStatusWrapper* status, const char* schemaName, const char* tableName, + IReplicatedRecord* record) { WriteLog(parent->log, "%p\tDelete from %s.%s\n", this, schemaName, tableName); try { - return dumpData(record) ? FB_TRUE : FB_FALSE; + dumpData(record); } catch (const int) { parent->status->setErrors(err); - return FB_FALSE; } } -FB_BOOLEAN ReplTransaction::deprecatedExecuteSql(const char* sql) -{ - WriteLog(parent->log, "%p\tdeprecatedExecuteSql(%s)\n", this, sql); - return FB_TRUE; -} - -FB_BOOLEAN ReplTransaction::deprecatedExecuteSqlIntl(unsigned charset, const char* sql) -{ - WriteLog(parent->log, "%p\tdeprecatedExecuteSqlIntl(%u, %s)\n", this, charset, sql); - return FB_TRUE; -} - -FB_BOOLEAN ReplTransaction::executeSqlIntl2(unsigned charset, const char* schemaSearchPath, const char* sql) +void ReplTransaction::executeSqlIntl2(CheckStatusWrapper* status, unsigned charset, const char* schemaSearchPath, + const char* sql) { WriteLog(parent->log, "%p\tExecuteSqlIntl2(%u, %s, %s)\n", this, charset, schemaSearchPath, sql); - return FB_TRUE; } diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 97a2149574f..5e0a0d9e7e5 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -1814,6 +1814,10 @@ interface ReplicatedField : Versioned uint getCharSet(); // Return pointer to data (or nullptr for NULL values) const void* getData(); + +version: // 5.0 => 6.0 Alpha1 + boolean isPrimary(uint* position); + boolean isUnique(uint* position); } interface ReplicatedRecord : Versioned diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index ea7b89c20d0..270182e62b1 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -7217,7 +7217,7 @@ namespace Firebird } }; -#define FIREBIRD_IREPLICATED_FIELD_VERSION 2u +#define FIREBIRD_IREPLICATED_FIELD_VERSION 3u class IReplicatedField : public IVersioned { @@ -7231,6 +7231,8 @@ namespace Firebird unsigned (CLOOP_CARG *getLength)(IReplicatedField* self) CLOOP_NOEXCEPT; unsigned (CLOOP_CARG *getCharSet)(IReplicatedField* self) CLOOP_NOEXCEPT; const void* (CLOOP_CARG *getData)(IReplicatedField* self) CLOOP_NOEXCEPT; + FB_BOOLEAN (CLOOP_CARG *isPrimary)(IReplicatedField* self, unsigned* position) CLOOP_NOEXCEPT; + FB_BOOLEAN (CLOOP_CARG *isUnique)(IReplicatedField* self, unsigned* position) CLOOP_NOEXCEPT; }; protected: @@ -7287,6 +7289,26 @@ namespace Firebird const void* ret = static_cast(this->cloopVTable)->getData(this); return ret; } + + FB_BOOLEAN isPrimary(unsigned* position) + { + if (cloopVTable->version < 3) + { + return 0; + } + FB_BOOLEAN ret = static_cast(this->cloopVTable)->isPrimary(this, position); + return ret; + } + + FB_BOOLEAN isUnique(unsigned* position) + { + if (cloopVTable->version < 3) + { + return 0; + } + FB_BOOLEAN ret = static_cast(this->cloopVTable)->isUnique(this, position); + return ret; + } }; #define FIREBIRD_IREPLICATED_RECORD_VERSION 2u @@ -21362,6 +21384,8 @@ namespace Firebird this->getLength = &Name::cloopgetLengthDispatcher; this->getCharSet = &Name::cloopgetCharSetDispatcher; this->getData = &Name::cloopgetDataDispatcher; + this->isPrimary = &Name::cloopisPrimaryDispatcher; + this->isUnique = &Name::cloopisUniqueDispatcher; } } vTable; @@ -21458,6 +21482,32 @@ namespace Firebird return static_cast(0); } } + + static FB_BOOLEAN CLOOP_CARG cloopisPrimaryDispatcher(IReplicatedField* self, unsigned* position) CLOOP_NOEXCEPT + { + try + { + return static_cast(self)->Name::isPrimary(position); + } + catch (...) + { + StatusType::catchException(0); + return static_cast(0); + } + } + + static FB_BOOLEAN CLOOP_CARG cloopisUniqueDispatcher(IReplicatedField* self, unsigned* position) CLOOP_NOEXCEPT + { + try + { + return static_cast(self)->Name::isUnique(position); + } + catch (...) + { + StatusType::catchException(0); + return static_cast(0); + } + } }; template > > @@ -21480,6 +21530,8 @@ namespace Firebird virtual unsigned getLength() = 0; virtual unsigned getCharSet() = 0; virtual const void* getData() = 0; + virtual FB_BOOLEAN isPrimary(unsigned* position) = 0; + virtual FB_BOOLEAN isUnique(unsigned* position) = 0; }; template diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 10b376125fd..bb3089d53ff 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -749,6 +749,8 @@ ISC_TIMESTAMP_TZ_EX = record IReplicatedField_getLengthPtr = function(this: IReplicatedField): Cardinal; cdecl; IReplicatedField_getCharSetPtr = function(this: IReplicatedField): Cardinal; cdecl; IReplicatedField_getDataPtr = function(this: IReplicatedField): Pointer; cdecl; + IReplicatedField_isPrimaryPtr = function(this: IReplicatedField; position: CardinalPtr): Boolean; cdecl; + IReplicatedField_isUniquePtr = function(this: IReplicatedField; position: CardinalPtr): Boolean; cdecl; IReplicatedRecord_getCountPtr = function(this: IReplicatedRecord): Cardinal; cdecl; IReplicatedRecord_getFieldPtr = function(this: IReplicatedRecord; index: Cardinal): IReplicatedField; cdecl; IReplicatedRecord_getRawLengthPtr = function(this: IReplicatedRecord): Cardinal; cdecl; @@ -3909,10 +3911,12 @@ ReplicatedFieldVTable = class(VersionedVTable) getLength: IReplicatedField_getLengthPtr; getCharSet: IReplicatedField_getCharSetPtr; getData: IReplicatedField_getDataPtr; + isPrimary: IReplicatedField_isPrimaryPtr; + isUnique: IReplicatedField_isUniquePtr; end; IReplicatedField = class(IVersioned) - const VERSION = 2; + const VERSION = 3; function getName(): PAnsiChar; function getType(): Cardinal; @@ -3921,6 +3925,8 @@ IReplicatedField = class(IVersioned) function getLength(): Cardinal; function getCharSet(): Cardinal; function getData(): Pointer; + function isPrimary(position: CardinalPtr): Boolean; + function isUnique(position: CardinalPtr): Boolean; end; IReplicatedFieldImpl = class(IReplicatedField) @@ -3933,6 +3939,8 @@ IReplicatedFieldImpl = class(IReplicatedField) function getLength(): Cardinal; virtual; abstract; function getCharSet(): Cardinal; virtual; abstract; function getData(): Pointer; virtual; abstract; + function isPrimary(position: CardinalPtr): Boolean; virtual; abstract; + function isUnique(position: CardinalPtr): Boolean; virtual; abstract; end; ReplicatedRecordVTable = class(VersionedVTable) @@ -10172,6 +10180,26 @@ function IReplicatedField.getData(): Pointer; Result := ReplicatedFieldVTable(vTable).getData(Self); end; +function IReplicatedField.isPrimary(position: CardinalPtr): Boolean; +begin + if (vTable.version < 3) then begin + Result := false; + end + else begin + Result := ReplicatedFieldVTable(vTable).isPrimary(Self, position); + end; +end; + +function IReplicatedField.isUnique(position: CardinalPtr): Boolean; +begin + if (vTable.version < 3) then begin + Result := false; + end + else begin + Result := ReplicatedFieldVTable(vTable).isUnique(Self, position); + end; +end; + function IReplicatedRecord.getCount(): Cardinal; begin Result := ReplicatedRecordVTable(vTable).getCount(Self); @@ -17653,6 +17681,26 @@ function IReplicatedFieldImpl_getDataDispatcher(this: IReplicatedField): Pointer end end; +function IReplicatedFieldImpl_isPrimaryDispatcher(this: IReplicatedField; position: CardinalPtr): Boolean; cdecl; +begin + Result := false; + try + Result := IReplicatedFieldImpl(this).isPrimary(position); + except + on e: Exception do FbException.catchException(nil, e); + end +end; + +function IReplicatedFieldImpl_isUniqueDispatcher(this: IReplicatedField; position: CardinalPtr): Boolean; cdecl; +begin + Result := false; + try + Result := IReplicatedFieldImpl(this).isUnique(position); + except + on e: Exception do FbException.catchException(nil, e); + end +end; + var IReplicatedFieldImpl_vTable: ReplicatedFieldVTable; @@ -19311,7 +19359,7 @@ initialization IInt128Impl_vTable.fromString := @IInt128Impl_fromStringDispatcher; IReplicatedFieldImpl_vTable := ReplicatedFieldVTable.create; - IReplicatedFieldImpl_vTable.version := 2; + IReplicatedFieldImpl_vTable.version := 3; IReplicatedFieldImpl_vTable.getName := @IReplicatedFieldImpl_getNameDispatcher; IReplicatedFieldImpl_vTable.getType := @IReplicatedFieldImpl_getTypeDispatcher; IReplicatedFieldImpl_vTable.getSubType := @IReplicatedFieldImpl_getSubTypeDispatcher; @@ -19319,6 +19367,8 @@ initialization IReplicatedFieldImpl_vTable.getLength := @IReplicatedFieldImpl_getLengthDispatcher; IReplicatedFieldImpl_vTable.getCharSet := @IReplicatedFieldImpl_getCharSetDispatcher; IReplicatedFieldImpl_vTable.getData := @IReplicatedFieldImpl_getDataDispatcher; + IReplicatedFieldImpl_vTable.isPrimary := @IReplicatedFieldImpl_isPrimaryDispatcher; + IReplicatedFieldImpl_vTable.isUnique := @IReplicatedFieldImpl_isUniqueDispatcher; IReplicatedRecordImpl_vTable := ReplicatedRecordVTable.create; IReplicatedRecordImpl_vTable.version := 2; diff --git a/src/jrd/replication/Publisher.cpp b/src/jrd/replication/Publisher.cpp index 5a2505c4df3..cfc24c17e53 100644 --- a/src/jrd/replication/Publisher.cpp +++ b/src/jrd/replication/Publisher.cpp @@ -31,8 +31,11 @@ #include "../jrd/trig.h" #include "../jrd/Database.h" #include "../jrd/blb_proto.h" +#include "../jrd/btr_proto.h" #include "../jrd/cch_proto.h" +#include "../jrd/dpm_proto.h" #include "../jrd/evl_proto.h" +#include "../jrd/idx_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../common/isc_proto.h" @@ -297,9 +300,10 @@ namespace public IReplicatedFieldImpl { public: - ReplicatedRecordImpl(thread_db* tdbb, const jrd_rel* relation, const Record* record) + ReplicatedRecordImpl(thread_db* tdbb, jrd_rel* relation, const Record* record) : m_record(record), - m_relation(relation) + m_relation(relation), + m_tdbb(tdbb) { } @@ -352,6 +356,16 @@ namespace return m_record->getData() + (IPTR) m_desc->dsc_address; } + FB_BOOLEAN isPrimary(unsigned* position) override + { + return isKey(idx_primary, position); + } + + FB_BOOLEAN isUnique(unsigned* position) override + { + return isKey(idx_unique, position); + } + IReplicatedField* getField(unsigned index) override { const auto format = m_record->getFormat(); @@ -384,11 +398,47 @@ namespace private: const Record* const m_record; - const jrd_rel* const m_relation; + jrd_rel* const m_relation; const dsc* m_desc = nullptr; // optimization unsigned m_fieldIndex = 0; SLONG m_fieldLength = 0; SLONG m_fieldType = 0; + + std::optional m_idxList; + thread_db* const m_tdbb; + + FB_BOOLEAN isKey(int flag, unsigned* position) + { + if (!m_idxList.has_value()) + { + m_idxList.emplace(); + const auto relPages = m_relation->getPages(m_tdbb); + BTR_all(m_tdbb, m_relation, *m_idxList, relPages); + } + + if (position) + *position = idx_invalid; + + for (const auto& idx : m_idxList.value()) + { + if (idx.idx_id != idx_invalid && idx.idx_flags & flag) + { + for (int fld = 0; fld < idx.idx_count; fld++) + { + const int fldNum = idx.idx_rpt[fld].idx_field; + if (m_fieldIndex == fldNum) + { + if (position) + *position = fld; + + return FB_TRUE; + } + } + } + } + + return FB_FALSE; + } }; }