19 using System.Collections.Generic;
33 namespace Deveel.Data.Sql.Tables {
50 if (composite == null)
51 throw new ArgumentNullException(
"composite");
53 Composite = composite;
54 StoreSystem = storeSystem;
57 SourceName = sourceName;
83 public int TableId {
get;
private set; }
89 public string SourceName {
get;
private set; }
91 public bool IsRootLocked {
101 public int ColumnCount {
105 public int RawRowCount {
115 public long CurrentUniqueId {
118 return sequenceId - 1;
123 public bool CanCompact {
131 public bool IsReadOnly {
137 public bool IsClosed {
150 public bool HasChangesPending {
158 public string StoreIdentity {
get;
private set; }
160 public IStore Store {
get;
private set; }
166 public bool HasShutdown {
get;
private set; }
170 public bool CellCaching {
171 get {
return CellCache != null; }
175 return StoreSystem.StoreExists(StoreIdentity);
184 public void Close(
bool dropPending) {
215 }
catch (Exception) {
220 indexSetStore.
Close();
223 StoreSystem.CloseStore(Store);
234 for (
long rowNumber = 0; rowNumber < elements; ++rowNumber) {
240 long recordPointer = a.ReadInt8();
241 ReleaseRowObjects(recordPointer);
248 throw new NotImplementedException();
252 bool needsCheck = OpenTable();
271 if (IsRootLocked || HasChangesPending)
273 throw new Exception(
"Odd, we are root locked or have pending journal changes.");
278 var indexSet = CreateIndexSet();
279 var masterIndex = indexSet.GetIndex(0);
284 int rowCount = RawRowCount;
285 for (
int rowNumber = 0; rowNumber < rowCount; ++rowNumber) {
287 if (!IsRecordDeleted(rowNumber)) {
289 var type = ReadRecordState(rowNumber);
296 if (!masterIndex.Contains(rowNumber)) {
298 DoHardRowRemove(rowNumber);
301 WriteRecordState(rowNumber,
RecordState.CommittedAdded);
306 if (!masterIndex.Contains(rowNumber)) {
311 WriteRecordState(rowNumber,
RecordState.CommittedRemoved);
318 if (masterIndex.Contains(rowNumber)) {
323 WriteRecordState(rowNumber,
RecordState.CommittedAdded);
340 var usedAreas =
new List<long>();
342 usedAreas.Add(headerArea.
Id);
346 usedAreas.Add(headerArea.ReadInt8());
347 usedAreas.Add(headerArea.ReadInt8());
357 for (
long i = 0; i < elements; ++i) {
361 usedAreas.Add(area.ReadInt8());
367 var aStore = (StoreBase)Store;
368 var leakedAreas = aStore.FindAllocatedAreasNotIn(usedAreas).ToList();
369 if (leakedAreas.Count == 0) {
371 foreach (
long areaPointer
in leakedAreas) {
372 Store.DeleteArea(areaPointer);
381 Store = StoreSystem.OpenStore(StoreIdentity);
382 bool needCheck = !Store.ClosedClean;
413 if ((TableId & 0x0F0000000) != 0)
414 throw new InvalidOperationException(
"'table_id' exceeds maximum possible keys.");
432 if (colInfo.IsIndexable) {
433 var indexName =
String.Format(
"IDX_{0}", colInfo.ColumnName);
434 var indexType = colInfo.IndexType;
435 if (
String.IsNullOrEmpty(indexType))
454 SetTableInfo(tableInfo);
466 string str = tableName.Replace(
'.',
'_').ToLower();
471 StringBuilder osifiedName =
new StringBuilder();
473 for (
int i = 0; i < str.Length || count > 64; ++i) {
475 if ((c >=
'a' && c <=
'z') ||
476 (c >=
'A' && c <=
'Z') ||
477 (c >=
'0' && c <=
'9') ||
479 osifiedName.Append(c);
484 return String.Format(
"{0}_{1}", tableId, osifiedName);
492 Store = StoreSystem.CreateStore(StoreIdentity);
510 using (var stream =
new MemoryStream()) {
511 var writer =
new BinaryWriter(stream, Encoding.Unicode);
515 tableInfoBuf = stream.ToArray();
518 byte[] indexSetInfoBuf;
519 using (var stream =
new MemoryStream()) {
520 var writer =
new BinaryWriter(stream, Encoding.Unicode);
525 indexSetInfoBuf = stream.ToArray();
532 var headerWriter = Store.CreateArea(80);
533 long headerPointer = headerWriter.Id;
535 var dataTableDefWriter = Store.CreateArea(tableInfoBuf.Length);
536 long tableInfoOffset = dataTableDefWriter.Id;
538 var indexSetWriter = Store.CreateArea(indexSetInfoBuf.Length);
539 long indexSetInfoPointer = indexSetWriter.Id;
542 listHeaderOffset = recordList.
Create();
544 firstDeleteChainRecord = -1;
548 indexHeaderOffset = indexSetStore.
Create();
551 headerWriter.WriteInt4(1);
552 headerWriter.WriteInt4(TableId);
553 headerWriter.WriteInt8(sequenceId);
554 headerWriter.WriteInt8(tableInfoOffset);
555 headerWriter.WriteInt8(indexSetInfoPointer);
556 headerWriter.WriteInt8(indexHeaderOffset);
557 headerWriter.WriteInt8(listHeaderOffset);
558 headerWriter.Flush();
561 dataTableDefWriter.Write(tableInfoBuf, 0, tableInfoBuf.Length);
562 dataTableDefWriter.Flush();
565 indexSetWriter.Write(indexSetInfoBuf, 0, indexSetInfoBuf.Length);
566 indexSetWriter.Flush();
569 var fixedArea = Store.GetArea(-1);
570 fixedArea.WriteInt8(headerPointer);
574 headerArea = Store.GetArea(headerPointer);
582 var fixedArea = Store.GetArea(-1);
585 headerArea = Store.GetArea(fixedArea.ReadInt8());
588 var version = headerArea.ReadInt4();
590 throw new IOException(
"Incorrect version identifier.");
592 TableId = headerArea.ReadInt4();
593 sequenceId = headerArea.ReadInt8();
594 long infoPointer = headerArea.ReadInt8();
595 long indexInfoPointer = headerArea.ReadInt8();
596 indexHeaderOffset = headerArea.ReadInt8();
597 listHeaderOffset = headerArea.ReadInt8();
600 using (var stream = Store.GetAreaInputStream(infoPointer)) {
601 var reader =
new BinaryReader(stream, Encoding.Unicode);
602 version = reader.ReadInt32();
604 throw new IOException(
"Incorrect TableInfo version identifier.");
612 using (var stream = Store.GetAreaInputStream(indexInfoPointer)) {
613 var reader =
new BinaryReader(stream, Encoding.Unicode);
614 version = reader.ReadInt32();
616 throw new IOException(
"Incorrect IndexSetInfo version identifier.");
622 recordList.
Open(listHeaderOffset);
628 indexSetStore.
Open(indexHeaderOffset);
629 }
catch (IOException) {
634 indexHeaderOffset = indexSetStore.
Create();
637 headerArea.WriteInt8(indexHeaderOffset);
648 throw new Exception(
"IO operation while shutting down.");
654 headerArea.WriteInt8(sequenceId);
659 }
catch (IOException e) {
660 throw new InvalidOperationException(
"IO Error: " + e.Message);
671 throw new Exception(
"IO operation while shutting down.");
677 headerArea.WriteInt8(sequenceId);
682 }
catch (IOException e) {
683 throw new InvalidOperationException(
"IO Error: " + e.Message, e);
700 throw new InvalidOperationException(
"Can't commit transaction journal, table is Read only.");
710 CommitIndexSet(indexSet);
729 foreach (var entry
in change) {
731 var rowEvent = (TableRowEvent) entry;
732 var rowIndex = rowEvent.RowNumber;
736 var oldType = WriteRecordState(rowIndex,
RecordState.CommittedAdded);
741 WriteRecordState(rowIndex, oldType);
742 throw new InvalidOperationException(
String.Format(
"Record {0} of table {1} was not in an uncommitted state!",
743 rowIndex, TableName));
747 var oldType = WriteRecordState(rowIndex,
RecordState.CommittedRemoved);
751 WriteRecordState(rowIndex, oldType);
752 throw new InvalidOperationException(
String.Format(
"Record {0} of table {1} was not in an added state!", rowIndex,
757 GC.DeleteRow(rowIndex);
761 }
catch (IOException e) {
762 throw new InvalidOperationException(
"IO Error: " + e.Message, e);
771 throw new IOException(
"IO operation while shutting down.");
774 var blockArea = recordList.
GetRecord(rowNumber);
777 var oldStatus = (
RecordState) blockArea.ReadInt4();
783 blockArea.Position = pos;
784 blockArea.WriteInt4((
int)state);
797 var blockArea = recordList.
GetRecord(rowNumber);
804 var state = ReadRecordState(rowNumber);
811 OnDeleteRow(rowNumber);
822 throw new InvalidOperationException(
"Cannot remove row, table is locked");
824 var typeKey = ReadRecordState(rowIndex);
827 throw new InvalidOperationException(
String.Format(
"The row {0} is not marked as committed removed", rowIndex));
829 DoHardRowRemove(rowIndex);
837 throw new InvalidOperationException(
"Assertion failed: Can't remove row, table is under a root Lock.");
840 if (IsRecordDeleted(recordIndex))
843 var typeKey = ReadRecordState(recordIndex);
849 DoHardRowRemove(recordIndex);
857 throw new IOException(
"IO operation while VM shutting down.");
860 var blockArea = recordList.
GetRecord(rowIndex);
866 throw new IOException(
"Record is already marked as deleted.");
868 long recordPointer = blockArea.ReadInt8();
874 blockArea.Position = p;
876 blockArea.WriteInt8(firstDeleteChainRecord);
878 firstDeleteChainRecord = rowIndex;
885 ReleaseRowObjects(recordPointer);
888 Store.DeleteArea(recordPointer);
890 RemoveRowFromCache(rowIndex);
899 for (
int i = 0; i < colCount; i++) {
900 CellCache.Remove(
DatabaseContext.DatabaseName(), TableId, rowIndex, i);
907 IArea recordArea = Store.GetArea(recordPointer);
908 recordArea.ReadInt4();
911 for (
int i = 0; i < ColumnCount; ++i) {
912 int ctype = recordArea.ReadInt4();
913 int cellOffset = recordArea.ReadInt4();
917 }
else if (ctype == 2) {
919 recordArea.
Position = cellOffset + 4 + (ColumnCount * 8);
921 int btype = recordArea.ReadInt4();
922 recordArea.ReadInt4();
925 long blobRefId = recordArea.ReadInt8();
934 throw new Exception(
"Unrecognised type.");
943 rowNumber = OnAddRow(row);
958 throw new IOException(
"IO operation while shutting down.");
961 if (firstDeleteChainRecord == -1)
966 long recycledRecord = firstDeleteChainRecord;
967 var block = recordList.
GetRecord(recycledRecord);
973 throw new InvalidOperationException(
String.Format(
"Record {0} is not deleted. ({1})", recPos, status));
976 long nextChain = block.ReadInt8();
977 firstDeleteChainRecord = nextChain;
986 block.Position = recPos;
988 block.WriteInt8(recordPointer);
994 return recycledRecord;
1006 int newBlockNumber = recordList.
BlockCount - 1;
1011 var a = recordList.
GetRecord(startIndex);
1014 for (
long n = 0; n < sizeOfBlock - 1; ++n) {
1016 a.WriteInt8(startIndex + n + 1);
1021 a.WriteInt8(firstDeleteChainRecord);
1025 firstDeleteChainRecord = startIndex;
1040 long recordPointer = WriteRecord(row);
1043 rowNumber = AddToRecordList(recordPointer);
1044 intRowNumber = (int)rowNumber;
1050 for (
int i = 0; i < rowCells; ++i) {
1059 return (
int)rowNumber;
1066 int[] cellSizes =
new int[rowCells];
1067 int[] cellTypes =
new int[rowCells];
1073 int allRecordsSize = 0;
1074 for (
int i = 0; i < rowCells; ++i) {
1080 var largeObjectRef = (IObjectRef)cell.
Value;
1084 if (largeObjectRef != null) {
1087 ObjectStore.EstablishObject(largeObjectRef.ObjectId.Id);
1090 cellSize = cell.Size;
1094 cellSizes[i] = cellSize;
1095 cellTypes[i] = cellType;
1096 allRecordsSize += cellSize;
1100 var area = Store.CreateArea(allRecordsSize + (rowCells * 8) + 4);
1101 long recordPointer = area.
Id;
1104 using (var areaStream =
new AreaStream(area)) {
1105 var writer =
new BinaryWriter(areaStream);
1110 for (
int i = 0; i < rowCells; ++i) {
1111 writer.Write(cellTypes[i]);
1112 writer.Write(cellSkip);
1113 cellSkip += cellSizes[i];
1117 for (
int i = 0; i < rowCells; ++i) {
1119 int cellType = cellTypes[i];
1120 if (cellType == 1) {
1123 }
else if (cellType == 2) {
1127 if (largeObjectRef == null) {
1135 writer.Write(largeObjectRef.ObjectId.Id);
1138 throw new IOException(
"Unrecognised cell type.");
1150 return recordPointer;
1158 var indexSet = CreateIndexSet();
1162 int rowCount = RawRowCount;
1165 IIndex masterIndex = indexSet.GetIndex(0);
1168 for (
int rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
1170 if (!IsRecordDeleted(rowIndex)) {
1172 if (!masterIndex.UniqueInsertSort(rowIndex))
1173 throw new Exception(
"Master index entry was duplicated.");
1178 CommitIndexSet(indexSet);
1181 int indexCount = indexSetInfo.IndexCount;
1182 for (
int i = 0; i < indexCount; ++i) {
1190 var indexSet = CreateIndexSet();
1193 var masterIndex = indexSet.GetIndex(0);
1199 var index = CreateColumnIndex(indexSet, minTable, indexNumber);
1202 int rowCount = RawRowCount;
1203 for (
int rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
1205 if (!IsRecordDeleted(rowIndex))
1206 index.Insert(rowIndex);
1210 CommitIndexSet(indexSet);
1217 if (!column.IsIndexable ||
1218 (
String.IsNullOrEmpty(column.IndexType) ||
1223 return CreateIndexAt(indexSet, table, indexI);
1233 if (indexInfo == null)
1238 if (cols.Length != 1)
1239 throw new Exception(
"Multi-column indexes not supported at this time.");
1242 var colIndex = tableInfo.IndexOfColumn(cols[0]);
1247 var indexList = indexSet.
GetIndex(indexInfo.Offset);
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);
1261 throw new NotImplementedException();
1277 throw new InvalidOperationException(
"Too many root locks removed!");
1313 if (CellCache.TryGetValue(
Database.
Name, TableId, rowIndex, columnOffset, out cell))
1321 long recordPointer = -1;
1334 var listBlock = recordList.
GetRecord(rowIndex);
1338 throw new InvalidOperationException(
String.Format(
"Record {0} was deleted: unable to read.", rowIndex));
1341 recordPointer = listBlock.ReadInt8();
1345 using (var stream = Store.GetAreaInputStream(recordPointer)) {
1346 var reader =
new BinaryReader(stream);
1348 stream.Seek(4 + (columnOffset * 8), SeekOrigin.Current);
1350 int cellType = reader.ReadInt32();
1351 int cellOffset = reader.ReadInt32();
1353 int curAt = 8 + 4 + (columnOffset * 8);
1354 int beAt = 4 + (ColumnCount * 8);
1355 int skipAmount = (beAt - curAt) + cellOffset;
1357 stream.Seek(skipAmount, SeekOrigin.Current);
1361 var type =
TableInfo[columnOffset].ColumnType;
1364 if (cellType == 1) {
1366 ob = type.DeserializeObject(stream);
1367 }
else if (cellType == 2) {
1369 int fType = reader.ReadInt32();
1370 int fReserved = reader.ReadInt32();
1371 long refId = reader.ReadInt64();
1376 ob = type.CreateFromLargeObject(objRef);
1377 }
else if (fType == 1) {
1380 throw new Exception(
"Unknown blob type.");
1383 throw new Exception(
"Unrecognised cell type input data.");
1396 }
catch (IOException e) {
1397 throw new Exception(
String.Format(
"Error getting cell at ({0}, {1}) pointer = " + recordPointer +
".", rowIndex,
1403 CellCache.Set(
Database.
Name, TableId, rowIndex, columnOffset, cell);
1409 #region MinimalTable
1416 this.source = source;
1417 this.masterIndex = masterIndex;
1431 var iterator = masterIndex.GetEnumerator();
1442 this.enumerator = enumerator;
1451 return enumerator.MoveNext();
1459 get {
return new Row(table, enumerator.Current); }
1462 object IEnumerator.Current {
1463 get {
return Current; }
1467 IEnumerator IEnumerable.GetEnumerator() {
1468 return GetEnumerator();
1476 get {
return source.DatabaseContext; }
1483 public int RowCount {
1487 return masterIndex.Count;
1492 return source.GetValue((
int)rowNumber, columnOffset);
1496 throw new NotImplementedException();
1512 throw new InvalidOperationException(
"Can't rollback transaction journal, table is Read only.");
1519 foreach (var tableEvent
in registry) {
1521 var rowEvent = (TableRowEvent) tableEvent;
1523 var oldState = WriteRecordState(rowEvent.RowNumber,
RecordState.CommittedRemoved);
1525 WriteRecordState(rowEvent.RowNumber, oldState);
1526 throw new InvalidOperationException(
String.Format(
"Record {0} was not in an uncommitted state.",
1527 rowEvent.RowNumber));
1530 GC.DeleteRow(rowEvent.RowNumber);
1534 }
catch (IOException e) {
1535 throw new InvalidOperationException(
"IO Error: " + e.Message, e);
1544 if (allMerged && !IsReadOnly) {
1550 #region TypeResolver
1556 this.database = database;
1560 using (var session = database.CreateSystemSession()) {
1561 using (var query = session.CreateQuery()) {
1562 return query.Context.ResolveType(resolveContext.
TypeName, resolveContext.
GetMeta());
void MergeChanges(long commitId)
static TableInfo Deserialize(Stream stream, ITypeResolver typeResolver)
static string MakeSourceIdentity(ISystemContext context, int tableId, string tableName)
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.
void AddRegistry(TableEventRegistry registry)
void Open(long listPointer)
A long string in the system.
void WriteDeleteHead(long value)
long BlockFirstPosition(int blockNumber)
TableInfo(ObjectName tableName)
Constructs the object with the given table name.
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...
IIndexSet CreateIndexSet()
Defines the contract for stores that handle lrge objects within a database system.
IEnumerable< TableEventRegistry > FindChangesSinceCmmit(long commitId)
RowEnumerator(MinimalTable table, IIndexEnumerator< int > enumerator)
IDatabaseContext DatabaseContext
void CopyFrom(int tableId, TableSource destSource, IIndexSet indexSet)
ILargeObject GetObject(ObjectId id)
Gets an object that was previously created for the given unique identifier.
DataObject GetValue(int columnOffset)
Gets or the value of a cell of the row at the given offset.
void PrepareIndexLists(int count, int type, int blockSize)
long Position
Returns or sets the current position of the pointer within the area.
long firstDeleteChainRecord
void HardRemoveRow(int rowIndex)
Represents a database object, such as a table, a trigger, a type or a column.
void Create(TableInfo tableInfo)
int FindIndexForColumns(string[] columnNames)
An object that creates and manages the IStore objects that the database engine uses to represent itse...
ObjectName FullName
Gets the fully qualified name of the object used to resolve it uniquely within the database...
void Close(bool dropPending)
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.
void SetUniqueId(long value)
Describes the name of an object within a database.
SqlType ResolveType(TypeResolveContext resolveContext)
void OnDeleteRow(int rowIndex)
IndexSetInfo(ObjectName tableName, IEnumerable< IndexInfo > indexes, bool readOnly)
Database(DatabaseSystem system, IDatabaseContext context)
ISqlObject Value
Gets the underlined value that is handled.
A single row in a table of a database.
void Open(long startPointer)
void GetAreasUsed(List< long > list)
void CommitIndexSet(IIndexSet indexSet)
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)
The representation of a single database in the system.
IndexInfo GetIndex(int offset)
IEnumerator< Row > GetEnumerator()
const string InsertSearch
RecordState WriteRecordState(int rowNumber, RecordState state)
RecordState
An enumeration that represents the various states of a record.
IMutableTable CreateTableAtCommit(ITransaction transaction)
void SetTableInfo(TableInfo info)
ObjectName TableName
Gets the fully qualified name of the table that is ensured to be unique within the system...
void RollbackTransactionChange(TableEventRegistry registry)
FixedRecordList recordList
IndexSetStore indexSetStore
void GetAreasUsed(IList< long > usedAreas)
Defines the contract for a valid SQL Object
TableSource(TableSourceComposite composite, IStoreSystem storeSystem, IObjectStore objStore, int tableId, string sourceName)
void AddIndex(IndexInfo indexInfo)
readonly IIndex masterIndex
IIndexSet GetSnapshotIndex()
ColumnIndex CreateColumnIndex(IIndexSet indexSet, ITable table, int columnOffset)
long WriteRecord(Row data)
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
void SerializeValueTo(Stream stream, ISystemContext systemContext)
IIndexEnumerator< int > enumerator
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
An interface for access the contents of an area of a store.
DbObjectType ObjectType
Gets the type of database object that the implementation is for
Defines the properties of a specific SQL Type and handles the values compatible.
void DoHardRowRemove(int rowNumber)
ColumnIndex CreateIndexAt(IIndexSet indexSet, ITable table, int indexI)
An object that access to a set of indexes.
void AddIndex(IndexInfo indexInfo)
An interface for querying and accessing an index of primitive integers.
DataTypeMeta GetMeta(string name)
void BuildIndex(int indexNumber)
long AddToRecordList(long recordPointer)
long BlockNodeCount(int blockNumber)
void ReleaseRowObjects(long recordPointer)
bool IsRecordDeleted(int rowNumber)
int ColumnCount
Gets a count of the columns defined by this object.
TypeResolver(IDatabase database)
void SerialiazeTo(Stream stream)
DatabaseContext(ISystemContext systemContext, IConfiguration configuration)
int ColumnCount
Gets the number of columns in the parent table of this row.
ISystemContext SystemContext
RecordState ReadRecordState(int rowNumber)
The simplest implementation of a transaction.
void CommitTransactionChange(int commitId, TableEventRegistry change, IIndexSet indexSet)
DbObjectType
The kind of objects that can be handled by a database system and its managers
bool MergeChanges(long commitId)
long Id
Returns the unique identifier that represents this area.
void CommitIndexSet(IIndexSet indexSet)
Defines the metadata properties of a table existing within a database.
MinimalTable(TableSource source, IIndex masterIndex)
void RemoveRowFromCache(int rowIndex)
IEnumerable< TableEventRegistry > FindSinceCommit(long commitId)
string Name
Gets the database name, as configured in the parent context.
readonly TableSource source
readonly IDatabase database
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...
An interface that defines contracts to alter the contents of a table.
bool HardCheckAndReclaimRow(int recordIndex)
IArea GetRecord(long recordNumber)
static void Serialize(TableInfo tableInfo, Stream stream)
ISystemContext SystemContext
Gets the context of the database system that handles this database.