summaryrefslogtreecommitdiff
path: root/src/md/enc/metamodelenc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/md/enc/metamodelenc.cpp')
-rw-r--r--src/md/enc/metamodelenc.cpp471
1 files changed, 471 insertions, 0 deletions
diff --git a/src/md/enc/metamodelenc.cpp b/src/md/enc/metamodelenc.cpp
new file mode 100644
index 0000000000..4d972827ca
--- /dev/null
+++ b/src/md/enc/metamodelenc.cpp
@@ -0,0 +1,471 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// MetaModelENC.cpp
+//
+
+//
+// Implementation for applying ENC deltas to a MiniMd.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include <limits.h>
+#include <posterror.h>
+#include <metamodelrw.h>
+#include <stgio.h>
+#include <stgtiggerstorage.h>
+#include "mdlog.h"
+#include "rwutil.h"
+
+ULONG CMiniMdRW::m_SuppressedDeltaColumns[TBL_COUNT] = {0};
+
+//*****************************************************************************
+// Copy the data from one MiniMd to another.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyRecordDelta(
+ CMiniMdRW &mdDelta, // The delta MetaData.
+ ULONG ixTbl, // The table with the data.
+ void *pDelta, // The delta MetaData record.
+ void *pRecord) // The record to update.
+{
+ HRESULT hr = S_OK;
+ ULONG mask = m_SuppressedDeltaColumns[ixTbl];
+
+ for (ULONG ixCol = 0; ixCol<m_TableDefs[ixTbl].m_cCols; ++ixCol, mask >>= 1)
+ { // Skip certain pointer columns.
+ if (mask & 0x01)
+ continue;
+
+ ULONG val = mdDelta.GetCol(ixTbl, ixCol, pDelta);
+ IfFailRet(PutCol(ixTbl, ixCol, pRecord, val));
+ }
+ return hr;
+} // CMiniMdRW::ApplyRecordDelta
+
+//*****************************************************************************
+// Apply a delta record to a table, generically.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyTableDelta(
+ CMiniMdRW &mdDelta, // Interface to MD with the ENC delta.
+ ULONG ixTbl, // Table index to update.
+ RID iRid, // RID of the changed item.
+ int fc) // Function code of update.
+{
+ HRESULT hr = S_OK;
+ void *pRec; // Record in existing MetaData.
+ void *pDeltaRec; // Record if Delta MetaData.
+ RID newRid; // Rid of new record.
+
+ // Get the delta record.
+ IfFailGo(mdDelta.GetDeltaRecord(ixTbl, iRid, &pDeltaRec));
+ // Get the record from the base metadata.
+ if (iRid > m_Schema.m_cRecs[ixTbl])
+ { // Added record. Each addition is the next one.
+ _ASSERTE(iRid == m_Schema.m_cRecs[ixTbl] + 1);
+ switch (ixTbl)
+ {
+ case TBL_TypeDef:
+ IfFailGo(AddTypeDefRecord(reinterpret_cast<TypeDefRec **>(&pRec), &newRid));
+ break;
+ case TBL_Method:
+ IfFailGo(AddMethodRecord(reinterpret_cast<MethodRec **>(&pRec), &newRid));
+ break;
+ case TBL_EventMap:
+ IfFailGo(AddEventMapRecord(reinterpret_cast<EventMapRec **>(&pRec), &newRid));
+ break;
+ case TBL_PropertyMap:
+ IfFailGo(AddPropertyMapRecord(reinterpret_cast<PropertyMapRec **>(&pRec), &newRid));
+ break;
+ default:
+ IfFailGo(AddRecord(ixTbl, &pRec, &newRid));
+ break;
+ }
+ IfNullGo(pRec);
+ _ASSERTE(iRid == newRid);
+ }
+ else
+ { // Updated record.
+ IfFailGo(getRow(ixTbl, iRid, &pRec));
+ }
+
+ // Copy the record info.
+ IfFailGo(ApplyRecordDelta(mdDelta, ixTbl, pDeltaRec, pRec));
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ApplyTableDelta
+
+//*****************************************************************************
+// Get the record from a Delta MetaData that corresponds to the actual record.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::GetDeltaRecord(
+ ULONG ixTbl, // Table.
+ ULONG iRid, // Record in the table.
+ void **ppRecord)
+{
+ HRESULT hr;
+ ULONG iMap; // RID in map table.
+ ENCMapRec *pMap; // Row in map table.
+
+ *ppRecord = NULL;
+ // If no remap, just return record directly.
+ if ((m_Schema.m_cRecs[TBL_ENCMap] == 0) || (ixTbl == TBL_Module) || !IsMinimalDelta())
+ {
+ return getRow(ixTbl, iRid, ppRecord);
+ }
+
+ // Use the remap table to find the physical row containing this logical row.
+ iMap = (*m_rENCRecs)[ixTbl];
+ IfFailRet(GetENCMapRecord(iMap, &pMap));
+
+ // Search for desired record.
+ while ((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) < iRid))
+ {
+ IfFailRet(GetENCMapRecord(++iMap, &pMap));
+ }
+
+ _ASSERTE((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) == iRid));
+
+ // Relative position within table's group in map is physical rid.
+ iRid = iMap - (*m_rENCRecs)[ixTbl] + 1;
+
+ return getRow(ixTbl, iRid, ppRecord);
+} // CMiniMdRW::GetDeltaRecord
+
+//*****************************************************************************
+// Given a MetaData with ENC changes, apply those changes to this MetaData.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyHeapDeltas(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ if (mdDelta.IsMinimalDelta())
+ {
+ return ApplyHeapDeltasWithMinimalDelta(mdDelta);
+ }
+ else
+ {
+ return ApplyHeapDeltasWithFullDelta(mdDelta);
+ }
+}// CMiniMdRW::ApplyHeapDeltas
+
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyHeapDeltasWithMinimalDelta(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ HRESULT hr = S_OK;
+
+ // Extend the heaps with EnC minimal delta
+ IfFailGo(m_StringHeap.AddStringHeap(
+ &(mdDelta.m_StringHeap),
+ 0)); // Start offset in the mdDelta
+ IfFailGo(m_BlobHeap.AddBlobHeap(
+ &(mdDelta.m_BlobHeap),
+ 0)); // Start offset in the mdDelta
+ IfFailGo(m_UserStringHeap.AddBlobHeap(
+ &(mdDelta.m_UserStringHeap),
+ 0)); // Start offset in the mdDelta
+ // We never do a minimal delta with the guid heap
+ IfFailGo(m_GuidHeap.AddGuidHeap(
+ &(mdDelta.m_GuidHeap),
+ m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::ApplyHeapDeltasWithMinimalDelta
+
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyHeapDeltasWithFullDelta(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ HRESULT hr = S_OK;
+
+ // Extend the heaps with EnC full delta
+ IfFailRet(m_StringHeap.AddStringHeap(
+ &(mdDelta.m_StringHeap),
+ m_StringHeap.GetUnalignedSize())); // Starting offset in the full delta string heap
+ IfFailRet(m_BlobHeap.AddBlobHeap(
+ &(mdDelta.m_BlobHeap),
+ m_BlobHeap.GetUnalignedSize())); // Starting offset in the full delta blob heap
+ IfFailRet(m_UserStringHeap.AddBlobHeap(
+ &(mdDelta.m_UserStringHeap),
+ m_UserStringHeap.GetUnalignedSize())); // Starting offset in the full delta user string heap
+ IfFailRet(m_GuidHeap.AddGuidHeap(
+ &(mdDelta.m_GuidHeap),
+ m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap
+
+ return hr;
+} // CMiniMdRW::ApplyHeapDeltasWithFullDelta
+
+//*****************************************************************************
+// Driver for the delta process.
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::ApplyDelta(
+ CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
+{
+ HRESULT hr = S_OK;
+ ULONG iENC; // Loop control.
+ ULONG iRid; // RID of some record.
+ ULONG iNew; // RID of a new record.
+ int i; // Loop control.
+ ULONG ixTbl; // A table.
+
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_ApplyDeltaBreak))
+ {
+ _ASSERTE(!"CMiniMDRW::ApplyDelta()");
+ }
+#endif // _DEBUG
+
+ // Init the suppressed column table. We know this one isn't zero...
+ if (m_SuppressedDeltaColumns[TBL_TypeDef] == 0)
+ {
+ m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList);
+ m_SuppressedDeltaColumns[TBL_PropertyMap] = (1 << PropertyMapRec::COL_PropertyList);
+ m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList);
+ m_SuppressedDeltaColumns[TBL_Method] = (1 << MethodRec::COL_ParamList);
+ m_SuppressedDeltaColumns[TBL_TypeDef] = (1 << TypeDefRec::COL_FieldList)|(1<<TypeDefRec::COL_MethodList);
+ }
+
+ // Verify the version of the MD.
+ if (m_Schema.m_major != mdDelta.m_Schema.m_major ||
+ m_Schema.m_minor != mdDelta.m_Schema.m_minor)
+ {
+ _ASSERTE(!"Version of Delta MetaData is a incompatible with current MetaData.");
+ //<TODO>@FUTURE: unique error in the future since we are not shipping ENC.</TODO>
+ return E_INVALIDARG;
+ }
+
+ // verify MVIDs.
+ ModuleRec *pModDelta;
+ ModuleRec *pModBase;
+ IfFailGo(mdDelta.GetModuleRecord(1, &pModDelta));
+ IfFailGo(GetModuleRecord(1, &pModBase));
+ GUID GuidDelta;
+ GUID GuidBase;
+ IfFailGo(mdDelta.getMvidOfModule(pModDelta, &GuidDelta));
+ IfFailGo(getMvidOfModule(pModBase, &GuidBase));
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) && (GuidDelta != GuidBase))
+ {
+ _ASSERTE(!"Delta MetaData has different base than current MetaData.");
+ return E_INVALIDARG;
+ }
+
+#ifndef FEATURE_CORECLR
+ // Verify that the delta is based on the base.
+ IfFailGo(mdDelta.getEncBaseIdOfModule(pModDelta, &GuidDelta));
+ IfFailGo(getEncBaseIdOfModule(pModBase,&GuidBase));
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) &&
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas) &&
+ (GuidDelta != GuidBase))
+ {
+ _ASSERTE(!"The Delta MetaData is based on a different generation than the current MetaData.");
+ return E_INVALIDARG;
+ }
+#endif //!FEATURE_CORECLR
+
+ // Let the other md prepare for sparse records.
+ IfFailGo(mdDelta.StartENCMap());
+
+ // Fix the heaps.
+ IfFailGo(ApplyHeapDeltas(mdDelta));
+
+ // Truncate some tables in preparation to copy in new ENCLog data.
+ for (i = 0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG)-1; ++i)
+ {
+ m_Tables[ixTbl].Delete();
+ IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount(
+ m_TableDefs[ixTbl].m_cbRec,
+ mdDelta.m_Schema.m_cRecs[ixTbl]
+ COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
+ INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
+ m_Schema.m_cRecs[ixTbl] = 0;
+ }
+
+ // For each record in the ENC log...
+ for (iENC = 1; iENC <= mdDelta.m_Schema.m_cRecs[TBL_ENCLog]; ++iENC)
+ {
+ // Get the record, and the updated token.
+ ENCLogRec *pENC;
+ IfFailGo(mdDelta.GetENCLogRecord(iENC, &pENC));
+ ENCLogRec *pENC2;
+ IfFailGo(AddENCLogRecord(&pENC2, &iNew));
+ IfNullGo(pENC2);
+ ENCLogRec *pENC3;
+ _ASSERTE(iNew == iENC);
+ ULONG func = pENC->GetFuncCode();
+ pENC2->SetFuncCode(pENC->GetFuncCode());
+ pENC2->SetToken(pENC->GetToken());
+
+ // What kind of record is this?
+ if (IsRecId(pENC->GetToken()))
+ { // Non-token table
+ iRid = RidFromRecId(pENC->GetToken());
+ ixTbl = TblFromRecId(pENC->GetToken());
+ }
+ else
+ { // Token table.
+ iRid = RidFromToken(pENC->GetToken());
+ ixTbl = GetTableForToken(pENC->GetToken());
+ }
+
+ RID rid_Ignore;
+ // Switch based on the function code.
+ switch (func)
+ {
+ case eDeltaMethodCreate:
+ // Next ENC record will define the new Method.
+ MethodRec *pMethodRecord;
+ IfFailGo(AddMethodRecord(&pMethodRecord, &rid_Ignore));
+ IfFailGo(AddMethodToTypeDef(iRid, m_Schema.m_cRecs[TBL_Method]));
+ break;
+
+ case eDeltaParamCreate:
+ // Next ENC record will define the new Param. This record is
+ // tricky because params will be re-ordered based on their sequence,
+ // but the sequence isn't set until the NEXT record is applied.
+ // So, for ParamCreate only, apply the param record delta before
+ // adding the parent-child linkage.
+ ParamRec *pParamRecord;
+ IfFailGo(AddParamRecord(&pParamRecord, &rid_Ignore));
+
+ // Should have recorded a Param delta after the Param add.
+ _ASSERTE(iENC<mdDelta.m_Schema.m_cRecs[TBL_ENCLog]);
+ IfFailGo(mdDelta.GetENCLogRecord(iENC+1, &pENC3));
+ _ASSERTE(pENC3->GetFuncCode() == 0);
+ _ASSERTE(GetTableForToken(pENC3->GetToken()) == TBL_Param);
+ IfFailGo(ApplyTableDelta(mdDelta, TBL_Param, RidFromToken(pENC3->GetToken()), eDeltaFuncDefault));
+
+ // Now that Param record is OK, set up linkage.
+ IfFailGo(AddParamToMethod(iRid, m_Schema.m_cRecs[TBL_Param]));
+ break;
+
+ case eDeltaFieldCreate:
+ // Next ENC record will define the new Field.
+ FieldRec *pFieldRecord;
+ IfFailGo(AddFieldRecord(&pFieldRecord, &rid_Ignore));
+ IfFailGo(AddFieldToTypeDef(iRid, m_Schema.m_cRecs[TBL_Field]));
+ break;
+
+ case eDeltaPropertyCreate:
+ // Next ENC record will define the new Property.
+ PropertyRec *pPropertyRecord;
+ IfFailGo(AddPropertyRecord(&pPropertyRecord, &rid_Ignore));
+ IfFailGo(AddPropertyToPropertyMap(iRid, m_Schema.m_cRecs[TBL_Property]));
+ break;
+
+ case eDeltaEventCreate:
+ // Next ENC record will define the new Event.
+ EventRec *pEventRecord;
+ IfFailGo(AddEventRecord(&pEventRecord, &rid_Ignore));
+ IfFailGo(AddEventToEventMap(iRid, m_Schema.m_cRecs[TBL_Event]));
+ break;
+
+ case eDeltaFuncDefault:
+ IfFailGo(ApplyTableDelta(mdDelta, ixTbl, iRid, func));
+ break;
+
+ default:
+ _ASSERTE(!"Unexpected function in ApplyDelta");
+ IfFailGo(E_UNEXPECTED);
+ break;
+ }
+ }
+ m_Schema.m_cRecs[TBL_ENCLog] = mdDelta.m_Schema.m_cRecs[TBL_ENCLog];
+
+ErrExit:
+ // Store the result for returning (IfFailRet will modify hr)
+ HRESULT hrReturn = hr;
+ IfFailRet(mdDelta.EndENCMap());
+
+#ifndef FEATURE_CORECLR
+ if (SUCCEEDED(hrReturn) &&
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) &&
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas))
+ {
+ GUID GuidNewBase;
+
+ // We'll use the delta's 'delta guid' for our new base guid
+ IfFailRet(mdDelta.getEncIdOfModule(pModDelta, &GuidNewBase));
+
+ IfFailRet(PutGuid(TBL_Module, ModuleRec::COL_EncBaseId, pModBase, GuidNewBase));
+ }
+#endif //!FEATURE_CORECLR
+
+ return hrReturn;
+} // CMiniMdRW::ApplyDelta
+
+//*****************************************************************************
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::StartENCMap() // S_OK or error.
+{
+ HRESULT hr = S_OK;
+ ULONG iENC; // Loop control.
+ ULONG ixTbl; // A table.
+ int ixTblPrev = -1; // Table previously seen.
+
+ _ASSERTE(m_rENCRecs == 0);
+
+ if (m_Schema.m_cRecs[TBL_ENCMap] == 0)
+ return S_OK;
+
+ // Build an array of pointers into the ENCMap table for fast access to the ENCMap
+ // for each table.
+ m_rENCRecs = new (nothrow) ULONGARRAY;
+ IfNullGo(m_rENCRecs);
+ if (!m_rENCRecs->AllocateBlock(TBL_COUNT))
+ IfFailGo(E_OUTOFMEMORY);
+ for (iENC = 1; iENC <= m_Schema.m_cRecs[TBL_ENCMap]; ++iENC)
+ {
+ ENCMapRec *pMap;
+ IfFailGo(GetENCMapRecord(iENC, &pMap));
+ ixTbl = TblFromRecId(pMap->GetToken());
+ _ASSERTE((int)ixTbl >= ixTblPrev);
+ _ASSERTE(ixTbl < TBL_COUNT);
+ _ASSERTE(ixTbl != TBL_ENCMap);
+ _ASSERTE(ixTbl != TBL_ENCLog);
+ if ((int)ixTbl == ixTblPrev)
+ continue;
+ // Catch up on any skipped tables.
+ while (ixTblPrev < (int)ixTbl)
+ {
+ (*m_rENCRecs)[++ixTblPrev] = iENC;
+ }
+ }
+ while (ixTblPrev < TBL_COUNT-1)
+ {
+ (*m_rENCRecs)[++ixTblPrev] = iENC;
+ }
+
+ErrExit:
+ return hr;
+} // CMiniMdRW::StartENCMap
+
+//*****************************************************************************
+//*****************************************************************************
+__checkReturn
+HRESULT
+CMiniMdRW::EndENCMap()
+{
+ if (m_rENCRecs != NULL)
+ {
+ delete m_rENCRecs;
+ m_rENCRecs = NULL;
+ }
+
+ return S_OK;
+} // CMiniMdRW::EndENCMap