DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
TableSource.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;
19 using System.Collections.Generic;
20 using System.IO;
21 using System.Linq;
22 using System.Text;
23 
24 using Deveel.Data.Caching;
25 using Deveel.Data.Index;
26 using Deveel.Data.Services;
27 using Deveel.Data.Sql;
28 using Deveel.Data.Sql.Objects;
29 using Deveel.Data.Store;
31 using Deveel.Data.Types;
32 
33 namespace Deveel.Data.Sql.Tables {
37 
39  private long indexHeaderOffset;
40  private long listHeaderOffset;
41  private IArea headerArea;
42  private long firstDeleteChainRecord;
43 
44  private long sequenceId;
45 
46  private bool isClosed;
47  private int rootLock;
48 
49  internal TableSource(TableSourceComposite composite, IStoreSystem storeSystem, IObjectStore objStore, int tableId, string sourceName) {
50  if (composite == null)
51  throw new ArgumentNullException("composite");
52 
53  Composite = composite;
54  StoreSystem = storeSystem;
55  ObjectStore = objStore;
56  TableId = tableId;
57  SourceName = sourceName;
58 
59  GC = new TableSourceGC(this);
60 
61  CellCache = composite.DatabaseContext.ResolveService<ITableCellCache>();
62 
63  // Generate the name of the store file name.
64  StoreIdentity = MakeSourceIdentity(composite.DatabaseContext.SystemContext, tableId, sourceName);
65  }
66 
67  public TableSourceComposite Composite { get; private set; }
68 
70  get { return Composite.DatabaseContext; }
71  }
72 
74  get { return Composite.Database; }
75  }
76 
78  get { return DatabaseContext.SystemContext; }
79  }
80 
81  private IStoreSystem StoreSystem { get; set; }
82 
83  public int TableId { get; private set; }
84 
85  public ObjectName TableName {
86  get { return TableInfo.TableName; }
87  }
88 
89  public string SourceName { get; private set; }
90 
91  public bool IsRootLocked {
92  get {
93  lock (this) {
94  return rootLock > 0;
95  }
96  }
97  }
98 
99  public TableInfo TableInfo { get; private set; }
100 
101  public int ColumnCount {
102  get { return TableInfo.ColumnCount; }
103  }
104 
105  public int RawRowCount {
106  get {
107  lock (recordList) {
108  long total = recordList.NodeCount;
109  // 32-bit row limitation here - we should return a long.
110  return (int)total;
111  }
112  }
113  }
114 
115  public long CurrentUniqueId {
116  get {
117  lock (recordList) {
118  return sequenceId - 1;
119  }
120  }
121  }
122 
123  public bool CanCompact {
124  get {
125  // TODO: We should perform some analysis on the data to decide if a
126  // compact is necessary or not.
127  return true;
128  }
129  }
130 
131  public bool IsReadOnly {
132  get { return DatabaseContext.SystemContext.ReadOnly(); }
133  }
134 
135  public IndexSetInfo IndexSetInfo { get; private set; }
136 
137  public bool IsClosed {
138  get {
139  lock (this) {
140  return isClosed;
141  }
142  }
143  protected set {
144  lock (this) {
145  isClosed = value;
146  }
147  }
148  }
149 
150  public bool HasChangesPending {
151  get {
152  lock (this) {
153  return tableIndices.HasChangesPending;
154  }
155  }
156  }
157 
158  public string StoreIdentity { get; private set; }
159 
160  public IStore Store { get; private set; }
161 
162  public IObjectStore ObjectStore { get; private set; }
163 
164  public TableSourceGC GC { get; private set; }
165 
166  public bool HasShutdown { get; private set; }
167 
168  private ITableCellCache CellCache { get; set; }
169 
170  public bool CellCaching {
171  get { return CellCache != null; }
172  }
173 
174  public bool Exists() {
175  return StoreSystem.StoreExists(StoreIdentity);
176  }
177 
178  private void ClearLocks() {
179  lock (this) {
180  rootLock = 0;
181  }
182  }
183 
184  public void Close(bool dropPending) {
185  if (IsClosed)
186  return;
187 
188  lock (this) {
189  // NOTE: This method MUST be synchronized over the table to prevent
190  // establishing a root Lock on this table. If a root Lock is established
191  // then the collection event could fail.
192 
193  lock (recordList) {
194  // If we are root locked, we must become un root locked.
195  ClearLocks();
196 
197  try {
198  try {
199  Store.Lock();
200 
201  // Force a garbage collection event.
202  if (!IsReadOnly)
203  GC.Collect(true);
204 
205  // If we are closing pending a drop, we need to remove all blob
206  // references input the table.
207  // NOTE: This must only happen after the above collection event.
208  if (dropPending) {
209  // Scan and remove all blob references for this dropped table.
210  ReleaseObjects();
211  }
212  } finally {
213  Store.Unlock();
214  }
215  } catch (Exception) {
216  // TODO: Register the error to the logs
217  }
218 
219  // Synchronize the store
220  indexSetStore.Close();
221 
222  // Close the store input the store system.
223  StoreSystem.CloseStore(Store);
224 
225  TableInfo = null;
226  IsClosed = true;
227  }
228  }
229  }
230 
231  private void ReleaseObjects() {
232  lock (recordList) {
233  long elements = recordList.NodeCount;
234  for (long rowNumber = 0; rowNumber < elements; ++rowNumber) {
235  var a = recordList.GetRecord(rowNumber);
236  var status = (RecordState) a.ReadInt4();
237  // Is the record not deleted?
238  if (status != RecordState.Deleted) {
239  // Get the record pointer
240  long recordPointer = a.ReadInt8();
241  ReleaseRowObjects(recordPointer);
242  }
243  }
244  }
245  }
246 
247  public bool Drop() {
248  throw new NotImplementedException();
249  }
250 
251  public void Open() {
252  bool needsCheck = OpenTable();
253 
254  // Create table indices
255  tableIndices = new VersionedTableIndexList(this);
256 
257  // Load internal state
258  LoadInternal();
259 
260  if (needsCheck) {
261  // Do an opening scan of the table. Any records that are uncommited
262  // must be marked as deleted.
263  DoOpeningScan();
264  }
265  }
266 
267  private void DoOpeningScan() {
268  lock (this) {
269  // ASSERTION: No root locks and no pending transaction changes,
270  // VERY important we assert there's no pending transactions.
271  if (IsRootLocked || HasChangesPending)
272  // This shouldn't happen if we are calling from 'open'.
273  throw new Exception("Odd, we are root locked or have pending journal changes.");
274 
275  // This is pointless if we are in Read only mode.
276  if (!IsReadOnly) {
277  // Get the master index of rows in this table
278  var indexSet = CreateIndexSet();
279  var masterIndex = indexSet.GetIndex(0);
280 
281  // NOTE: We assume the index information is correct and that the
282  // allocation information is potentially bad.
283 
284  int rowCount = RawRowCount;
285  for (int rowNumber = 0; rowNumber < rowCount; ++rowNumber) {
286  // Is this record marked as deleted?
287  if (!IsRecordDeleted(rowNumber)) {
288  // Get the type flags for this record.
289  var type = ReadRecordState(rowNumber);
290 
291  // Check if this record is marked as committed removed, or is an
292  // uncommitted record.
293  if (type == RecordState.CommittedRemoved ||
294  type == RecordState.Uncommitted) {
295  // Check it's not in the master index...
296  if (!masterIndex.Contains(rowNumber)) {
297  // Delete it.
298  DoHardRowRemove(rowNumber);
299  } else {
300  // Mark the row as committed added because it is in the index.
301  WriteRecordState(rowNumber, RecordState.CommittedAdded);
302 
303  }
304  } else {
305  // Must be committed added. Check it's indexed.
306  if (!masterIndex.Contains(rowNumber)) {
307  // Not indexed, so data is inconsistant.
308 
309  // Mark the row as committed removed because it is not in the
310  // index.
311  WriteRecordState(rowNumber, RecordState.CommittedRemoved);
312 
313  }
314  }
315  } else {
316  // if deleted
317  // Check this record isn't in the master index.
318  if (masterIndex.Contains(rowNumber)) {
319  // It's in the master index which is wrong! We should remake the
320  // indices.
321 
322  // Mark the row as committed added because it is in the index.
323  WriteRecordState(rowNumber, RecordState.CommittedAdded);
324 
325  }
326  }
327  } // for (int i = 0 ; i < row_count; ++i)
328 
329  // Dispose the index set
330  indexSet.Dispose();
331  }
332 
333  ScanForLeaks();
334  }
335  }
336 
337  private void ScanForLeaks() {
338  lock (recordList) {
339  // The list of pointers to areas (as Long).
340  var usedAreas = new List<long>();
341 
342  usedAreas.Add(headerArea.Id);
343 
344  headerArea.Position = 16;
345  // Add the DataTableInfo and DataIndexSetInfo objects
346  usedAreas.Add(headerArea.ReadInt8());
347  usedAreas.Add(headerArea.ReadInt8());
348 
349  // Add all the used areas input the list_structure itself.
350  recordList.GetAreasUsed(usedAreas);
351 
352  // Adds all the user areas input the index store.
353  indexSetStore.GetAreasUsed(usedAreas);
354 
355  // Search the list structure for all areas
356  long elements = recordList.NodeCount;
357  for (long i = 0; i < elements; ++i) {
358  var area = recordList.GetRecord(i);
359  var status = (RecordState) area.ReadInt4();
360  if (status != RecordState.Deleted) {
361  usedAreas.Add(area.ReadInt8());
362  }
363  }
364 
365  // Following depends on store implementation
366  if (Store is StoreBase) {
367  var aStore = (StoreBase)Store;
368  var leakedAreas = aStore.FindAllocatedAreasNotIn(usedAreas).ToList();
369  if (leakedAreas.Count == 0) {
370  } else {
371  foreach (long areaPointer in leakedAreas) {
372  Store.DeleteArea(areaPointer);
373  }
374  }
375  }
376  }
377  }
378 
379  private bool OpenTable() {
380  // Open the store.
381  Store = StoreSystem.OpenStore(StoreIdentity);
382  bool needCheck = !Store.ClosedClean;
383 
384  // Setup the list structure
385  recordList = new FixedRecordList(Store, 12);
386 
387  // Read and setup the pointers
388  ReadStoreHeaders();
389 
390  return needCheck;
391  }
392 
394  return indexSetStore.GetSnapshotIndex();
395  }
396 
397  public void AddIndex(IndexInfo indexInfo) {
398  lock (this) {
399  // TODO: are there other checks to be done here?
400 
401  IndexSetInfo.AddIndex(indexInfo);
402  }
403  }
404 
405  private void CommitIndexSet(IIndexSet indexSet) {
406  indexSetStore.CommitIndexSet(indexSet);
407  indexSet.Dispose();
408  }
409 
410  private void SetTableInfo(TableInfo info) {
411  lock (this) {
412  // Check table_id isn't too large.
413  if ((TableId & 0x0F0000000) != 0)
414  throw new InvalidOperationException("'table_id' exceeds maximum possible keys.");
415 
416  info.Establish(TableId);
417  TableInfo = info;
418 
419  // Create table indices
420  tableIndices = new VersionedTableIndexList(this);
421 
422  // Setup the DataIndexSetInfo
423  SetIndexSetInfo();
424  }
425  }
426 
427  private void SetIndexSetInfo() {
428  lock (this) {
429  // Create the initial DataIndexSetInfo object.
431  foreach (var colInfo in TableInfo) {
432  if (colInfo.IsIndexable) {
433  var indexName = String.Format("IDX_{0}", colInfo.ColumnName);
434  var indexType = colInfo.IndexType;
435  if (String.IsNullOrEmpty(indexType))
436  indexType = DefaultIndexTypes.InsertSearch;
437 
438  IndexSetInfo.AddIndex(new IndexInfo(indexName, indexType, new[] {colInfo.ColumnName}, false));
439  }
440  }
441  }
442  }
443 
444  private void LoadInternal() {
445  lock (this) {
446  // Set up the stat keys.
447  // TODO:
448  isClosed = false;
449  }
450  }
451 
452  public void Create(TableInfo tableInfo) {
453  // Set the data table info object
454  SetTableInfo(tableInfo);
455 
456  // Load internal state
457  LoadInternal();
458 
459  // Set up internal state of this object
460  //TableId = tableInfo.Id;
461 
462  CreateTable();
463  }
464 
465  private static string MakeSourceIdentity(ISystemContext context, int tableId, string tableName) {
466  string str = tableName.Replace('.', '_').ToLower();
467 
468  // Go through each character and remove each non a-z,A-Z,0-9,_ character.
469  // This ensure there are no strange characters in the file name that the
470  // underlying OS may not like.
471  StringBuilder osifiedName = new StringBuilder();
472  int count = 0;
473  for (int i = 0; i < str.Length || count > 64; ++i) {
474  char c = str[i];
475  if ((c >= 'a' && c <= 'z') ||
476  (c >= 'A' && c <= 'Z') ||
477  (c >= '0' && c <= '9') ||
478  c == '_') {
479  osifiedName.Append(c);
480  ++count;
481  }
482  }
483 
484  return String.Format("{0}_{1}", tableId, osifiedName);
485  }
486 
487  private void CreateTable() {
488  // Initially set the table sequence_id to 1
489  sequenceId = 1;
490 
491  // Create and open the store.
492  Store = StoreSystem.CreateStore(StoreIdentity);
493 
494  try {
495  Store.Lock();
496 
497  // Setup the list structure
498  recordList = new FixedRecordList(Store, 12);
499  } finally {
500  Store.Unlock();
501  }
502 
503  // Initialize the store to an empty state,
504  SetupInitialStore();
505  indexSetStore.PrepareIndexLists(TableInfo.ColumnCount + 1, 1, 1024);
506  }
507 
508  private void SetupInitialStore() {
509  byte[] tableInfoBuf;
510  using (var stream = new MemoryStream()) {
511  var writer = new BinaryWriter(stream, Encoding.Unicode);
512  writer.Write(1);
513  TableInfo.Serialize(TableInfo, writer);
514 
515  tableInfoBuf = stream.ToArray();
516  }
517 
518  byte[] indexSetInfoBuf;
519  using (var stream = new MemoryStream()) {
520  var writer = new BinaryWriter(stream, Encoding.Unicode);
521  writer.Write(1);
522 
523  IndexSetInfo.SerialiazeTo(stream);
524 
525  indexSetInfoBuf = stream.ToArray();
526  }
527 
528  try {
529  Store.Lock();
530 
531  // Allocate an 80 byte header
532  var headerWriter = Store.CreateArea(80);
533  long headerPointer = headerWriter.Id;
534  // Allocate space to store the DataTableInfo serialization
535  var dataTableDefWriter = Store.CreateArea(tableInfoBuf.Length);
536  long tableInfoOffset = dataTableDefWriter.Id;
537  // Allocate space to store the DataIndexSetInfo serialization
538  var indexSetWriter = Store.CreateArea(indexSetInfoBuf.Length);
539  long indexSetInfoPointer = indexSetWriter.Id;
540 
541  // Allocate space for the list header
542  listHeaderOffset = recordList.Create();
543  recordList.WriteDeleteHead(-1);
544  firstDeleteChainRecord = -1;
545 
546  // Create the index store
547  indexSetStore = new IndexSetStore(DatabaseContext, Store);
548  indexHeaderOffset = indexSetStore.Create();
549 
550  // Write the main header
551  headerWriter.WriteInt4(1); // Version
552  headerWriter.WriteInt4(TableId); // table id
553  headerWriter.WriteInt8(sequenceId); // initial sequence id
554  headerWriter.WriteInt8(tableInfoOffset); // pointer to DataTableInfo
555  headerWriter.WriteInt8(indexSetInfoPointer); // pointer to DataIndexSetInfo
556  headerWriter.WriteInt8(indexHeaderOffset); // index header pointer
557  headerWriter.WriteInt8(listHeaderOffset); // list header pointer
558  headerWriter.Flush();
559 
560  // Write the table info
561  dataTableDefWriter.Write(tableInfoBuf, 0, tableInfoBuf.Length);
562  dataTableDefWriter.Flush();
563 
564  // Write the index set info
565  indexSetWriter.Write(indexSetInfoBuf, 0, indexSetInfoBuf.Length);
566  indexSetWriter.Flush();
567 
568  // Set the pointer to the header input the reserved area.
569  var fixedArea = Store.GetArea(-1);
570  fixedArea.WriteInt8(headerPointer);
571  fixedArea.Flush();
572 
573  // Set the header area
574  headerArea = Store.GetArea(headerPointer);
575  } finally {
576  Store.Unlock();
577  }
578  }
579 
580  private void ReadStoreHeaders() {
581  // Read the fixed header
582  var fixedArea = Store.GetArea(-1);
583 
584  // Set the header area
585  headerArea = Store.GetArea(fixedArea.ReadInt8());
586 
587  // Open a stream to the header
588  var version = headerArea.ReadInt4(); // version
589  if (version != 1)
590  throw new IOException("Incorrect version identifier.");
591 
592  TableId = headerArea.ReadInt4(); // table_id
593  sequenceId = headerArea.ReadInt8(); // sequence id
594  long infoPointer = headerArea.ReadInt8(); // pointer to DataTableInfo
595  long indexInfoPointer = headerArea.ReadInt8(); // pointer to DataIndexSetInfo
596  indexHeaderOffset = headerArea.ReadInt8(); // pointer to index header
597  listHeaderOffset = headerArea.ReadInt8(); // pointer to list header
598 
599  // Read the table info
600  using (var stream = Store.GetAreaInputStream(infoPointer)) {
601  var reader = new BinaryReader(stream, Encoding.Unicode);
602  version = reader.ReadInt32();
603  if (version != 1)
604  throw new IOException("Incorrect TableInfo version identifier.");
605 
606  var userTypeResolver = new TypeResolver(Database);
607  TableInfo = TableInfo.Deserialize(stream, userTypeResolver);
608  TableInfo.Establish(TableId);
609  }
610 
611  // Read the data index set info
612  using (var stream = Store.GetAreaInputStream(indexInfoPointer)) {
613  var reader = new BinaryReader(stream, Encoding.Unicode);
614  version = reader.ReadInt32();
615  if (version != 1)
616  throw new IOException("Incorrect IndexSetInfo version identifier.");
617 
618  IndexSetInfo = Sql.IndexSetInfo.DeserializeFrom(stream);
619  }
620 
621  // Read the list header
622  recordList.Open(listHeaderOffset);
623  firstDeleteChainRecord = recordList.ReadDeleteHead();
624 
625  // Init the index store
626  indexSetStore = new IndexSetStore(DatabaseContext, Store);
627  try {
628  indexSetStore.Open(indexHeaderOffset);
629  } catch (IOException) {
630  // If this failed try writing output a new empty index set.
631  // ISSUE: Should this occur here? This is really an attempt at repairing
632  // the index store.
633  indexSetStore = new IndexSetStore(DatabaseContext, Store);
634  indexHeaderOffset = indexSetStore.Create();
635  indexSetStore.PrepareIndexLists(TableInfo.ColumnCount + 1, 1, 1024);
636  headerArea.Position = 32;
637  headerArea.WriteInt8(indexHeaderOffset);
638  headerArea.Position = 0;
639  headerArea.Flush();
640  }
641  }
642 
643  public long GetNextUniqueId() {
644  lock (recordList) {
645  long v = sequenceId;
646  ++sequenceId;
647  if (HasShutdown)
648  throw new Exception("IO operation while shutting down.");
649 
650  try {
651  try {
652  Store.Lock();
653  headerArea.Position = 4 + 4;
654  headerArea.WriteInt8(sequenceId);
655  headerArea.Flush();
656  } finally {
657  Store.Unlock();
658  }
659  } catch (IOException e) {
660  throw new InvalidOperationException("IO Error: " + e.Message);
661  }
662 
663  return v;
664  }
665  }
666 
667  public void SetUniqueId(long value) {
668  lock (recordList) {
669  sequenceId = value;
670  if (HasShutdown)
671  throw new Exception("IO operation while shutting down.");
672 
673  try {
674  try {
675  Store.Lock();
676  headerArea.Position = 4 + 4;
677  headerArea.WriteInt8(sequenceId);
678  headerArea.Flush();
679  } finally {
680  Store.Unlock();
681  }
682  } catch (IOException e) {
683  throw new InvalidOperationException("IO Error: " + e.Message, e);
684  }
685  }
686  }
687 
689  return CreateTableAtCommit(transaction, new TableEventRegistry(this));
690  }
691 
693  return new TransactionTable(transaction, this, registry);
694  }
695 
696  internal void CommitTransactionChange(int commitId, TableEventRegistry change, IIndexSet indexSet) {
697  lock (this) {
698  // ASSERT: Can't do this if source is Read only.
699  if (IsReadOnly)
700  throw new InvalidOperationException("Can't commit transaction journal, table is Read only.");
701 
702  change.CommitId = commitId;
703 
704  try {
705  // Add this registry to the multi version table indices log
706  tableIndices.AddRegistry(change);
707 
708  // Write the modified index set to the index store
709  // (Updates the index file)
710  CommitIndexSet(indexSet);
711 
712  // Update the state of the committed added data to the file system.
713  // (Updates data to the allocation file)
714  //
715  // ISSUE: This can add up to a lot of changes to the allocation file and
716  // the runtime could potentially be terminated in the middle of
717  // the update. If an interruption happens the allocation information
718  // may be incorrectly flagged. The type of corruption this would
719  // result in would be;
720  // + From an 'update' the updated record may disappear.
721  // + From a 'delete' the deleted record may not delete.
722  // + From an 'insert' the inserted record may not insert.
723  //
724  // Note, the possibility of this type of corruption occuring has been
725  // minimized as best as possible given the current architecture.
726  // Also note that is not possible for a table file to become corrupted
727  // beyond recovery from this issue.
728 
729  foreach (var entry in change) {
730  if (entry is TableRowEvent) {
731  var rowEvent = (TableRowEvent) entry;
732  var rowIndex = rowEvent.RowNumber;
733 
734  if (rowEvent.EventType == TableRowEventType.Add) {
735  // Record commit added
736  var oldType = WriteRecordState(rowIndex, RecordState.CommittedAdded);
737 
738  // Check the record was in an uncommitted state before we changed
739  // it.
740  if (oldType != RecordState.Uncommitted) {
741  WriteRecordState(rowIndex, oldType);
742  throw new InvalidOperationException(String.Format("Record {0} of table {1} was not in an uncommitted state!",
743  rowIndex, TableName));
744  }
745  } else if (rowEvent.EventType == TableRowEventType.Remove) {
746  // Record commit removed
747  var oldType = WriteRecordState(rowIndex, RecordState.CommittedRemoved);
748 
749  // Check the record was in an added state before we removed it.
750  if (oldType != RecordState.CommittedAdded) {
751  WriteRecordState(rowIndex, oldType);
752  throw new InvalidOperationException(String.Format("Record {0} of table {1} was not in an added state!", rowIndex,
753  TableName));
754  }
755 
756  // Notify collector that this row has been marked as deleted.
757  GC.DeleteRow(rowIndex);
758  }
759  }
760  }
761  } catch (IOException e) {
762  throw new InvalidOperationException("IO Error: " + e.Message, e);
763  }
764 
765  }
766  }
767 
768  public RecordState WriteRecordState(int rowNumber, RecordState state) {
769  lock (recordList) {
770  if (HasShutdown)
771  throw new IOException("IO operation while shutting down.");
772 
773  // Find the record entry input the block list.
774  var blockArea = recordList.GetRecord(rowNumber);
775  var pos = blockArea.Position;
776  // Get the status.
777  var oldStatus = (RecordState) blockArea.ReadInt4();
778 
779  // Write the new status
780  try {
781  Store.Lock();
782 
783  blockArea.Position = pos;
784  blockArea.WriteInt4((int)state);
785  blockArea.Flush();
786  } finally {
787  Store.Unlock();
788  }
789 
790  return oldStatus;
791  }
792  }
793 
794  public RecordState ReadRecordState(int rowNumber) {
795  lock (recordList) {
796  // Find the record entry input the block list.
797  var blockArea = recordList.GetRecord(rowNumber);
798  // Get the status.
799  return (RecordState) blockArea.ReadInt4();
800  }
801  }
802 
803  public bool IsRecordDeleted(int rowNumber) {
804  var state = ReadRecordState(rowNumber);
805  return state == RecordState.Deleted;
806  }
807 
808  private void DoHardRowRemove(int rowNumber) {
809  lock (this) {
810  // Internally delete the row,
811  OnDeleteRow(rowNumber);
812 
813  // Update stats
814  //TODO:
815  }
816  }
817 
818  internal void HardRemoveRow(int rowIndex) {
819  lock (this) {
820  // ASSERTION: We are not under a root Lock.
821  if (IsRootLocked)
822  throw new InvalidOperationException("Cannot remove row, table is locked");
823 
824  var typeKey = ReadRecordState(rowIndex);
825  // Check this record is marked as committed removed.
826  if (typeKey != RecordState.CommittedRemoved)
827  throw new InvalidOperationException(String.Format("The row {0} is not marked as committed removed", rowIndex));
828 
829  DoHardRowRemove(rowIndex);
830  }
831  }
832 
833  internal bool HardCheckAndReclaimRow(int recordIndex) {
834  lock (this) {
835  // ASSERTION: We are not under a root Lock.
836  if (IsRootLocked)
837  throw new InvalidOperationException("Assertion failed: Can't remove row, table is under a root Lock.");
838 
839  // Row already deleted?
840  if (IsRecordDeleted(recordIndex))
841  return false;
842 
843  var typeKey = ReadRecordState(recordIndex);
844 
845  // Check this record is marked as committed removed.
846  if (typeKey != RecordState.CommittedRemoved)
847  return false;
848 
849  DoHardRowRemove(recordIndex);
850  return true;
851  }
852  }
853 
854  private void OnDeleteRow(int rowIndex) {
855  lock (recordList) {
856  if (HasShutdown)
857  throw new IOException("IO operation while VM shutting down.");
858 
859  // Find the record entry input the block list.
860  var blockArea = recordList.GetRecord(rowIndex);
861  var p = blockArea.Position;
862  var status = (RecordState) blockArea.ReadInt4();
863 
864  // Check it is not already deleted
865  if (status == RecordState.Deleted)
866  throw new IOException("Record is already marked as deleted.");
867 
868  long recordPointer = blockArea.ReadInt8();
869 
870  // Update the status record.
871  try {
872  Store.Lock();
873 
874  blockArea.Position = p;
875  blockArea.WriteInt4((int)RecordState.Deleted);
876  blockArea.WriteInt8(firstDeleteChainRecord);
877  blockArea.Flush();
878  firstDeleteChainRecord = rowIndex;
879 
880  // Update the first_delete_chain_record field input the header
881  recordList.WriteDeleteHead(firstDeleteChainRecord);
882 
883  // If the record contains any references to blobs, remove the reference
884  // here.
885  ReleaseRowObjects(recordPointer);
886 
887  // Free the record from the store
888  Store.DeleteArea(recordPointer);
889  } finally {
890  RemoveRowFromCache(rowIndex);
891  Store.Unlock();
892  }
893  }
894  }
895 
896  private void RemoveRowFromCache(int rowIndex) {
897  if (CellCaching) {
898  var colCount = TableInfo.ColumnCount;
899  for (int i = 0; i < colCount; i++) {
900  CellCache.Remove(DatabaseContext.DatabaseName(), TableId, rowIndex, i);
901  }
902  }
903  }
904 
905  private void ReleaseRowObjects(long recordPointer) {
906  // NOTE: Does this need to be optimized?
907  IArea recordArea = Store.GetArea(recordPointer);
908  recordArea.ReadInt4(); // reserved
909 
910  // Look for any blob references input the row
911  for (int i = 0; i < ColumnCount; ++i) {
912  int ctype = recordArea.ReadInt4();
913  int cellOffset = recordArea.ReadInt4();
914 
915  if (ctype == 1) {
916  // Type 1 is not a large object
917  } else if (ctype == 2) {
918  var curP = recordArea.Position;
919  recordArea.Position = cellOffset + 4 + (ColumnCount * 8);
920 
921  int btype = recordArea.ReadInt4();
922  recordArea.ReadInt4(); // (reserved)
923 
924  if (btype == 0) {
925  long blobRefId = recordArea.ReadInt8();
926 
927  // Release this reference
928  ObjectStore.ReleaseObject(blobRefId);
929  }
930 
931  // Revert the area pointer
932  recordArea.Position = curP;
933  } else {
934  throw new Exception("Unrecognised type.");
935  }
936  }
937  }
938 
939  public int AddRow(Row row) {
940  int rowNumber;
941 
942  lock (this) {
943  rowNumber = OnAddRow(row);
944 
945  } // lock
946 
947  // Update stats
948  // TODO:
949 
950  // Return the record index of the new data in the table
951  return rowNumber;
952  }
953 
954 
955  private long AddToRecordList(long recordPointer) {
956  lock (recordList) {
957  if (HasShutdown)
958  throw new IOException("IO operation while shutting down.");
959 
960  // If there are no free deleted records input the delete chain,
961  if (firstDeleteChainRecord == -1)
962  // Grow the fixed structure to allow more nodes,
963  GrowRecordList();
964 
965  // Pull free block from the delete chain and recycle it.
966  long recycledRecord = firstDeleteChainRecord;
967  var block = recordList.GetRecord(recycledRecord);
968  var recPos = block.Position;
969 
970  // Status of the recycled block
971  var status = (RecordState) block.ReadInt4();
972  if (status != RecordState.Deleted)
973  throw new InvalidOperationException(String.Format("Record {0} is not deleted. ({1})", recPos, status));
974 
975  // The pointer to the next input the chain.
976  long nextChain = block.ReadInt8();
977  firstDeleteChainRecord = nextChain;
978 
979  try {
980  Store.Lock();
981 
982  // Update the first_delete_chain_record field input the header
983  recordList.WriteDeleteHead(firstDeleteChainRecord);
984 
985  // Update the block
986  block.Position = recPos;
987  block.WriteInt4((int)RecordState.Uncommitted);
988  block.WriteInt8(recordPointer);
989  block.Flush();
990  } finally {
991  Store.Unlock();
992  }
993 
994  return recycledRecord;
995  }
996  }
997 
998  private void GrowRecordList() {
999  try {
1000  Store.Lock();
1001 
1002  // Increase the size of the list structure.
1003  recordList.IncreaseSize();
1004 
1005  // The start record of the new size
1006  int newBlockNumber = recordList.BlockCount - 1;
1007  long startIndex = recordList.BlockFirstPosition(newBlockNumber);
1008  long sizeOfBlock = recordList.BlockNodeCount(newBlockNumber);
1009 
1010  // The IArea object for the new position
1011  var a = recordList.GetRecord(startIndex);
1012 
1013  // Set the rest of the block as deleted records
1014  for (long n = 0; n < sizeOfBlock - 1; ++n) {
1015  a.WriteInt4((int)RecordState.Deleted);
1016  a.WriteInt8(startIndex + n + 1);
1017  }
1018 
1019  // The last block is end of delete chain.
1020  a.WriteInt4((int)RecordState.Deleted);
1021  a.WriteInt8(firstDeleteChainRecord);
1022  a.Flush();
1023 
1024  // And set the new delete chain
1025  firstDeleteChainRecord = startIndex;
1026 
1027  // Set the reserved area
1028  recordList.WriteDeleteHead(firstDeleteChainRecord);
1029  } finally {
1030  Store.Unlock();
1031  }
1032  }
1033 
1034  private int OnAddRow(Row row) {
1035  long rowNumber;
1036  int intRowNumber;
1037 
1038  // Write the record to the store.
1039  lock (recordList) {
1040  long recordPointer = WriteRecord(row);
1041 
1042  // Now add this record into the record block list,
1043  rowNumber = AddToRecordList(recordPointer);
1044  intRowNumber = (int)rowNumber;
1045  }
1046 
1047  // Update the cell cache as appropriate
1048  if (CellCaching && row.CanBeCached) {
1049  int rowCells = row.ColumnCount;
1050  for (int i = 0; i < rowCells; ++i) {
1051  // Put the row/column/TObject into the cache.
1052  CellCache.Set(Database.Name, TableId, intRowNumber, i, row.GetValue(i));
1053  }
1054  }
1055 
1056  // Return the record index of the new data input the table
1057  // NOTE: We are casting this from a long to int which means we are limited
1058  // to ~2 billion record references.
1059  return (int)rowNumber;
1060  }
1061 
1062  private long WriteRecord(Row data) {
1063  // Calculate how much space this record will use
1064  int rowCells = data.ColumnCount;
1065 
1066  int[] cellSizes = new int[rowCells];
1067  int[] cellTypes = new int[rowCells];
1068 
1069  try {
1070  Store.Lock();
1071 
1072  // Establish a reference to any blobs input the record
1073  int allRecordsSize = 0;
1074  for (int i = 0; i < rowCells; ++i) {
1075  var cell = data.GetValue(i);
1076  int cellSize;
1077  int cellType;
1078 
1079  if (cell.Value is IObjectRef) {
1080  var largeObjectRef = (IObjectRef)cell.Value;
1081 
1082  cellSize = 16;
1083  cellType = 2;
1084  if (largeObjectRef != null) {
1085  // Tell the blob store interface that we've made a static reference
1086  // to this blob.
1087  ObjectStore.EstablishObject(largeObjectRef.ObjectId.Id);
1088  }
1089  } else {
1090  cellSize = cell.Size;
1091  cellType = 1;
1092  }
1093 
1094  cellSizes[i] = cellSize;
1095  cellTypes[i] = cellType;
1096  allRecordsSize += cellSize;
1097  }
1098 
1099  // Allocate space for the record,
1100  var area = Store.CreateArea(allRecordsSize + (rowCells * 8) + 4);
1101  long recordPointer = area.Id;
1102 
1103  // The record output stream
1104  using (var areaStream = new AreaStream(area)) {
1105  var writer = new BinaryWriter(areaStream);
1106 
1107  // Write the record header first,
1108  writer.Write(0); // reserved for future use
1109  int cellSkip = 0;
1110  for (int i = 0; i < rowCells; ++i) {
1111  writer.Write(cellTypes[i]);
1112  writer.Write(cellSkip);
1113  cellSkip += cellSizes[i];
1114  }
1115 
1116  // Now Write a serialization of the cells themselves,
1117  for (int i = 0; i < rowCells; ++i) {
1118  var obj = data.GetValue(i);
1119  int cellType = cellTypes[i];
1120  if (cellType == 1) {
1121  // Regular object
1122  obj.SerializeValueTo(areaStream, SystemContext);
1123  } else if (cellType == 2) {
1124  // This is a binary large object and must be represented as a ref
1125  // to a blob input the BlobStore.
1126  var largeObjectRef = (IObjectRef)obj.Value;
1127  if (largeObjectRef == null) {
1128  // null value
1129  writer.Write(1);
1130  writer.Write(0); // Reserved for future use
1131  writer.Write(-1L);
1132  } else {
1133  writer.Write(0);
1134  writer.Write(0); // Reserved for future use
1135  writer.Write(largeObjectRef.ObjectId.Id);
1136  }
1137  } else {
1138  throw new IOException("Unrecognised cell type.");
1139  }
1140  }
1141 
1142  // Flush the output
1143  writer.Flush();
1144  }
1145 
1146  // Finish the record
1147  area.Flush();
1148 
1149  // Return the record
1150  return recordPointer;
1151  } finally {
1152  Store.Unlock();
1153  }
1154  }
1155 
1156  public void BuildIndexes() {
1157  lock (this) {
1158  var indexSet = CreateIndexSet();
1159 
1160  var indexSetInfo = IndexSetInfo;
1161 
1162  int rowCount = RawRowCount;
1163 
1164  // Master index is always on index position 0
1165  IIndex masterIndex = indexSet.GetIndex(0);
1166 
1167  // First, update the master index
1168  for (int rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
1169  // If this row isn't deleted, set the index information for it,
1170  if (!IsRecordDeleted(rowIndex)) {
1171  // First add to master inde
1172  if (!masterIndex.UniqueInsertSort(rowIndex))
1173  throw new Exception("Master index entry was duplicated.");
1174  }
1175  }
1176 
1177  // Commit the master index
1178  CommitIndexSet(indexSet);
1179 
1180  // Now go ahead and build each index in this table
1181  int indexCount = indexSetInfo.IndexCount;
1182  for (int i = 0; i < indexCount; ++i) {
1183  BuildIndex(i);
1184  }
1185  }
1186  }
1187 
1188  private void BuildIndex(int indexNumber) {
1189  lock (this) {
1190  var indexSet = CreateIndexSet();
1191 
1192  // Master index is always on index position 0
1193  var masterIndex = indexSet.GetIndex(0);
1194 
1195  // A minimal ITable for constructing the indexes
1196  var minTable = new MinimalTable(this, masterIndex);
1197 
1198  // Set up schemes for the index,
1199  var index = CreateColumnIndex(indexSet, minTable, indexNumber);
1200 
1201  // Rebuild the entire index
1202  int rowCount = RawRowCount;
1203  for (int rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
1204  // If this row isn't deleted, set the index information for it,
1205  if (!IsRecordDeleted(rowIndex))
1206  index.Insert(rowIndex);
1207  }
1208 
1209  // Commit the index
1210  CommitIndexSet(indexSet);
1211  }
1212  }
1213 
1214  internal ColumnIndex CreateColumnIndex(IIndexSet indexSet, ITable table, int columnOffset) {
1215  lock (this) {
1216  var column = TableInfo[columnOffset];
1217  if (!column.IsIndexable ||
1218  (String.IsNullOrEmpty(column.IndexType) ||
1219  column.IndexType.Equals(DefaultIndexTypes.BlindSearch)))
1220  return new BlindSearchIndex(table, columnOffset);
1221 
1222  var indexI = IndexSetInfo.FindIndexForColumns(new[] {column.ColumnName});
1223  return CreateIndexAt(indexSet, table, indexI);
1224  }
1225  }
1226 
1227  private ColumnIndex CreateIndexAt(IIndexSet indexSet, ITable table, int indexI) {
1228  lock (this) {
1229  try {
1230  // Get the IndexDef object
1231  var indexInfo = IndexSetInfo.GetIndex(indexI);
1232 
1233  if (indexInfo == null)
1234  return null;
1235 
1236  string[] cols = indexInfo.ColumnNames;
1237  var tableInfo = TableInfo;
1238  if (cols.Length != 1)
1239  throw new Exception("Multi-column indexes not supported at this time.");
1240 
1241  // If a single column
1242  var colIndex = tableInfo.IndexOfColumn(cols[0]);
1243 
1244  if (indexInfo.IndexType.Equals(DefaultIndexTypes.InsertSearch)) {
1245  // Get the index from the index set and set up the new InsertSearch
1246  // scheme.
1247  var indexList = indexSet.GetIndex(indexInfo.Offset);
1248  return new InsertSearchIndex(table, colIndex, indexList);
1249  }
1250 
1251  // TODO: support metadata from input
1252  return SystemContext.CreateColumnIndex(indexInfo.IndexType, table, colIndex);
1253  } catch (Exception ex) {
1254  throw new InvalidOperationException(
1255  String.Format("An error occurred while creating a colummn for table {0}", TableName), ex);
1256  }
1257  }
1258  }
1259 
1260  public void CopyFrom(int tableId, TableSource destSource, IIndexSet indexSet) {
1261  throw new NotImplementedException();
1262  }
1263 
1264  public void AddLock() {
1265  lock (this) {
1266  // TODO: Emit the stat to the system
1267  ++rootLock;
1268  }
1269  }
1270 
1271  public void RemoveLock() {
1272  lock (this) {
1273  if (!isClosed) {
1274  // TODO: Emit the event to the system
1275 
1276  if (rootLock == 0)
1277  throw new InvalidOperationException("Too many root locks removed!");
1278 
1279  --rootLock;
1280 
1281  // If the last Lock is removed, schedule a possible collection.
1282  if (rootLock == 0)
1283  CheckForCleanup();
1284  }
1285  }
1286  }
1287 
1288  private void CheckForCleanup() {
1289  lock (this) {
1290  GC.Collect(false);
1291  }
1292  }
1293 
1294  public DataObject GetValue(int rowIndex, int columnOffset) {
1295  // NOTES:
1296  // This is called *A LOT*. It's a key part of the 20% of the program
1297  // that's run 80% of the time.
1298  // This performs very nicely for rows that are completely contained within
1299  // 1 sector. However, rows that contain large cells (eg. a large binary
1300  // or a large string) and spans many sectors will not be utilizing memory
1301  // as well as it could.
1302  // The reason is because all the data for a row is Read from the store even
1303  // if only 1 cell of the column is requested. This will have a big
1304  // impact on column scans and searches. The cell cache takes some of this
1305  // performance bottleneck away.
1306  // However, a better implementation of this method is made difficult by
1307  // the fact that sector spans can be compressed. We should perhaps
1308  // revise the low level data storage so only sectors can be compressed.
1309 
1310  // First check if this is within the cache before we continue.
1311  DataObject cell;
1312  if (CellCaching) {
1313  if (CellCache.TryGetValue(Database.Name, TableId, rowIndex, columnOffset, out cell))
1314  return cell;
1315  }
1316 
1317  // We maintain a cache of byte[] arrays that contain the rows Read input
1318  // from the file. If consecutive reads are made to the same row, then
1319  // this will cause lots of fast cache hits.
1320 
1321  long recordPointer = -1;
1322  try {
1323  lock (recordList) {
1324  // Increment the file hits counter
1325  //TODO:
1326  //++sRunFileHits;
1327 
1328  //if (sRunFileHits >= 100) {
1329  // // TODO: Register the stats
1330  // sRunFileHits = 0;
1331  //}
1332 
1333  // Get the node for the record
1334  var listBlock = recordList.GetRecord(rowIndex);
1335  var status = (RecordState) listBlock.ReadInt4();
1336  // Check it's not deleted
1337  if (status == RecordState.Deleted)
1338  throw new InvalidOperationException(String.Format("Record {0} was deleted: unable to read.", rowIndex));
1339 
1340  // Get the pointer to the record we are reading
1341  recordPointer = listBlock.ReadInt8();
1342  }
1343 
1344  // Open a stream to the record
1345  using (var stream = Store.GetAreaInputStream(recordPointer)) {
1346  var reader = new BinaryReader(stream);
1347 
1348  stream.Seek(4 + (columnOffset * 8), SeekOrigin.Current);
1349 
1350  int cellType = reader.ReadInt32();
1351  int cellOffset = reader.ReadInt32();
1352 
1353  int curAt = 8 + 4 + (columnOffset * 8);
1354  int beAt = 4 + (ColumnCount * 8);
1355  int skipAmount = (beAt - curAt) + cellOffset;
1356 
1357  stream.Seek(skipAmount, SeekOrigin.Current);
1358 
1359  // Get the TType for this column
1360  // NOTE: It's possible this call may need optimizing?
1361  var type = TableInfo[columnOffset].ColumnType;
1362 
1363  ISqlObject ob;
1364  if (cellType == 1) {
1365  // If standard object type
1366  ob = type.DeserializeObject(stream);
1367  } else if (cellType == 2) {
1368  // If reference to a blob input the BlobStore
1369  int fType = reader.ReadInt32();
1370  int fReserved = reader.ReadInt32();
1371  long refId = reader.ReadInt64();
1372 
1373  if (fType == 0) {
1374  // Resolve the reference
1375  var objRef = ObjectStore.GetObject(refId);
1376  ob = type.CreateFromLargeObject(objRef);
1377  } else if (fType == 1) {
1378  ob = null;
1379  } else {
1380  throw new Exception("Unknown blob type.");
1381  }
1382  } else {
1383  throw new Exception("Unrecognised cell type input data.");
1384  }
1385 
1386  // Wrap it around a TObject
1387  cell = new DataObject(type, ob);
1388 
1389  // And close the reader.
1390 #if PCL
1391  reader.Dispose();
1392 #else
1393  reader.Close();
1394 #endif
1395  }
1396  } catch (IOException e) {
1397  throw new Exception(String.Format("Error getting cell at ({0}, {1}) pointer = " + recordPointer + ".", rowIndex,
1398  columnOffset), e);
1399  }
1400 
1401  // And WriteByte input the cache and return it.
1402  if (CellCaching) {
1403  CellCache.Set(Database.Name, TableId, rowIndex, columnOffset, cell);
1404  }
1405 
1406  return cell;
1407  }
1408 
1409  #region MinimalTable
1410 
1412  private readonly TableSource source;
1413  private readonly IIndex masterIndex;
1414 
1415  public MinimalTable(TableSource source, IIndex masterIndex) {
1416  this.source = source;
1417  this.masterIndex = masterIndex;
1418  }
1419 
1421  get { return TableInfo.TableName; }
1422  }
1423 
1425  get { return DbObjectType.Table; }
1426  }
1427 
1428  public IEnumerator<Row> GetEnumerator() {
1429  // NOTE: Returns iterator across master index before journal entry
1430  // changes.
1431  var iterator = masterIndex.GetEnumerator();
1432  // Wrap it around a IRowEnumerator object.
1433  return new RowEnumerator(this, iterator);
1434  }
1435 
1436  private class RowEnumerator : IEnumerator<Row> {
1439 
1441  this.table = table;
1442  this.enumerator = enumerator;
1443  }
1444 
1445  public void Dispose() {
1446  table = null;
1447  enumerator = null;
1448  }
1449 
1450  public bool MoveNext() {
1451  return enumerator.MoveNext();
1452  }
1453 
1454  public void Reset() {
1455  enumerator.Reset();
1456  }
1457 
1458  public Row Current {
1459  get { return new Row(table, enumerator.Current); }
1460  }
1461 
1462  object IEnumerator.Current {
1463  get { return Current; }
1464  }
1465  }
1466 
1467  IEnumerator IEnumerable.GetEnumerator() {
1468  return GetEnumerator();
1469  }
1470 
1471  public void Dispose() {
1472 
1473  }
1474 
1476  get { return source.DatabaseContext; }
1477  }
1478 
1480  get { return source.TableInfo; }
1481  }
1482 
1483  public int RowCount {
1484  get {
1485  // NOTE: Returns the number of rows in the master index before journal
1486  // entries have been made.
1487  return masterIndex.Count;
1488  }
1489  }
1490 
1491  public DataObject GetValue(long rowNumber, int columnOffset) {
1492  return source.GetValue((int)rowNumber, columnOffset);
1493  }
1494 
1495  public ColumnIndex GetIndex(int columnOffset) {
1496  throw new NotImplementedException();
1497  }
1498  }
1499 
1500  #endregion
1501 
1502  public IEnumerable<TableEventRegistry> FindChangesSinceCmmit(long commitId) {
1503  lock (this) {
1504  return tableIndices.FindSinceCommit(commitId);
1505  }
1506  }
1507 
1509  lock (this) {
1510  // ASSERT: Can't do this is source is Read only.
1511  if (IsReadOnly)
1512  throw new InvalidOperationException("Can't rollback transaction journal, table is Read only.");
1513 
1514  // Any rows added in the journal are marked as committed deleted and the
1515  // journal is then discarded.
1516 
1517  try {
1518  // Mark all rows in the data_store as appropriate to the changes.
1519  foreach (var tableEvent in registry) {
1520  if (tableEvent is TableRowEvent) {
1521  var rowEvent = (TableRowEvent) tableEvent;
1522  if (rowEvent.EventType == TableRowEventType.Add) {
1523  var oldState = WriteRecordState(rowEvent.RowNumber, RecordState.CommittedRemoved);
1524  if (oldState != RecordState.Uncommitted) {
1525  WriteRecordState(rowEvent.RowNumber, oldState);
1526  throw new InvalidOperationException(String.Format("Record {0} was not in an uncommitted state.",
1527  rowEvent.RowNumber));
1528  }
1529 
1530  GC.DeleteRow(rowEvent.RowNumber);
1531  }
1532  }
1533  }
1534  } catch (IOException e) {
1535  throw new InvalidOperationException("IO Error: " + e.Message, e);
1536  }
1537  }
1538  }
1539 
1540  public void MergeChanges(long commitId) {
1541  lock (this) {
1542  bool allMerged = tableIndices.MergeChanges(commitId);
1543  // If all journal entries merged then schedule deleted row collection.
1544  if (allMerged && !IsReadOnly) {
1545  CheckForCleanup();
1546  }
1547  }
1548  }
1549 
1550  #region TypeResolver
1551 
1553  private readonly IDatabase database;
1554 
1555  public TypeResolver(IDatabase database) {
1556  this.database = database;
1557  }
1558 
1559  public SqlType ResolveType(TypeResolveContext resolveContext) {
1560  using (var session = database.CreateSystemSession()) {
1561  using (var query = session.CreateQuery()) {
1562  return query.Context.ResolveType(resolveContext.TypeName, resolveContext.GetMeta());
1563  }
1564  }
1565  }
1566  }
1567 
1568  #endregion
1569  }
1570 }
void MergeChanges(long commitId)
static TableInfo Deserialize(Stream stream, ITypeResolver typeResolver)
Definition: TableInfo.cs:411
static string MakeSourceIdentity(ISystemContext context, int tableId, string tableName)
Definition: TableSource.cs:465
ColumnIndex GetIndex(int columnOffset)
Gets an index for given column that can be used to select values from this table. ...
Defines the contract to access the data contained into a table of a database.
Definition: ITable.cs:40
A long string in the system.
long BlockFirstPosition(int blockNumber)
TableInfo(ObjectName tableName)
Constructs the object with the given table name.
Definition: TableInfo.cs:54
The context of a single database within a system.
The execution context of a database system, that is defining the configurations and the components us...
Defines the contract for stores that handle lrge objects within a database system.
Definition: IObjectStore.cs:35
IEnumerable< TableEventRegistry > FindChangesSinceCmmit(long commitId)
RowEnumerator(MinimalTable table, IIndexEnumerator< int > enumerator)
void CopyFrom(int tableId, TableSource destSource, IIndexSet indexSet)
ILargeObject GetObject(ObjectId id)
Gets an object that was previously created for the given unique identifier.
Definition: ObjectStore.cs:490
DataObject GetValue(int columnOffset)
Gets or the value of a cell of the row at the given offset.
Definition: Row.cs:203
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 HardRemoveRow(int rowIndex)
Definition: TableSource.cs:818
Represents a database object, such as a table, a trigger, a type or a column.
Definition: IDbObject.cs:24
void Create(TableInfo tableInfo)
Definition: TableSource.cs:452
int FindIndexForColumns(string[] columnNames)
Definition: IndexSetInfo.cs:99
An object that creates and manages the IStore objects that the database engine uses to represent itse...
Definition: IStoreSystem.cs:31
ObjectName FullName
Gets the fully qualified name of the object used to resolve it uniquely within the database...
Definition: IDbObject.cs:30
void Close(bool dropPending)
Definition: TableSource.cs:184
This is the context of a database system, that handles the configurations and services used by all th...
The default implementation of a database in a system.
Definition: Database.cs:38
Describes the name of an object within a database.
Definition: ObjectName.cs:44
SqlType ResolveType(TypeResolveContext resolveContext)
IndexSetInfo(ObjectName tableName, IEnumerable< IndexInfo > indexes, bool readOnly)
Definition: IndexSetInfo.cs:28
Database(DatabaseSystem system, IDatabaseContext context)
Definition: Database.cs:39
ISqlObject Value
Gets the underlined value that is handled.
Definition: DataObject.cs:84
A single row in a table of a database.
Definition: Row.cs:44
void Open(long startPointer)
void GetAreasUsed(List< long > list)
void CommitIndexSet(IIndexSet indexSet)
Definition: TableSource.cs:405
DataObject GetValue(long rowNumber, int columnOffset)
Gets a single cell within the table that is located at the given column offset and row...
IMutableTable CreateTableAtCommit(ITransaction transaction, TableEventRegistry registry)
Definition: TableSource.cs:692
The representation of a single database in the system.
Definition: IDatabase.cs:40
IndexInfo GetIndex(int offset)
Definition: IndexSetInfo.cs:65
RecordState WriteRecordState(int rowNumber, RecordState state)
Definition: TableSource.cs:768
RecordState
An enumeration that represents the various states of a record.
Definition: RecordState.cs:23
IMutableTable CreateTableAtCommit(ITransaction transaction)
Definition: TableSource.cs:688
void SetTableInfo(TableInfo info)
Definition: TableSource.cs:410
ObjectName TableName
Gets the fully qualified name of the table that is ensured to be unique within the system...
Definition: TableInfo.cs:97
void RollbackTransactionChange(TableEventRegistry registry)
void GetAreasUsed(IList< long > usedAreas)
Defines the contract for a valid SQL Object
Definition: ISqlObject.cs:23
TableSource(TableSourceComposite composite, IStoreSystem storeSystem, IObjectStore objStore, int tableId, string sourceName)
Definition: TableSource.cs:49
void AddIndex(IndexInfo indexInfo)
Definition: IndexSetInfo.cs:58
ColumnIndex CreateColumnIndex(IIndexSet indexSet, ITable table, int columnOffset)
DataObject GetValue(int rowIndex, int columnOffset)
IIndex GetIndex(int index)
Gets a mutable implementation of IIndex for the given index number in this set of indices...
VersionedTableIndexList tableIndices
Definition: TableSource.cs:36
void SerializeValueTo(Stream stream, ISystemContext systemContext)
Definition: DataObject.cs:920
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
An interface for access the contents of an area of a store.
Definition: IArea.cs:23
DbObjectType ObjectType
Gets the type of database object that the implementation is for
Definition: IDbObject.cs:35
Defines the properties of a specific SQL Type and handles the values compatible.
Definition: SqlType.cs:33
void DoHardRowRemove(int rowNumber)
Definition: TableSource.cs:808
ColumnIndex CreateIndexAt(IIndexSet indexSet, ITable table, int indexI)
An object that access to a set of indexes.
Definition: IIndexSet.cs:27
void AddIndex(IndexInfo indexInfo)
Definition: TableSource.cs:397
An interface for querying and accessing an index of primitive integers.
Definition: IIndex.cs:37
void BuildIndex(int indexNumber)
long AddToRecordList(long recordPointer)
Definition: TableSource.cs:955
long BlockNodeCount(int blockNumber)
void ReleaseRowObjects(long recordPointer)
Definition: TableSource.cs:905
bool IsRecordDeleted(int rowNumber)
Definition: TableSource.cs:803
int ColumnCount
Gets a count of the columns defined by this object.
Definition: TableInfo.cs:159
void SerialiazeTo(Stream stream)
DatabaseContext(ISystemContext systemContext, IConfiguration configuration)
int ColumnCount
Gets the number of columns in the parent table of this row.
Definition: Row.cs:156
RecordState ReadRecordState(int rowNumber)
Definition: TableSource.cs:794
The simplest implementation of a transaction.
Definition: ITransaction.cs:30
void CommitTransactionChange(int commitId, TableEventRegistry change, IIndexSet indexSet)
Definition: TableSource.cs:696
DbObjectType
The kind of objects that can be handled by a database system and its managers
Definition: DbObjectType.cs:27
long Id
Returns the unique identifier that represents this area.
Definition: IArea.cs:31
void CommitIndexSet(IIndexSet indexSet)
Defines the metadata properties of a table existing within a database.
Definition: TableInfo.cs:41
MinimalTable(TableSource source, IIndex masterIndex)
void RemoveRowFromCache(int rowIndex)
Definition: TableSource.cs:896
IEnumerable< TableEventRegistry > FindSinceCommit(long commitId)
string Name
Gets the database name, as configured in the parent context.
Definition: Database.cs:77
TableRowEventType
The kind of events that can happen on a table row during the life-time of a transaction.
A store is a resource where areas can be allocated and freed to store information (a memory allocator...
Definition: IStore.cs:56
An interface that defines contracts to alter the contents of a table.
bool HardCheckAndReclaimRow(int recordIndex)
Definition: TableSource.cs:833
IArea GetRecord(long recordNumber)
static void Serialize(TableInfo tableInfo, Stream stream)
Definition: TableInfo.cs:387
ISystemContext SystemContext
Gets the context of the database system that handles this database.