1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// MetaModelENC.cpp
6//
7
8//
9// Implementation for applying ENC deltas to a MiniMd.
10//
11//*****************************************************************************
12#include "stdafx.h"
13#include <limits.h>
14#include <posterror.h>
15#include <metamodelrw.h>
16#include <stgio.h>
17#include <stgtiggerstorage.h>
18#include "mdlog.h"
19#include "rwutil.h"
20
21ULONG CMiniMdRW::m_SuppressedDeltaColumns[TBL_COUNT] = {0};
22
23//*****************************************************************************
24// Copy the data from one MiniMd to another.
25//*****************************************************************************
26__checkReturn
27HRESULT
28CMiniMdRW::ApplyRecordDelta(
29 CMiniMdRW &mdDelta, // The delta MetaData.
30 ULONG ixTbl, // The table with the data.
31 void *pDelta, // The delta MetaData record.
32 void *pRecord) // The record to update.
33{
34 HRESULT hr = S_OK;
35 ULONG mask = m_SuppressedDeltaColumns[ixTbl];
36
37 for (ULONG ixCol = 0; ixCol<m_TableDefs[ixTbl].m_cCols; ++ixCol, mask >>= 1)
38 { // Skip certain pointer columns.
39 if (mask & 0x01)
40 continue;
41
42 ULONG val = mdDelta.GetCol(ixTbl, ixCol, pDelta);
43 IfFailRet(PutCol(ixTbl, ixCol, pRecord, val));
44 }
45 return hr;
46} // CMiniMdRW::ApplyRecordDelta
47
48//*****************************************************************************
49// Apply a delta record to a table, generically.
50//*****************************************************************************
51__checkReturn
52HRESULT
53CMiniMdRW::ApplyTableDelta(
54 CMiniMdRW &mdDelta, // Interface to MD with the ENC delta.
55 ULONG ixTbl, // Table index to update.
56 RID iRid, // RID of the changed item.
57 int fc) // Function code of update.
58{
59 HRESULT hr = S_OK;
60 void *pRec; // Record in existing MetaData.
61 void *pDeltaRec; // Record if Delta MetaData.
62 RID newRid; // Rid of new record.
63
64 // Get the delta record.
65 IfFailGo(mdDelta.GetDeltaRecord(ixTbl, iRid, &pDeltaRec));
66 // Get the record from the base metadata.
67 if (iRid > m_Schema.m_cRecs[ixTbl])
68 { // Added record. Each addition is the next one.
69 _ASSERTE(iRid == m_Schema.m_cRecs[ixTbl] + 1);
70 switch (ixTbl)
71 {
72 case TBL_TypeDef:
73 IfFailGo(AddTypeDefRecord(reinterpret_cast<TypeDefRec **>(&pRec), &newRid));
74 break;
75 case TBL_Method:
76 IfFailGo(AddMethodRecord(reinterpret_cast<MethodRec **>(&pRec), &newRid));
77 break;
78 case TBL_EventMap:
79 IfFailGo(AddEventMapRecord(reinterpret_cast<EventMapRec **>(&pRec), &newRid));
80 break;
81 case TBL_PropertyMap:
82 IfFailGo(AddPropertyMapRecord(reinterpret_cast<PropertyMapRec **>(&pRec), &newRid));
83 break;
84 default:
85 IfFailGo(AddRecord(ixTbl, &pRec, &newRid));
86 break;
87 }
88 IfNullGo(pRec);
89 _ASSERTE(iRid == newRid);
90 }
91 else
92 { // Updated record.
93 IfFailGo(getRow(ixTbl, iRid, &pRec));
94 }
95
96 // Copy the record info.
97 IfFailGo(ApplyRecordDelta(mdDelta, ixTbl, pDeltaRec, pRec));
98
99ErrExit:
100 return hr;
101} // CMiniMdRW::ApplyTableDelta
102
103//*****************************************************************************
104// Get the record from a Delta MetaData that corresponds to the actual record.
105//*****************************************************************************
106__checkReturn
107HRESULT
108CMiniMdRW::GetDeltaRecord(
109 ULONG ixTbl, // Table.
110 ULONG iRid, // Record in the table.
111 void **ppRecord)
112{
113 HRESULT hr;
114 ULONG iMap; // RID in map table.
115 ENCMapRec *pMap; // Row in map table.
116
117 *ppRecord = NULL;
118 // If no remap, just return record directly.
119 if ((m_Schema.m_cRecs[TBL_ENCMap] == 0) || (ixTbl == TBL_Module) || !IsMinimalDelta())
120 {
121 return getRow(ixTbl, iRid, ppRecord);
122 }
123
124 // Use the remap table to find the physical row containing this logical row.
125 iMap = (*m_rENCRecs)[ixTbl];
126 IfFailRet(GetENCMapRecord(iMap, &pMap));
127
128 // Search for desired record.
129 while ((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) < iRid))
130 {
131 IfFailRet(GetENCMapRecord(++iMap, &pMap));
132 }
133
134 _ASSERTE((TblFromRecId(pMap->GetToken()) == ixTbl) && (RidFromRecId(pMap->GetToken()) == iRid));
135
136 // Relative position within table's group in map is physical rid.
137 iRid = iMap - (*m_rENCRecs)[ixTbl] + 1;
138
139 return getRow(ixTbl, iRid, ppRecord);
140} // CMiniMdRW::GetDeltaRecord
141
142//*****************************************************************************
143// Given a MetaData with ENC changes, apply those changes to this MetaData.
144//*****************************************************************************
145__checkReturn
146HRESULT
147CMiniMdRW::ApplyHeapDeltas(
148 CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
149{
150 if (mdDelta.IsMinimalDelta())
151 {
152 return ApplyHeapDeltasWithMinimalDelta(mdDelta);
153 }
154 else
155 {
156 return ApplyHeapDeltasWithFullDelta(mdDelta);
157 }
158}// CMiniMdRW::ApplyHeapDeltas
159
160__checkReturn
161HRESULT
162CMiniMdRW::ApplyHeapDeltasWithMinimalDelta(
163 CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
164{
165 HRESULT hr = S_OK;
166
167 // Extend the heaps with EnC minimal delta
168 IfFailGo(m_StringHeap.AddStringHeap(
169 &(mdDelta.m_StringHeap),
170 0)); // Start offset in the mdDelta
171 IfFailGo(m_BlobHeap.AddBlobHeap(
172 &(mdDelta.m_BlobHeap),
173 0)); // Start offset in the mdDelta
174 IfFailGo(m_UserStringHeap.AddBlobHeap(
175 &(mdDelta.m_UserStringHeap),
176 0)); // Start offset in the mdDelta
177 // We never do a minimal delta with the guid heap
178 IfFailGo(m_GuidHeap.AddGuidHeap(
179 &(mdDelta.m_GuidHeap),
180 m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap
181
182ErrExit:
183 return hr;
184} // CMiniMdRW::ApplyHeapDeltasWithMinimalDelta
185
186__checkReturn
187HRESULT
188CMiniMdRW::ApplyHeapDeltasWithFullDelta(
189 CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
190{
191 HRESULT hr = S_OK;
192
193 // Extend the heaps with EnC full delta
194 IfFailRet(m_StringHeap.AddStringHeap(
195 &(mdDelta.m_StringHeap),
196 m_StringHeap.GetUnalignedSize())); // Starting offset in the full delta string heap
197 IfFailRet(m_BlobHeap.AddBlobHeap(
198 &(mdDelta.m_BlobHeap),
199 m_BlobHeap.GetUnalignedSize())); // Starting offset in the full delta blob heap
200 IfFailRet(m_UserStringHeap.AddBlobHeap(
201 &(mdDelta.m_UserStringHeap),
202 m_UserStringHeap.GetUnalignedSize())); // Starting offset in the full delta user string heap
203 IfFailRet(m_GuidHeap.AddGuidHeap(
204 &(mdDelta.m_GuidHeap),
205 m_GuidHeap.GetSize())); // Starting offset in the full delta guid heap
206
207 return hr;
208} // CMiniMdRW::ApplyHeapDeltasWithFullDelta
209
210//*****************************************************************************
211// Driver for the delta process.
212//*****************************************************************************
213__checkReturn
214HRESULT
215CMiniMdRW::ApplyDelta(
216 CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
217{
218 HRESULT hr = S_OK;
219 ULONG iENC; // Loop control.
220 ULONG iRid; // RID of some record.
221 ULONG iNew; // RID of a new record.
222 int i; // Loop control.
223 ULONG ixTbl; // A table.
224
225#ifdef _DEBUG
226 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_ApplyDeltaBreak))
227 {
228 _ASSERTE(!"CMiniMDRW::ApplyDelta()");
229 }
230#endif // _DEBUG
231
232 // Init the suppressed column table. We know this one isn't zero...
233 if (m_SuppressedDeltaColumns[TBL_TypeDef] == 0)
234 {
235 m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList);
236 m_SuppressedDeltaColumns[TBL_PropertyMap] = (1 << PropertyMapRec::COL_PropertyList);
237 m_SuppressedDeltaColumns[TBL_EventMap] = (1 << EventMapRec::COL_EventList);
238 m_SuppressedDeltaColumns[TBL_Method] = (1 << MethodRec::COL_ParamList);
239 m_SuppressedDeltaColumns[TBL_TypeDef] = (1 << TypeDefRec::COL_FieldList)|(1<<TypeDefRec::COL_MethodList);
240 }
241
242 // Verify the version of the MD.
243 if (m_Schema.m_major != mdDelta.m_Schema.m_major ||
244 m_Schema.m_minor != mdDelta.m_Schema.m_minor)
245 {
246 _ASSERTE(!"Version of Delta MetaData is a incompatible with current MetaData.");
247 //<TODO>@FUTURE: unique error in the future since we are not shipping ENC.</TODO>
248 return E_INVALIDARG;
249 }
250
251 // verify MVIDs.
252 ModuleRec *pModDelta;
253 ModuleRec *pModBase;
254 IfFailGo(mdDelta.GetModuleRecord(1, &pModDelta));
255 IfFailGo(GetModuleRecord(1, &pModBase));
256 GUID GuidDelta;
257 GUID GuidBase;
258 IfFailGo(mdDelta.getMvidOfModule(pModDelta, &GuidDelta));
259 IfFailGo(getMvidOfModule(pModBase, &GuidBase));
260 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) && (GuidDelta != GuidBase))
261 {
262 _ASSERTE(!"Delta MetaData has different base than current MetaData.");
263 return E_INVALIDARG;
264 }
265
266
267 // Let the other md prepare for sparse records.
268 IfFailGo(mdDelta.StartENCMap());
269
270 // Fix the heaps.
271 IfFailGo(ApplyHeapDeltas(mdDelta));
272
273 // Truncate some tables in preparation to copy in new ENCLog data.
274 for (i = 0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG)-1; ++i)
275 {
276 m_Tables[ixTbl].Delete();
277 IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount(
278 m_TableDefs[ixTbl].m_cbRec,
279 mdDelta.m_Schema.m_cRecs[ixTbl]
280 COMMA_INDEBUG_MD(TRUE))); // fIsReadWrite
281 INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
282 m_Schema.m_cRecs[ixTbl] = 0;
283 }
284
285 // For each record in the ENC log...
286 for (iENC = 1; iENC <= mdDelta.m_Schema.m_cRecs[TBL_ENCLog]; ++iENC)
287 {
288 // Get the record, and the updated token.
289 ENCLogRec *pENC;
290 IfFailGo(mdDelta.GetENCLogRecord(iENC, &pENC));
291 ENCLogRec *pENC2;
292 IfFailGo(AddENCLogRecord(&pENC2, &iNew));
293 IfNullGo(pENC2);
294 ENCLogRec *pENC3;
295 _ASSERTE(iNew == iENC);
296 ULONG func = pENC->GetFuncCode();
297 pENC2->SetFuncCode(pENC->GetFuncCode());
298 pENC2->SetToken(pENC->GetToken());
299
300 // What kind of record is this?
301 if (IsRecId(pENC->GetToken()))
302 { // Non-token table
303 iRid = RidFromRecId(pENC->GetToken());
304 ixTbl = TblFromRecId(pENC->GetToken());
305 }
306 else
307 { // Token table.
308 iRid = RidFromToken(pENC->GetToken());
309 ixTbl = GetTableForToken(pENC->GetToken());
310 }
311
312 RID rid_Ignore;
313 // Switch based on the function code.
314 switch (func)
315 {
316 case eDeltaMethodCreate:
317 // Next ENC record will define the new Method.
318 MethodRec *pMethodRecord;
319 IfFailGo(AddMethodRecord(&pMethodRecord, &rid_Ignore));
320 IfFailGo(AddMethodToTypeDef(iRid, m_Schema.m_cRecs[TBL_Method]));
321 break;
322
323 case eDeltaParamCreate:
324 // Next ENC record will define the new Param. This record is
325 // tricky because params will be re-ordered based on their sequence,
326 // but the sequence isn't set until the NEXT record is applied.
327 // So, for ParamCreate only, apply the param record delta before
328 // adding the parent-child linkage.
329 ParamRec *pParamRecord;
330 IfFailGo(AddParamRecord(&pParamRecord, &rid_Ignore));
331
332 // Should have recorded a Param delta after the Param add.
333 _ASSERTE(iENC<mdDelta.m_Schema.m_cRecs[TBL_ENCLog]);
334 IfFailGo(mdDelta.GetENCLogRecord(iENC+1, &pENC3));
335 _ASSERTE(pENC3->GetFuncCode() == 0);
336 _ASSERTE(GetTableForToken(pENC3->GetToken()) == TBL_Param);
337 IfFailGo(ApplyTableDelta(mdDelta, TBL_Param, RidFromToken(pENC3->GetToken()), eDeltaFuncDefault));
338
339 // Now that Param record is OK, set up linkage.
340 IfFailGo(AddParamToMethod(iRid, m_Schema.m_cRecs[TBL_Param]));
341 break;
342
343 case eDeltaFieldCreate:
344 // Next ENC record will define the new Field.
345 FieldRec *pFieldRecord;
346 IfFailGo(AddFieldRecord(&pFieldRecord, &rid_Ignore));
347 IfFailGo(AddFieldToTypeDef(iRid, m_Schema.m_cRecs[TBL_Field]));
348 break;
349
350 case eDeltaPropertyCreate:
351 // Next ENC record will define the new Property.
352 PropertyRec *pPropertyRecord;
353 IfFailGo(AddPropertyRecord(&pPropertyRecord, &rid_Ignore));
354 IfFailGo(AddPropertyToPropertyMap(iRid, m_Schema.m_cRecs[TBL_Property]));
355 break;
356
357 case eDeltaEventCreate:
358 // Next ENC record will define the new Event.
359 EventRec *pEventRecord;
360 IfFailGo(AddEventRecord(&pEventRecord, &rid_Ignore));
361 IfFailGo(AddEventToEventMap(iRid, m_Schema.m_cRecs[TBL_Event]));
362 break;
363
364 case eDeltaFuncDefault:
365 IfFailGo(ApplyTableDelta(mdDelta, ixTbl, iRid, func));
366 break;
367
368 default:
369 _ASSERTE(!"Unexpected function in ApplyDelta");
370 IfFailGo(E_UNEXPECTED);
371 break;
372 }
373 }
374 m_Schema.m_cRecs[TBL_ENCLog] = mdDelta.m_Schema.m_cRecs[TBL_ENCLog];
375
376ErrExit:
377 // Store the result for returning (IfFailRet will modify hr)
378 HRESULT hrReturn = hr;
379 IfFailRet(mdDelta.EndENCMap());
380
381
382 return hrReturn;
383} // CMiniMdRW::ApplyDelta
384
385//*****************************************************************************
386//*****************************************************************************
387__checkReturn
388HRESULT
389CMiniMdRW::StartENCMap() // S_OK or error.
390{
391 HRESULT hr = S_OK;
392 ULONG iENC; // Loop control.
393 ULONG ixTbl; // A table.
394 int ixTblPrev = -1; // Table previously seen.
395
396 _ASSERTE(m_rENCRecs == 0);
397
398 if (m_Schema.m_cRecs[TBL_ENCMap] == 0)
399 return S_OK;
400
401 // Build an array of pointers into the ENCMap table for fast access to the ENCMap
402 // for each table.
403 m_rENCRecs = new (nothrow) ULONGARRAY;
404 IfNullGo(m_rENCRecs);
405 if (!m_rENCRecs->AllocateBlock(TBL_COUNT))
406 IfFailGo(E_OUTOFMEMORY);
407 for (iENC = 1; iENC <= m_Schema.m_cRecs[TBL_ENCMap]; ++iENC)
408 {
409 ENCMapRec *pMap;
410 IfFailGo(GetENCMapRecord(iENC, &pMap));
411 ixTbl = TblFromRecId(pMap->GetToken());
412 _ASSERTE((int)ixTbl >= ixTblPrev);
413 _ASSERTE(ixTbl < TBL_COUNT);
414 _ASSERTE(ixTbl != TBL_ENCMap);
415 _ASSERTE(ixTbl != TBL_ENCLog);
416 if ((int)ixTbl == ixTblPrev)
417 continue;
418 // Catch up on any skipped tables.
419 while (ixTblPrev < (int)ixTbl)
420 {
421 (*m_rENCRecs)[++ixTblPrev] = iENC;
422 }
423 }
424 while (ixTblPrev < TBL_COUNT-1)
425 {
426 (*m_rENCRecs)[++ixTblPrev] = iENC;
427 }
428
429ErrExit:
430 return hr;
431} // CMiniMdRW::StartENCMap
432
433//*****************************************************************************
434//*****************************************************************************
435__checkReturn
436HRESULT
437CMiniMdRW::EndENCMap()
438{
439 if (m_rENCRecs != NULL)
440 {
441 delete m_rENCRecs;
442 m_rENCRecs = NULL;
443 }
444
445 return S_OK;
446} // CMiniMdRW::EndENCMap
447