1 /* Copyright (C) 2004 - 2009  Versant Inc.  http://www.db4o.com */
2 
3 using System.Collections;
4 using Db4objects.Db4o;
5 using Db4objects.Db4o.Config;
6 using Db4objects.Db4o.Defragment;
7 using Db4objects.Db4o.Ext;
8 using Db4objects.Db4o.Foundation;
9 using Db4objects.Db4o.IO;
10 using Db4objects.Db4o.Internal;
11 using Db4objects.Db4o.Internal.Btree;
12 using Db4objects.Db4o.Internal.Classindex;
13 using Db4objects.Db4o.Internal.Encoding;
14 using Db4objects.Db4o.Internal.Ids;
15 using Db4objects.Db4o.Internal.Mapping;
16 using Db4objects.Db4o.Internal.Marshall;
17 using Db4objects.Db4o.Internal.Slots;
18 using Db4objects.Db4o.Typehandlers;
19 
20 namespace Db4objects.Db4o.Defragment
21 {
22 	/// <exclude></exclude>
23 	public class DefragmentServicesImpl : IDefragmentServices
24 	{
25 		public abstract class DbSelector
26 		{
DbSelector()27 			internal DbSelector()
28 			{
29 			}
30 
Db(DefragmentServicesImpl context)31 			internal abstract LocalObjectContainer Db(DefragmentServicesImpl context);
32 
Transaction(DefragmentServicesImpl context)33 			internal virtual Db4objects.Db4o.Internal.Transaction Transaction(DefragmentServicesImpl
34 				 context)
35 			{
36 				return Db(context).SystemTransaction();
37 			}
38 		}
39 
40 		private sealed class _DbSelector_39 : DefragmentServicesImpl.DbSelector
41 		{
_DbSelector_39()42 			public _DbSelector_39()
43 			{
44 			}
45 
Db(DefragmentServicesImpl context)46 			internal override LocalObjectContainer Db(DefragmentServicesImpl context)
47 			{
48 				return context._sourceDb;
49 			}
50 		}
51 
52 		public static readonly DefragmentServicesImpl.DbSelector Sourcedb = new _DbSelector_39
53 			();
54 
55 		private sealed class _DbSelector_45 : DefragmentServicesImpl.DbSelector
56 		{
_DbSelector_45()57 			public _DbSelector_45()
58 			{
59 			}
60 
Db(DefragmentServicesImpl context)61 			internal override LocalObjectContainer Db(DefragmentServicesImpl context)
62 			{
63 				return context._targetDb;
64 			}
65 		}
66 
67 		public static readonly DefragmentServicesImpl.DbSelector Targetdb = new _DbSelector_45
68 			();
69 
70 		private readonly LocalObjectContainer _sourceDb;
71 
72 		private readonly LocalObjectContainer _targetDb;
73 
74 		private readonly IIdMapping _mapping;
75 
76 		private IDefragmentListener _listener;
77 
78 		private IQueue4 _unindexed = new NonblockingQueue();
79 
80 		private DefragmentConfig _defragConfig;
81 
82 		/// <exception cref="System.IO.IOException"></exception>
DefragmentServicesImpl(DefragmentConfig defragConfig, IDefragmentListener listener)83 		public DefragmentServicesImpl(DefragmentConfig defragConfig, IDefragmentListener
84 			listener)
85 		{
86 			_listener = listener;
87 			Config4Impl originalConfig = (Config4Impl)defragConfig.Db4oConfig();
88 			IStorage storage = defragConfig.BackupStorage();
89 			if (defragConfig.ReadOnly())
90 			{
91 				storage = new NonFlushingStorage(storage);
92 			}
93 			Config4Impl sourceConfig = PrepareConfig(originalConfig, storage, defragConfig.ReadOnly
94 				());
95 			_sourceDb = (LocalObjectContainer)Db4oFactory.OpenFile(sourceConfig, defragConfig
96 				.TempPath()).Ext();
97 			_sourceDb.ShowInternalClasses(true);
98 			defragConfig.Db4oConfig().BlockSize(_sourceDb.BlockSize());
99 			if (!originalConfig.GenerateCommitTimestamps().DefiniteNo())
100 			{
101 				defragConfig.Db4oConfig().GenerateCommitTimestamps(_sourceDb.Config().GenerateCommitTimestamps
102 					().DefiniteYes());
103 			}
104 			_targetDb = FreshTargetFile(defragConfig);
105 			_mapping = defragConfig.Mapping();
106 			_mapping.Open();
107 			_defragConfig = defragConfig;
108 		}
109 
PrepareConfig(Config4Impl originalConfig, IStorage storage, bool readOnly)110 		private Config4Impl PrepareConfig(Config4Impl originalConfig, IStorage storage, bool
111 			 readOnly)
112 		{
113 			Config4Impl sourceConfig = (Config4Impl)originalConfig.DeepClone(null);
114 			sourceConfig.WeakReferences(false);
115 			sourceConfig.Storage = storage;
116 			sourceConfig.ReadOnly(readOnly);
117 			return sourceConfig;
118 		}
119 
120 		/// <exception cref="System.IO.IOException"></exception>
FreshTempFile(string fileName, int blockSize )121 		internal static LocalObjectContainer FreshTempFile(string fileName, int blockSize
122 			)
123 		{
124 			FileStorage storage = new FileStorage();
125 			storage.Delete(fileName);
126 			IConfiguration db4oConfig = DefragmentConfig.VanillaDb4oConfig(blockSize);
127 			db4oConfig.ObjectClass(typeof(IdSlotMapping)).ObjectField("_id").Indexed(true);
128 			db4oConfig.Storage = storage;
129 			return (LocalObjectContainer)Db4oFactory.OpenFile(db4oConfig, fileName).Ext();
130 		}
131 
132 		/// <exception cref="System.IO.IOException"></exception>
FreshTargetFile(DefragmentConfig config)133 		internal static LocalObjectContainer FreshTargetFile(DefragmentConfig config)
134 		{
135 			config.Db4oConfig().Storage.Delete(config.OrigPath());
136 			return (LocalObjectContainer)Db4oFactory.OpenFile(config.ClonedDb4oConfig(), config
137 				.OrigPath());
138 		}
139 
MappedID(int oldID, int defaultID)140 		public virtual int MappedID(int oldID, int defaultID)
141 		{
142 			int mapped = InternalMappedID(oldID);
143 			return (mapped != 0 ? mapped : defaultID);
144 		}
145 
146 		/// <exception cref="Db4objects.Db4o.Internal.Mapping.MappingNotFoundException"></exception>
StrictMappedID(int oldID)147 		public virtual int StrictMappedID(int oldID)
148 		{
149 			int mapped = InternalMappedID(oldID);
150 			if (mapped == 0)
151 			{
152 				throw new MappingNotFoundException(oldID);
153 			}
154 			return mapped;
155 		}
156 
MappedID(int id)157 		public virtual int MappedID(int id)
158 		{
159 			if (id == 0)
160 			{
161 				return 0;
162 			}
163 			int mapped = InternalMappedID(id);
164 			if (mapped == 0)
165 			{
166 				_listener.NotifyDefragmentInfo(new DefragmentInfo("No mapping found for ID " + id
167 					));
168 				return Const4.InvalidObjectId;
169 			}
170 			return mapped;
171 		}
172 
173 		/// <exception cref="Db4objects.Db4o.Internal.Mapping.MappingNotFoundException"></exception>
InternalMappedID(int oldID)174 		private int InternalMappedID(int oldID)
175 		{
176 			if (oldID == 0)
177 			{
178 				return 0;
179 			}
180 			int mappedId = _mapping.MappedId(oldID);
181 			if (mappedId == 0 && _sourceDb.Handlers.IsSystemHandler(oldID))
182 			{
183 				return oldID;
184 			}
185 			return mappedId;
186 		}
187 
MapIDs(int oldID, int newID, bool isClassID)188 		public virtual void MapIDs(int oldID, int newID, bool isClassID)
189 		{
190 			_mapping.MapId(oldID, newID, isClassID);
191 		}
192 
Close()193 		public virtual void Close()
194 		{
195 			_sourceDb.Close();
196 			_targetDb.Close();
197 			_mapping.Close();
198 		}
199 
BufferByID(DefragmentServicesImpl.DbSelector selector , int id)200 		public virtual ByteArrayBuffer BufferByID(DefragmentServicesImpl.DbSelector selector
201 			, int id)
202 		{
203 			Slot slot = CommittedSlot(selector, id);
204 			return BufferByAddress(selector, slot.Address(), slot.Length());
205 		}
206 
CommittedSlot(DefragmentServicesImpl.DbSelector selector, int id)207 		private Slot CommittedSlot(DefragmentServicesImpl.DbSelector selector, int id)
208 		{
209 			return selector.Db(this).IdSystem().CommittedSlot(id);
210 		}
211 
212 		/// <exception cref="System.IO.IOException"></exception>
SourceBufferByAddress(int address, int length)213 		public virtual ByteArrayBuffer SourceBufferByAddress(int address, int length)
214 		{
215 			return BufferByAddress(Sourcedb, address, length);
216 		}
217 
218 		/// <exception cref="System.IO.IOException"></exception>
TargetBufferByAddress(int address, int length)219 		public virtual ByteArrayBuffer TargetBufferByAddress(int address, int length)
220 		{
221 			return BufferByAddress(Targetdb, address, length);
222 		}
223 
BufferByAddress(DefragmentServicesImpl.DbSelector selector, int address, int length)224 		public virtual ByteArrayBuffer BufferByAddress(DefragmentServicesImpl.DbSelector
225 			selector, int address, int length)
226 		{
227 			return selector.Db(this).DecryptedBufferByAddress(address, length);
228 		}
229 
230 		/// <exception cref="System.ArgumentException"></exception>
TargetStatefulBufferByAddress(int address, int length )231 		public virtual StatefulBuffer TargetStatefulBufferByAddress(int address, int length
232 			)
233 		{
234 			return _targetDb.ReadWriterByAddress(Targetdb.Transaction(this), address, length);
235 		}
236 
AllocateTargetSlot(int length)237 		public virtual Slot AllocateTargetSlot(int length)
238 		{
239 			return _targetDb.AllocateSlot(length);
240 		}
241 
TargetWriteBytes(DefragmentContextImpl context, int address)242 		public virtual void TargetWriteBytes(DefragmentContextImpl context, int address)
243 		{
244 			context.Write(_targetDb, address);
245 		}
246 
TargetWriteBytes(ByteArrayBuffer reader, int address)247 		public virtual void TargetWriteBytes(ByteArrayBuffer reader, int address)
248 		{
249 			_targetDb.WriteBytes(reader, address, 0);
250 		}
251 
StoredClasses(DefragmentServicesImpl.DbSelector selector )252 		public virtual IStoredClass[] StoredClasses(DefragmentServicesImpl.DbSelector selector
253 			)
254 		{
255 			LocalObjectContainer db = selector.Db(this);
256 			db.ShowInternalClasses(true);
257 			try
258 			{
259 				return db.ClassCollection().StoredClasses();
260 			}
261 			finally
262 			{
263 				db.ShowInternalClasses(false);
264 			}
265 		}
266 
StringIO()267 		public virtual LatinStringIO StringIO()
268 		{
269 			return _sourceDb.StringIO();
270 		}
271 
TargetCommit()272 		public virtual void TargetCommit()
273 		{
274 			_targetDb.Commit();
275 		}
276 
SourceHandler(int id)277 		public virtual ITypeHandler4 SourceHandler(int id)
278 		{
279 			return _sourceDb.TypeHandlerForClassMetadataID(id);
280 		}
281 
SourceClassCollectionID()282 		public virtual int SourceClassCollectionID()
283 		{
284 			return _sourceDb.ClassCollection().GetID();
285 		}
286 
287 		private Hashtable4 _classIndices = new Hashtable4(16);
288 
ClassIndexID(ClassMetadata classMetadata)289 		public virtual int ClassIndexID(ClassMetadata classMetadata)
290 		{
291 			return ClassIndex(classMetadata).Id();
292 		}
293 
TraverseAll(ClassMetadata classMetadata, IVisitor4 command)294 		public virtual void TraverseAll(ClassMetadata classMetadata, IVisitor4 command)
295 		{
296 			if (!classMetadata.HasClassIndex())
297 			{
298 				return;
299 			}
300 			classMetadata.Index().TraverseAll(Sourcedb.Transaction(this), command);
301 		}
302 
TraverseAllIndexSlots(ClassMetadata classMetadata, IVisitor4 command)303 		public virtual void TraverseAllIndexSlots(ClassMetadata classMetadata, IVisitor4
304 			command)
305 		{
306 			IEnumerator slotIDIter = classMetadata.Index().AllSlotIDs(Sourcedb.Transaction(this
307 				));
308 			while (slotIDIter.MoveNext())
309 			{
310 				command.Visit(slotIDIter.Current);
311 			}
312 		}
313 
TraverseAllIndexSlots(BTree btree, IVisitor4 command)314 		public virtual void TraverseAllIndexSlots(BTree btree, IVisitor4 command)
315 		{
316 			IEnumerator slotIDIter = btree.AllNodeIds(Sourcedb.Transaction(this));
317 			while (slotIDIter.MoveNext())
318 			{
319 				command.Visit(slotIDIter.Current);
320 			}
321 		}
322 
RegisterBTreeIDs(BTree btree, IDMappingCollector collector)323 		public virtual void RegisterBTreeIDs(BTree btree, IDMappingCollector collector)
324 		{
325 			collector.CreateIDMapping(this, btree.GetID(), false);
326 			TraverseAllIndexSlots(btree, new _IVisitor4_244(this, collector));
327 		}
328 
329 		private sealed class _IVisitor4_244 : IVisitor4
330 		{
_IVisitor4_244(DefragmentServicesImpl _enclosing, IDMappingCollector collector )331 			public _IVisitor4_244(DefragmentServicesImpl _enclosing, IDMappingCollector collector
332 				)
333 			{
334 				this._enclosing = _enclosing;
335 				this.collector = collector;
336 			}
337 
Visit(object obj)338 			public void Visit(object obj)
339 			{
340 				int id = ((int)obj);
341 				collector.CreateIDMapping(this._enclosing, id, false);
342 			}
343 
344 			private readonly DefragmentServicesImpl _enclosing;
345 
346 			private readonly IDMappingCollector collector;
347 		}
348 
DatabaseIdentityID(DefragmentServicesImpl.DbSelector selector)349 		public virtual int DatabaseIdentityID(DefragmentServicesImpl.DbSelector selector)
350 		{
351 			LocalObjectContainer db = selector.Db(this);
352 			Db4oDatabase identity = db.Identity();
353 			if (identity == null)
354 			{
355 				return 0;
356 			}
357 			return identity.GetID(selector.Transaction(this));
358 		}
359 
ClassIndex(ClassMetadata classMetadata)360 		private IClassIndexStrategy ClassIndex(ClassMetadata classMetadata)
361 		{
362 			IClassIndexStrategy classIndex = (IClassIndexStrategy)_classIndices.Get(classMetadata
363 				);
364 			if (classIndex == null)
365 			{
366 				classIndex = new BTreeClassIndexStrategy(classMetadata);
367 				_classIndices.Put(classMetadata, classIndex);
368 				classIndex.Initialize(_targetDb);
369 			}
370 			return classIndex;
371 		}
372 
SystemTrans()373 		public virtual Db4objects.Db4o.Internal.Transaction SystemTrans()
374 		{
375 			return Sourcedb.Transaction(this);
376 		}
377 
CopyIdentity()378 		public virtual void CopyIdentity()
379 		{
380 			_targetDb.SetIdentity(_sourceDb.Identity());
381 		}
382 
ReplaceClassMetadataRepository()383 		public virtual void ReplaceClassMetadataRepository()
384 		{
385 			Db4objects.Db4o.Internal.Transaction systemTransaction = _targetDb.SystemTransaction
386 				();
387 			// Can't use strictMappedID because the repository ID can
388 			// be lower than HandlerRegisrtry _highestBuiltinTypeID and
389 			// the ClassRepository ID would be treated as a system handler
390 			// and the unmapped ID would be returned.
391 			int newRepositoryId = _mapping.MappedId(SourceClassCollectionID());
392 			int sourceIdentityID = DatabaseIdentityID(DefragmentServicesImpl.Sourcedb);
393 			int targetIdentityID = _mapping.MappedId(sourceIdentityID);
394 			int targetUuidIndexID = _mapping.MappedId(SourceUuidIndexID());
395 			int oldIdentityId = _targetDb.SystemData().Identity().GetID(systemTransaction);
396 			int oldRepositoryId = _targetDb.ClassCollection().GetID();
397 			ClassMetadataRepository oldRepository = _targetDb.ClassCollection();
398 			ClassMetadataRepository newRepository = new ClassMetadataRepository(systemTransaction
399 				);
400 			newRepository.SetID(newRepositoryId);
401 			newRepository.Read(systemTransaction);
402 			newRepository.InitOnUp(systemTransaction);
403 			_targetDb.SystemData().ClassCollectionID(newRepositoryId);
404 			_targetDb.ReplaceClassMetadataRepository(newRepository);
405 			_targetDb.SystemData().UuidIndexId(targetUuidIndexID);
406 			Db4oDatabase identity = (Db4oDatabase)_targetDb.GetByID(systemTransaction, targetIdentityID
407 				);
408 			_targetDb.SetIdentity(identity);
409 			ClassMetadataIterator iterator = oldRepository.Iterator();
410 			while (iterator.MoveNext())
411 			{
412 				ClassMetadata classMetadata = iterator.CurrentClass();
413 				BTreeClassIndexStrategy index = (BTreeClassIndexStrategy)classMetadata.Index();
414 				index.Btree().Free(_targetDb.LocalSystemTransaction());
415 				FreeById(classMetadata.GetID());
416 			}
417 			FreeById(oldIdentityId);
418 			FreeById(oldRepositoryId);
419 		}
420 
DefragIdToTimestampBtree()421 		public virtual void DefragIdToTimestampBtree()
422 		{
423 			if (_sourceDb.SystemData().IdToTimestampIndexId() == 0)
424 			{
425 				return;
426 			}
427 			LocalTransaction targetTransaction = (LocalTransaction)_targetDb.SystemTransaction
428 				();
429 			LocalTransaction sourceTransaction = (LocalTransaction)_sourceDb.SystemTransaction
430 				();
431 			CommitTimestampSupport target = targetTransaction.CommitTimestampSupport();
432 			CommitTimestampSupport source = sourceTransaction.CommitTimestampSupport();
433 			if (source.IdToTimestamp() == null)
434 			{
435 				return;
436 			}
437 			source.IdToTimestamp().TraverseKeys(sourceTransaction, new _IVisitor4_336(this, target
438 				, targetTransaction));
439 		}
440 
441 		private sealed class _IVisitor4_336 : IVisitor4
442 		{
_IVisitor4_336(DefragmentServicesImpl _enclosing, CommitTimestampSupport target , LocalTransaction targetTransaction)443 			public _IVisitor4_336(DefragmentServicesImpl _enclosing, CommitTimestampSupport target
444 				, LocalTransaction targetTransaction)
445 			{
446 				this._enclosing = _enclosing;
447 				this.target = target;
448 				this.targetTransaction = targetTransaction;
449 			}
450 
Visit(object te)451 			public void Visit(object te)
452 			{
453 				int mappedID = this._enclosing.MappedID(((CommitTimestampSupport.TimestampEntry)te
454 					).ParentID());
455 				target.Put(targetTransaction, mappedID, ((CommitTimestampSupport.TimestampEntry)te
456 					).GetCommitTimestamp());
457 			}
458 
459 			private readonly DefragmentServicesImpl _enclosing;
460 
461 			private readonly CommitTimestampSupport target;
462 
463 			private readonly LocalTransaction targetTransaction;
464 		}
465 
FreeById(int id)466 		private void FreeById(int id)
467 		{
468 			_targetDb.SystemTransaction().IdSystem().NotifySlotDeleted(id, SlotChangeFactory.
469 				SystemObjects);
470 		}
471 
SourceBufferByID(int sourceID)472 		public virtual ByteArrayBuffer SourceBufferByID(int sourceID)
473 		{
474 			return BufferByID(Sourcedb, sourceID);
475 		}
476 
SourceUuidIndex()477 		public virtual BTree SourceUuidIndex()
478 		{
479 			if (SourceUuidIndexID() == 0)
480 			{
481 				return null;
482 			}
483 			return _sourceDb.UUIDIndex().GetIndex(SystemTrans());
484 		}
485 
TargetUuidIndexID(int id)486 		public virtual void TargetUuidIndexID(int id)
487 		{
488 			_targetDb.SystemData().UuidIndexId(id);
489 		}
490 
SourceUuidIndexID()491 		public virtual int SourceUuidIndexID()
492 		{
493 			return _sourceDb.SystemData().UuidIndexId();
494 		}
495 
SourceIdToTimestampIndexID()496 		public virtual int SourceIdToTimestampIndexID()
497 		{
498 			return _sourceDb.SystemData().IdToTimestampIndexId();
499 		}
500 
ClassMetadataForId(int id)501 		public virtual ClassMetadata ClassMetadataForId(int id)
502 		{
503 			return _sourceDb.ClassMetadataForID(id);
504 		}
505 
RegisterUnindexed(int id)506 		public virtual void RegisterUnindexed(int id)
507 		{
508 			_unindexed.Add(id);
509 		}
510 
UnindexedIDs()511 		public virtual IdSource UnindexedIDs()
512 		{
513 			return new IdSource(_unindexed);
514 		}
515 
SourceObjectHeader(ByteArrayBuffer buffer)516 		public virtual ObjectHeader SourceObjectHeader(ByteArrayBuffer buffer)
517 		{
518 			return new ObjectHeader(_sourceDb, buffer);
519 		}
520 
BlockSize()521 		public virtual int BlockSize()
522 		{
523 			return _sourceDb.BlockSize();
524 		}
525 
SourceAddressByID(int sourceID)526 		public virtual int SourceAddressByID(int sourceID)
527 		{
528 			return CommittedSlot(Sourcedb, sourceID).Address();
529 		}
530 
TargetAddressByID(int sourceID)531 		public virtual int TargetAddressByID(int sourceID)
532 		{
533 			return _mapping.AddressForId(sourceID);
534 		}
535 
Accept(IStoredClass klass)536 		public virtual bool Accept(IStoredClass klass)
537 		{
538 			return this._defragConfig.StoredClassFilter().Accept(klass);
539 		}
540 
TargetNewId()541 		public virtual int TargetNewId()
542 		{
543 			return _targetDb.IdSystem().NewId();
544 		}
545 
Mapping()546 		public virtual IIdMapping Mapping()
547 		{
548 			return _mapping;
549 		}
550 
CommitIds()551 		public virtual void CommitIds()
552 		{
553 			FreespaceCommitter freespaceCommitter = new FreespaceCommitter(_targetDb.FreespaceManager
554 				());
555 			freespaceCommitter.TransactionalIdSystem(SystemTrans().IdSystem());
556 			_targetDb.IdSystem().Commit(Mapping().SlotChanges(), freespaceCommitter);
557 			freespaceCommitter.Commit();
558 		}
559 	}
560 }
561