DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
IndexSetStore.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 using System.IO;
20 using System.Linq;
21 using System.Xml.Schema;
22 
23 using Deveel.Data;
24 using Deveel.Data.Store;
25 
26 namespace Deveel.Data.Index {
27  class IndexSetStore {
28  private IArea startArea;
29 
30  private long indexHeaderPointer;
32 
34 
35  // private bool disposed;
36 
37  private const int Magic = 0x0CA90291;
38 
39  public IndexSetStore(IDatabaseContext databaseContext, IStore store) {
40  Store = store;
41  }
42 
43  public IStore Store { get; private set; }
44 
45  internal void DeleteAreas(IEnumerable<int> deletedAreas) {
46  lock (this) {
47  if (Store != null) {
48  try {
49  Store.Lock();
50 
51  foreach (var id in deletedAreas) {
52  Store.DeleteArea(id);
53  }
54 
55  } catch (IOException) {
56  // TODO: Raise the error to the system
57  } finally {
58  Store.Unlock();
59  }
60  }
61  }
62  }
63 
64  private long CreateNewBlock() {
65  // Allocate the area
66  var a = Store.CreateArea(16);
67  long indexBlockP = a.Id;
68  // Setup the header
69  a.WriteInt4(1); // version
70  a.WriteInt4(0); // reserved
71  a.WriteInt8(0); // block entries
72  a.Flush();
73 
74  return indexBlockP;
75  }
76 
77  public long Create() {
78  lock (this) {
79  // Create an empty index header area
80  var a = Store.CreateArea(16);
81  indexHeaderPointer = a.Id;
82  a.WriteInt4(1); // version
83  a.WriteInt4(0); // reserved
84  a.WriteInt8(0); // number of indexes in the set
85  a.Flush();
86 
87  // Set up the local IArea object for the index header
88  indexHeaderArea = Store.GetArea(indexHeaderPointer);
89 
90  indexBlocks = new IndexBlock[0];
91 
92  // Allocate the starting header
93  var sa = Store.CreateArea(32);
94  long startPointer = sa.Id;
95  // The magic
96  sa.WriteInt4(Magic);
97  // The version
98  sa.WriteInt4(1);
99  // Pointer to the index header
100  sa.WriteInt8(indexHeaderPointer);
101  sa.Flush();
102 
103  // Set the 'start_area' value.
104  startArea = Store.GetArea(startPointer);
105 
106  return startPointer;
107  }
108  }
109 
110  public void Open(long startPointer) {
111  lock (this) {
112  // Set up the start area
113  startArea = Store.GetArea(startPointer);
114 
115  int magic = startArea.ReadInt4();
116  if (magic != Magic)
117  throw new IOException("Magic value for index set does not match.");
118 
119  int version = startArea.ReadInt4();
120  if (version != 1)
121  throw new IOException("Unknown version for index set.");
122 
123  // Setup the index_header area
124  indexHeaderPointer = startArea.ReadInt8();
125  indexHeaderArea = Store.GetArea(indexHeaderPointer);
126 
127  // Read the index header area
128  version = indexHeaderArea.ReadInt4(); // version
129  if (version != 1)
130  throw new IOException("Incorrect version");
131 
132  indexHeaderArea.ReadInt4(); // reserved
133  int indexCount = (int)indexHeaderArea.ReadInt8();
134  indexBlocks = new IndexBlock[indexCount];
135 
136  // Initialize each index block
137  for (int i = 0; i < indexCount; ++i) {
138  int type = indexHeaderArea.ReadInt4();
139  int blockSize = indexHeaderArea.ReadInt4();
140  long indexBlockPointer = indexHeaderArea.ReadInt8();
141  if (type != 1)
142  throw new IOException("Do not understand index type: " + type);
143 
144  indexBlocks[i] = new IndexBlock(this, i, blockSize, indexBlockPointer);
145  indexBlocks[i].AddReference();
146  }
147  }
148  }
149 
150  public void Close() {
151  lock (this) {
152  if (Store != null) {
153  for (int i = 0; i < indexBlocks.Length; ++i) {
154  indexBlocks[i].RemoveReference();
155  }
156  Store = null;
157  indexBlocks = null;
158  }
159  }
160  }
161 
162  public void GetAreasUsed(List<long> list) {
163  list.Add(startArea.Id);
164  list.Add(indexHeaderPointer);
165 
166  foreach (var block in indexBlocks) {
167  list.Add(block.StartOffset);
168  long[] blockPointers = block.GetBlockPointers();
169  list.AddRange(blockPointers);
170  }
171  }
172 
173  public void PrepareIndexLists(int count, int type, int blockSize) {
174  lock (this) {
175  try {
176  Store.Lock();
177 
178  // Allocate a new area for the list
179  int newSize = 16 + ((indexBlocks.Length + count) * 16);
180  var newIndexArea = Store.CreateArea(newSize);
181  long newIndexPointer = newIndexArea.Id;
182  var newIndexBlocks = new IndexBlock[(indexBlocks.Length + count)];
183 
184  // Copy the existing area
185  indexHeaderArea.Position = 0;
186  int version = indexHeaderArea.ReadInt4();
187  int reserved = indexHeaderArea.ReadInt4();
188  long icount = indexHeaderArea.ReadInt8();
189  newIndexArea.WriteInt4(version);
190  newIndexArea.WriteInt4(reserved);
191  newIndexArea.WriteInt8(icount + count);
192 
193  for (int i = 0; i < indexBlocks.Length; ++i) {
194  int itype = indexHeaderArea.ReadInt4();
195  int iblockSize = indexHeaderArea.ReadInt4();
196  long indexBlockP = indexHeaderArea.ReadInt8();
197 
198  newIndexArea.WriteInt4(itype);
199  newIndexArea.WriteInt4(iblockSize);
200  newIndexArea.WriteInt8(indexBlockP);
201 
202  newIndexBlocks[i] = indexBlocks[i];
203  }
204 
205  // Add the new entries
206  for (int i = 0; i < count; ++i) {
207  long newBlankBlockP = CreateNewBlock();
208 
209  newIndexArea.WriteInt4(type);
210  newIndexArea.WriteInt4(blockSize);
211  newIndexArea.WriteInt8(newBlankBlockP);
212 
213  var newBlock = new IndexBlock(this, indexBlocks.Length + i, blockSize, newBlankBlockP);
214  newBlock.AddReference();
215  newIndexBlocks[indexBlocks.Length + i] = newBlock;
216  }
217 
218  // Finished initializing the index.
219  newIndexArea.Flush();
220 
221  // The old index header pointer
222  long oldIndexHeaderP = indexHeaderPointer;
223 
224  // Update the state of this object,
225  indexHeaderPointer = newIndexPointer;
226  indexHeaderArea = Store.GetArea(newIndexPointer);
227  indexBlocks = newIndexBlocks;
228 
229  // Update the start pointer
230  startArea.Position = 8;
231  startArea.WriteInt8(newIndexPointer);
232  startArea.Flush();
233 
234  // Free the old header
235  Store.DeleteArea(oldIndexHeaderP);
236  } finally {
237  Store.Unlock();
238  }
239  }
240  }
241 
243  lock (this) {
244  // Clone the blocks list. This represents the current snapshot of the
245  // index state.
246  var snapshotIndexBlocks = (IndexBlock[])indexBlocks.Clone();
247 
248  // Add this as the reference
249  for (int i = 0; i < snapshotIndexBlocks.Length; ++i) {
250  snapshotIndexBlocks[i].AddReference();
251  }
252 
253  return new SnapshotIndexSet(this, snapshotIndexBlocks);
254  }
255  }
256 
257  private void CommitIndexHeader() {
258  lock (this) {
259  // Make a new index header area for the changed set.
260  var a = Store.CreateArea(16 + (indexBlocks.Length * 16));
261  long aOffset = a.Id;
262 
263  a.WriteInt4(1); // version
264  a.WriteInt4(0); // reserved
265  a.WriteInt8(indexBlocks.Length); // count
266 
267  foreach (var indBlock in indexBlocks) {
268  a.WriteInt4(1);
269  a.WriteInt4(indBlock.BlockSize);
270  a.WriteInt8(indBlock.StartOffset);
271  }
272 
273  // Finish creating the updated header
274  a.Flush();
275 
276  // The old index header pointer
277  long oldIndexHeaderP = indexHeaderPointer;
278 
279  // Set the new index header
280  indexHeaderPointer = aOffset;
281  indexHeaderArea = Store.GetArea(indexHeaderPointer);
282 
283  // Write the change to 'startPointer'
284  startArea.Position = 8;
285  startArea.WriteInt8(indexHeaderPointer);
286  startArea.Flush();
287 
288  // Free the old header index
289  Store.DeleteArea(oldIndexHeaderP);
290  }
291  }
292 
293  public void CommitIndexSet(IIndexSet indexSet) {
294  var removedBlocks = new List<IndexBlock>();
295 
296  lock (this) {
297  var sIndexSet = (SnapshotIndexSet)indexSet;
298  var indices = sIndexSet.AllIndices.ToList();
299 
300  try {
301  try {
302  Store.Lock();
303 
304  // For each Index in the index set,
305  foreach (var index in indices) {
306  int indexNum = index.IndexNumber;
307 
308  // The IndexBlock we are changing
309  var curIndexBlock = indexBlocks[indexNum];
310 
311  // Get all the blocks in the list
312  var blocks = index.AllBlocks.ToList();
313 
314  // Make up a new block list for this index set.
315  var area = Store.CreateArea(16 + (blocks.Count * 28));
316  long blockP = area.Id;
317  area.WriteInt4(1); // version
318  area.WriteInt4(0); // reserved
319  area.WriteInt8(blocks.Count); // block count
320 
321  foreach (var block in blocks) {
322  var mappedBlock = (IMappedBlock)block;
323 
324  long bottomInt = 0;
325  long topInt = 0;
326  int blockSize = mappedBlock.Count;
327  if (blockSize > 0) {
328  bottomInt = mappedBlock.Bottom;
329  topInt = mappedBlock.Top;
330  }
331 
332  long blockPointer = mappedBlock.BlockPointer;
333 
334  // Is the block new or was it changed?
335  if (blockPointer == -1 || mappedBlock.HasChanged) {
336  // If this isn't -1 then write this sector on the list of
337  // sectors to delete during GC.
338  if (blockPointer != -1)
339  curIndexBlock.AddDeletedArea(blockPointer);
340 
341  // This is a new block or a block that's been changed
342  // Write the block to the file system
343  blockPointer = mappedBlock.Flush();
344  }
345 
346  area.WriteInt8(bottomInt);
347  area.WriteInt8(topInt);
348  area.WriteInt8(blockPointer);
349  area.WriteInt4(blockSize | (((int)mappedBlock.CompactType) << 24));
350  }
351 
352  // Finish initializing the area
353  area.Flush();
354 
355  // Add the deleted blocks
356  var deletedBlocks = index.DeletedBlocks.ToArray();
357  for (int i = 0; i < deletedBlocks.Length; ++i) {
358  long delBlockP = deletedBlocks[i].BlockPointer;
359  if (delBlockP != -1)
360  curIndexBlock.AddDeletedArea(delBlockP);
361  }
362 
363  // Mark the current block as deleted
364  curIndexBlock.MarkAsDeleted();
365 
366  // Now create a new IndexBlock object
367  var newIndexBlock = new IndexBlock(this, indexNum, curIndexBlock.BlockSize, blockP);
368  newIndexBlock.Parent = curIndexBlock;
369 
370  // Add reference to the new one
371  newIndexBlock.AddReference();
372 
373  // Update the index_blocks list
374  indexBlocks[indexNum] = newIndexBlock;
375 
376  // We remove this later.
377  removedBlocks.Add(curIndexBlock);
378  }
379 
380  // Commit the new index header (index_blocks)
381  CommitIndexHeader();
382  } finally {
383  Store.Unlock();
384  }
385 
386  // Commit finished.
387 
388  } catch (IOException e) {
389  throw new InvalidOperationException("Error while committing index changed", e);
390  }
391 
392  }
393 
394  // Remove all the references for the changed blocks,
395  foreach (var block in removedBlocks) {
396  block.RemoveReference();
397  }
398  }
399 
400  public void CommitDropIndex(int indexNum) {
401  lock (this) {
402  // The IndexBlock we are dropping
403  var curIndexBlock = indexBlocks[indexNum];
404  int blockSize = curIndexBlock.BlockSize;
405 
406  try {
407  Store.Lock();
408 
409  // Add all the elements to the deleted areas in the block
410  var allBlockPointers = curIndexBlock.GetBlockPointers();
411  foreach (long area in allBlockPointers) {
412  curIndexBlock.AddDeletedArea(area);
413  }
414 
415  // Mark the current block as deleted
416  curIndexBlock.MarkAsDeleted();
417 
418  // Make up a new blank block list for this index set.
419  long blockP = CreateNewBlock();
420 
421  // Now create a new IndexBlock object
422  var newIndexBlock = new IndexBlock(this, indexNum, blockSize, blockP);
423 
424  // Add reference to the new one
425  newIndexBlock.AddReference();
426  // Remove reference to the old
427  curIndexBlock.RemoveReference();
428  // Update the index_blocks list
429  indexBlocks[indexNum] = newIndexBlock;
430 
431  // Commit the new index header (index_blocks)
432  CommitIndexHeader();
433  } finally {
434  Store.Unlock();
435  }
436  }
437  }
438 
439  #region IndexBlock
440 
441  #endregion
442  }
443 }
The context of a single database within a system.
void CommitDropIndex(int indexNum)
void PrepareIndexLists(int count, int type, int blockSize)
long Position
Returns or sets the current position of the pointer within the area.
Definition: IArea.cs:48
void Open(long startPointer)
void GetAreasUsed(List< long > list)
void DeleteAreas(IEnumerable< int > deletedAreas)
An interface for access the contents of an area of a store.
Definition: IArea.cs:23
An object that access to a set of indexes.
Definition: IIndexSet.cs:27
void AddDeletedArea(long pointer)
Definition: IndexBlock.cs:185
IndexSetStore(IDatabaseContext databaseContext, IStore store)
long Id
Returns the unique identifier that represents this area.
Definition: IArea.cs:31
void CommitIndexSet(IIndexSet indexSet)
A store is a resource where areas can be allocated and freed to store information (a memory allocator...
Definition: IStore.cs:56