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 | |
21 | ULONG CMiniMdRW::m_SuppressedDeltaColumns[TBL_COUNT] = {0}; |
22 | |
23 | //***************************************************************************** |
24 | // Copy the data from one MiniMd to another. |
25 | //***************************************************************************** |
26 | __checkReturn |
27 | HRESULT |
28 | CMiniMdRW::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 |
52 | HRESULT |
53 | CMiniMdRW::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 | |
99 | ErrExit: |
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 |
107 | HRESULT |
108 | CMiniMdRW::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 |
146 | HRESULT |
147 | CMiniMdRW::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 |
161 | HRESULT |
162 | CMiniMdRW::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 | |
182 | ErrExit: |
183 | return hr; |
184 | } // CMiniMdRW::ApplyHeapDeltasWithMinimalDelta |
185 | |
186 | __checkReturn |
187 | HRESULT |
188 | CMiniMdRW::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 |
214 | HRESULT |
215 | CMiniMdRW::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 | |
376 | ErrExit: |
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 |
388 | HRESULT |
389 | CMiniMdRW::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 | |
429 | ErrExit: |
430 | return hr; |
431 | } // CMiniMdRW::StartENCMap |
432 | |
433 | //***************************************************************************** |
434 | //***************************************************************************** |
435 | __checkReturn |
436 | HRESULT |
437 | CMiniMdRW::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 | |