DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
TransactionTable.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 
23 using Deveel.Data.Index;
24 using Deveel.Data.Sql;
25 using Deveel.Data.Sql.Tables;
26 
27 namespace Deveel.Data.Transactions {
29  private int rowListRebuild;
30  private IIndex rowIndex;
31 
32  private int[] indexRebuilds;
35 
36  private int lastEntryRICheck;
37 
38  private bool disposed;
39 
40  public TransactionTable(ITransaction transaction, TableSource tableSource, TableEventRegistry eventRegistry) {
41  Transaction = transaction;
42  TableSource = tableSource;
43  EventRegistry = eventRegistry;
44  Context = tableSource.DatabaseContext;
45  indexSet = transaction.GetTableManager().GetIndexSetForTable(tableSource);
46  rowListRebuild = 0;
47 
48  var colCount = ColumnCount;
49  indexRebuilds = new int[colCount];
50  columnIndexes = new ColumnIndex[colCount];
51  lastEntryRICheck = eventRegistry.EventCount;
52  }
53 
55  Dispose(false);
56  }
57 
58  public int ColumnCount {
59  get {
60  AssertNotDisposed();
61  return TableInfo.ColumnCount;
62  }
63  }
64 
65  public IContext Context { get; private set; }
66 
68  get {
69  AssertNotDisposed();
70  return TableSource.TableInfo;
71  }
72  }
73 
74  public int RowCount {
75  get {
76  AssertNotDisposed();
77 
78  // Ensure the row list is up to date.
79  EnsureRowIndexListCurrent();
80  return RowIndex.Count;
81  }
82  }
83 
84  private void AssertNotDisposed() {
85  if (disposed)
86  throw new ObjectDisposedException("Table", "The table was disposed.");
87  }
88 
89  private void EnsureRowIndexListCurrent() {
90  int rebuildIndex = rowListRebuild;
91  int journalCount = EventRegistry.EventCount;
92  while (rebuildIndex < journalCount) {
93  var command = EventRegistry.GetEvent(rebuildIndex);
94  if (command is TableRowEvent) {
95  var rowEvent = (TableRowEvent) command;
96  var index = rowEvent.RowNumber;
97 
98  if (rowEvent.EventType == TableRowEventType.Add ||
99  rowEvent.EventType == TableRowEventType.UpdateAdd) {
100  // Add to 'row_list'.
101  if (!RowIndex.UniqueInsertSort(index))
102  throw new InvalidOperationException(String.Format("Row index already used in this table ({0})", index));
103  } else if (rowEvent.EventType == TableRowEventType.Remove ||
104  rowEvent.EventType == TableRowEventType.UpdateRemove) {
105  // Remove from 'row_list'
106  if (!RowIndex.RemoveSort(index))
107  throw new InvalidOperationException("Row index removed that wasn't in this table!");
108  } else {
109  throw new InvalidOperationException(String.Format("Table row event type {0} is invalid.", rowEvent.EventType));
110  }
111  }
112 
113  ++rebuildIndex;
114  }
115 
116  // It's now current (row_list_rebuild == journal_count);
117  rowListRebuild = rebuildIndex;
118  }
119 
120  private void EnsureColumnIndexCurrent(int column) {
121  var index = columnIndexes[column];
122 
123  // NOTE: We should be assured that no Write operations can occur over
124  // this section of code because writes are exclusive operations
125  // within a transaction.
126  // Are there journal entries pending on this scheme since?
127  int rebuildIndex = indexRebuilds[column];
128  int journalCount = EventRegistry.EventCount;
129  while (rebuildIndex < journalCount) {
130  var tableEvent = EventRegistry.GetEvent(rebuildIndex);
131  if (tableEvent is TableRowEvent) {
132  var rowEvent = (TableRowEvent) tableEvent;
133  if (rowEvent.EventType == TableRowEventType.Add ||
134  rowEvent.EventType == TableRowEventType.UpdateAdd) {
135  index.Insert(rowEvent.RowNumber);
136  } else if (rowEvent.EventType == TableRowEventType.Remove ||
137  rowEvent.EventType == TableRowEventType.UpdateRemove) {
138  index.Remove(rowEvent.RowNumber);
139  } else {
140  throw new InvalidOperationException(String.Format("Table row event type {0} is invalid.", rowEvent.EventType));
141  }
142  }
143 
144  ++rebuildIndex;
145  }
146 
147  indexRebuilds[column] = rebuildIndex;
148  }
149 
150  public DataObject GetValue(long rowNumber, int columnOffset) {
151  AssertNotDisposed();
152 
153  return TableSource.GetValue((int) rowNumber, columnOffset);
154  }
155 
156  public ColumnIndex GetIndex(int columnOffset) {
157  AssertNotDisposed();
158 
159  if (columnOffset < 0 || columnOffset >= ColumnCount)
160  throw new ArgumentOutOfRangeException("columnOffset");
161 
162  var index = columnIndexes[columnOffset];
163 
164  // Cache the scheme in this object.
165  if (index == null) {
166  index = TableSource.CreateColumnIndex(indexSet, this, columnOffset);
167  columnIndexes[columnOffset] = index;
168  }
169 
170  // Update the underlying scheme to the most current version.
171  EnsureColumnIndexCurrent(columnOffset);
172 
173  return index;
174  }
175 
176  private IIndex RowIndex {
177  get {
178  if (rowIndex == null)
179  rowIndex = indexSet.GetIndex(0);
180 
181  return rowIndex;
182  }
183  }
184 
185  public ITransaction Transaction { get; private set; }
186 
187  public TableSource TableSource { get; private set; }
188 
189  public TableEventRegistry EventRegistry { get; private set; }
190 
191  private int TableId {
192  get { return TableSource.TableId; }
193  }
194 
195  public ObjectName FullName {
196  get { return TableInfo.TableName; }
197  }
198 
199  public DbObjectType ObjectType {
200  get { return DbObjectType.Table; }
201  }
202 
203  public IEnumerator<Row> GetEnumerator() {
204  // Ensure the row list is up to date.
205  EnsureRowIndexListCurrent();
206  var enumerator = RowIndex.GetEnumerator();
207  return new RowEnumerator(this, enumerator);
208  }
209 
210  IEnumerator IEnumerable.GetEnumerator() {
211  return GetEnumerator();
212  }
213 
214  private void Dispose(bool disposing) {
215  if (!disposed) {
216  if (disposing) {
217  // Dispose and invalidate the indexes
218  // This is really a safety measure to ensure the index can't be
219  // used outside the scope of the lifetime of this object.
220  if (columnIndexes != null) {
221  foreach (var columnIndex in columnIndexes) {
222  if (columnIndex != null)
223  columnIndex.Dispose();
224  }
225  }
226  }
227 
228  columnIndexes = null;
229  rowIndex = null;
230  EventRegistry = null;
231  indexRebuilds = null;
232  indexSet = null;
233  Transaction = null;
234  disposed = true;
235  }
236  }
237 
238  public void Dispose() {
239  Dispose(true);
240  GC.SuppressFinalize(this);
241  }
242 
243  public RowId AddRow(Row row) {
244  AssertNotDisposed();
245 
246  if (Transaction.ReadOnly())
247  throw new Exception("Transaction is Read only.");
248 
250  throw new InvalidOperationException("Can not add row - table is read-only.");
251 
252  int rowNum;
253  try {
254  rowNum = TableSource.AddRow(row);
255  } catch (Exception ex) {
256  throw new InvalidOperationException(
257  String.Format("Unknown error when adding a row to the table '{0}'.", TableInfo.TableName), ex);
258  }
259 
260  row.SetRowNumber(rowNum);
261 
262  // Note this doesn't need to be synchronized because we are exclusive on
263  // this table.
264 
265  EventRegistry.Register(new TableRowEvent(TableId, rowNum, TableRowEventType.Add));
266 
267  return new RowId(TableId, rowNum);
268  }
269 
270  public void UpdateRow(Row row) {
271  AssertNotDisposed();
272 
273  if (Transaction.ReadOnly())
274  throw new Exception("Transaction is Read only.");
275 
276  // Check this isn't a Read only source
278  throw new InvalidOperationException("Can not update row - table is read-only.");
279 
280  if (row.RowId.IsNull)
281  throw new ArgumentException("The ROWID cannot be null in an update.");
282 
283  if (row.RowId.TableId != TableId)
284  throw new ArgumentException("The row was not created from this table.");
285 
286  var rowNum = row.RowId.RowNumber;
287 
288  if (rowNum < 0)
289  throw new ArgumentException("The number part of the ROWID is invalid.");
290 
291  // Note this doesn't need to be synchronized because we are exclusive on
292  // this table.
293  EventRegistry.Register(new TableRowEvent(TableId, rowNum, TableRowEventType.UpdateRemove));
294 
295  int newRowIndex;
296 
297  try {
298  newRowIndex = TableSource.AddRow(row);
299  } catch (IOException e) {
300  throw new InvalidOperationException("IO Error: " + e.Message, e);
301  }
302 
303  row.SetRowNumber(newRowIndex);
304 
305  // Note this doesn't need to be synchronized because we are exclusive on
306  // this table.
307  EventRegistry.Register(new TableRowEvent(TableId, newRowIndex, TableRowEventType.UpdateAdd));
308  }
309 
310  public bool RemoveRow(RowId rowId) {
311  AssertNotDisposed();
312 
313  if (rowId.IsNull)
314  throw new ArgumentNullException("rowId");
315 
316  if (rowId.TableId != TableId)
317  throw new ArgumentException("The table part of the ROWID is not this table.");
318  if (rowId.RowNumber < 0)
319  throw new ArgumentException("The number part of the ROWID is not valid for remove.");
320 
321  if (Transaction.ReadOnly())
322  throw new Exception("Transaction is Read only.");
323 
324  // Check this isn't a Read only source
326  throw new InvalidOperationException("Can not remove row - table is Read only.");
327 
328  // NOTE: This must <b>NOT</b> call 'RemoveRow' in TableSource.
329  // We do not want to delete a row permanently from the underlying
330  // file because the transaction using this data source may yet decide
331  // to roll back the change and not delete the row.
332 
333  // Note this doesn't need to be synchronized because we are exclusive on
334  // this table.
335  EventRegistry.Register(new TableRowEvent(rowId.TableId, rowId.RowNumber, TableRowEventType.Remove));
336 
337  return true;
338  }
339 
340  public void FlushIndexes() {
341  AssertNotDisposed();
342 
343  EnsureRowIndexListCurrent();
344 
345  // This will flush all of the column indexes
346  for (int i = 0; i < columnIndexes.Length; ++i) {
347  GetIndex(i);
348  }
349  }
350 
351  private void ExecuteUpdateReferentialAction(ConstraintInfo constraint, DataObject[] originalKey, DataObject[] newKey,
352  IQuery context) {
353  var updateRule = constraint.OnUpdate;
354  if (updateRule == ForeignKeyAction.NoAction &&
355  constraint.Deferred != ConstraintDeferrability.InitiallyImmediate) {
356  // Constraint check is deferred
357  return;
358  }
359 
360  // So either update rule is not NO ACTION, or if it is we are initially
361  // immediate.
362  var keyTable = Transaction.GetMutableTable(constraint.TableName);
363  var tableInfo = keyTable.TableInfo;
364  var keyCols = tableInfo.IndexOfColumns(constraint.ColumnNames).ToArray();
365  var keyEntries = keyTable.FindKeys(keyCols, originalKey);
366 
367  // Are there keys effected?
368  if (keyEntries.Any()) {
369  if (updateRule == ForeignKeyAction.NoAction)
370 
371  // Throw an exception;
374  constraint.Deferred.AsDebugString() +
375  " foreign key constraint violation on update (" +
376  constraint.ConstraintName + ") Columns = " +
377  constraint.TableName + "( " +
378  String.Join(", ", constraint.ColumnNames) +
379  " ) -> " + constraint.ForeignTable.FullName + "( " +
380  String.Join(", ", constraint.ForeignColumnNames) +
381  " )");
382 
383  // Perform a referential action on each updated key
384  foreach (int rowNum in keyEntries) {
385  var dataRow = new Row(keyTable, new RowId(keyTable.TableInfo.Id, rowNum));
386  dataRow.SetFromTable();
387 
388  if (updateRule == ForeignKeyAction.Cascade) {
389  // Update the keys
390  for (int n = 0; n < keyCols.Length; ++n) {
391  dataRow.SetValue(keyCols[n], newKey[n]);
392  }
393  keyTable.UpdateRow(dataRow);
394  } else if (updateRule == ForeignKeyAction.SetNull) {
395  for (int n = 0; n < keyCols.Length; ++n) {
396  dataRow.SetNull(keyCols[n]);
397  }
398  keyTable.UpdateRow(dataRow);
399  } else if (updateRule == ForeignKeyAction.SetDefault) {
400  for (int n = 0; n < keyCols.Length; ++n) {
401  dataRow.SetDefault(keyCols[n], context);
402  }
403  keyTable.UpdateRow(dataRow);
404  } else {
405  throw new Exception("Do not understand referential action: " + updateRule);
406  }
407  }
408 
409  // Check referential integrity of modified table,
410  keyTable.AssertConstraints();
411  }
412  }
413 
414  public void AssertConstraints() {
415  AssertNotDisposed();
416 
417  try {
418  // Early exit condition
419  if (lastEntryRICheck == EventRegistry.EventCount)
420  return;
421 
422  // This table name
423  var tableInfo = TableInfo;
424  var tName = tableInfo.TableName;
425 
426  using (var session = new SystemSession(Transaction, tName.ParentName)) {
427  using (var context = session.CreateQuery()) {
428 
429  // Are there any added, deleted or updated entries in the journal since
430  // we last checked?
431  List<int> rowsUpdated = new List<int>();
432  List<int> rowsDeleted = new List<int>();
433  List<int> rowsAdded = new List<int>();
434 
435  var events = EventRegistry.Skip(lastEntryRICheck);
436  foreach (var tableEvent in events.OfType<TableRowEvent>()) {
437  var rowNum = tableEvent.RowNumber;
438  if (tableEvent.EventType == TableRowEventType.Remove ||
439  tableEvent.EventType == TableRowEventType.UpdateRemove) {
440  rowsDeleted.Add(rowNum);
441 
442  var index = rowsAdded.IndexOf(rowNum);
443  if (index != -1)
444  rowsAdded.RemoveAt(index);
445  } else if (tableEvent.EventType == TableRowEventType.Add ||
446  tableEvent.EventType == TableRowEventType.UpdateAdd) {
447  rowsAdded.Add(rowNum);
448  }
449 
450  if (tableEvent.EventType == TableRowEventType.UpdateAdd ||
451  tableEvent.EventType == TableRowEventType.UpdateRemove)
452  rowsUpdated.Add(rowNum);
453  }
454 
455  // Were there any updates or deletes?
456  if (rowsDeleted.Count > 0) {
457  // Get all references on this table
458  var foreignConstraints = Transaction.QueryTableImportedForeignKeys(tName);
459 
460  // For each foreign constraint
461  foreach (var constraint in foreignConstraints) {
462  // For each deleted/updated record in the table,
463  foreach (var rowNum in rowsDeleted) {
464  // What was the key before it was updated/deleted
465  var cols = tableInfo.IndexOfColumns(constraint.ForeignColumnNames).ToArray();
466 
467  var originalKey = new DataObject[cols.Length];
468  int nullCount = 0;
469  for (int p = 0; p < cols.Length; ++p) {
470  originalKey[p] = GetValue(rowNum, cols[p]);
471  if (originalKey[p].IsNull) {
472  ++nullCount;
473  }
474  }
475 
476  // Check the original key isn't null
477  if (nullCount != cols.Length) {
478  // Is is an update?
479  int updateIndex = rowsUpdated.IndexOf(rowNum);
480  if (updateIndex != -1) {
481  // Yes, this is an update
482  int rowIndexAdd = rowsUpdated[updateIndex + 1];
483 
484  // It must be an update, so first see if the change caused any
485  // of the keys to change.
486  bool keyChanged = false;
487  var keyUpdatedTo = new DataObject[cols.Length];
488  for (int p = 0; p < cols.Length; ++p) {
489  keyUpdatedTo[p] = GetValue(rowIndexAdd, cols[p]);
490  if (originalKey[p].CompareTo(keyUpdatedTo[p]) != 0) {
491  keyChanged = true;
492  }
493  }
494  if (keyChanged) {
495  // Allow the delete, and execute the action,
496  // What did the key update to?
497  ExecuteUpdateReferentialAction(constraint, originalKey, keyUpdatedTo, context);
498  }
499 
500  // If the key didn't change, we don't need to do anything.
501  } else {
502  // No, so it must be a delete,
503  // This will look at the referencee table and if it contains
504  // the key, work out what to do with it.
505  ExecuteDeleteReferentialAction(constraint, originalKey, context);
506  }
507 
508  } // If the key isn't null
509 
510  } // for each deleted rows
511 
512  } // for each foreign key reference to this table
513 
514  }
515 
516  // Were there any rows added (that weren't deleted)?
517  if (rowsAdded.Count > 0) {
518  int[] rowIndices = rowsAdded.ToArray();
519 
520  // Check for any field constraint violations in the added rows
521  Transaction.CheckFieldConstraintViolations(this, rowIndices);
522 
523  // Check this table, adding the given row_index, immediate
524  Transaction.CheckAddConstraintViolations(this, rowIndices, ConstraintDeferrability.InitiallyImmediate);
525  }
526  }
527  }
528  } catch (ConstraintViolationException) {
529  // If a constraint violation, roll back the changes since the last
530  // check.
531  int rollbackPoint = EventRegistry.EventCount - lastEntryRICheck;
532  if (rowListRebuild <= rollbackPoint) {
533  EventRegistry.Rollback(rollbackPoint);
534  } else {
535  // TODO: emit a warning
536  }
537 
538  throw;
539  } finally {
540  // Make sure we update the 'last_entry_ri_check' variable
541  lastEntryRICheck = EventRegistry.EventCount;
542  }
543  }
544 
545 
546  private void ExecuteDeleteReferentialAction(ConstraintInfo constraint, DataObject[] originalKey, IQuery context) {
547  var deleteRule = constraint.OnDelete;
548  if (deleteRule == ForeignKeyAction.NoAction &&
549  constraint.Deferred != ConstraintDeferrability.InitiallyImmediate) {
550  // Constraint check is deferred
551  return;
552  }
553 
554  // So either delete rule is not NO ACTION, or if it is we are initially
555  // immediate.
556  var keyTable = Transaction.GetMutableTable(constraint.TableName);
557  var tableInfo = keyTable.TableInfo;
558  var keyCols = tableInfo.IndexOfColumns(constraint.ColumnNames).ToArray();
559  var keyEntries = keyTable.FindKeys(keyCols, originalKey).ToList();
560 
561  // Are there keys effected?
562  if (keyEntries.Count > 0) {
563  if (deleteRule == ForeignKeyAction.NoAction) {
564  // Throw an exception;
567  constraint.Deferred.AsDebugString() +
568  " foreign key constraint violation on delete (" +
569  constraint.ConstraintName + ") Columns = " +
570  constraint.TableName + "( " +
571  String.Join(", ", constraint.ColumnNames) +
572  " ) -> " + constraint.ForeignTable.FullName + "( " +
573  String.Join(", ", constraint.ForeignColumnNames) +
574  " )");
575  }
576 
577  // Perform a referential action on each updated key
578  foreach (int rowNum in keyEntries) {
579  var dataRow = new Row(keyTable, new RowId(tableInfo.Id, rowNum));
580  dataRow.SetFromTable();
581 
582  if (deleteRule == ForeignKeyAction.Cascade) {
583  // Cascade the removal of the referenced rows
584  keyTable.RemoveRow(dataRow.RowId);
585  } else if (deleteRule == ForeignKeyAction.SetNull) {
586  for (int n = 0; n < keyCols.Length; ++n) {
587  dataRow.SetNull(keyCols[n]);
588  }
589  keyTable.UpdateRow(dataRow);
590  } else if (deleteRule == ForeignKeyAction.SetDefault) {
591  for (int n = 0; n < keyCols.Length; ++n) {
592  dataRow.SetDefault(keyCols[n], context);
593  }
594  keyTable.UpdateRow(dataRow);
595  } else {
596  throw new Exception("Do not understand referential action: " + deleteRule);
597  }
598  }
599 
600  // Check referential integrity of modified table,
601  keyTable.AssertConstraints();
602  }
603 
604  }
605 
606  public void AddLock() {
607  AssertNotDisposed();
608 
610  }
611 
612  public void RemoveLock() {
613  AssertNotDisposed();
614 
616  }
617 
618  #region RowEnumerator
619 
620  class RowEnumerator : IEnumerator<Row> {
621  private readonly TransactionTable table;
623 
625  this.table = table;
626  this.enumerator = enumerator;
627  }
628 
629  public void Dispose() {
630  enumerator.Dispose();
631  }
632 
633  public bool MoveNext() {
634  return enumerator.MoveNext();
635  }
636 
637  public void Reset() {
638  enumerator.Reset();
639  }
640 
641  public Row Current {
642  get { return new Row(table, enumerator.Current); }
643  }
644 
645  object IEnumerator.Current {
646  get { return Current; }
647  }
648  }
649 
650  #endregion
651 
652  object ILockable.RefId {
653  get { return TableId; }
654  }
655 
656  void ILockable.Released(Lock @lock) {
657  // TODO: decrement a count ref to disposal?
658  }
659 
660  void ILockable.Acquired(Lock @lock) {
661  // TODO: increment a count ref to prevent disposal?
662  }
663  }
664 }
bool IsNull
Gets a boolean value indicating if the object equivales to a NULL.
Definition: RowId.cs:64
Enumerates a known set of codes in a SQL Model
A database exception that represents a constraint violation.
The system implementation of a transaction model that handles isolated operations within a database c...
Definition: Transaction.cs:35
abstract void Insert(int rowNumber)
ConstraintDeferrability Deferred
Describes the name of an object within a database.
Definition: ObjectName.cs:44
ConstraintDeferrability
The type of deferrance of a constraint.
A single row in a table of a database.
Definition: Row.cs:44
void UpdateRow(Row row)
Updates the values of a row into the table.
int RowNumber
Gets the number of the column within the table referenced.
Definition: RowId.cs:58
TransactionTable(ITransaction transaction, TableSource tableSource, TableEventRegistry eventRegistry)
ObjectName TableName
Gets the fully qualified name of the table that is ensured to be unique within the system...
Definition: TableInfo.cs:97
void Rollback()
Rollback any write operations done during the lifetime of this transaction and invalidates it...
Definition: Transaction.cs:239
ForeignKeyAction
Enumerates the foreign key referential trigger actions.
void ExecuteUpdateReferentialAction(ConstraintInfo constraint, DataObject[] originalKey, DataObject[] newKey, IQuery context)
const int ForeignKeyViolation
A Foreign Key constraint violation error code.
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...
void FlushIndexes()
Flushes all changes made on this table to the backing index scheme.
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
string FullName
Gets the full reference name formatted.
Definition: ObjectName.cs:114
An object that access to a set of indexes.
Definition: IIndexSet.cs:27
An interface for querying and accessing an index of primitive integers.
Definition: IIndex.cs:37
void ExecuteDeleteReferentialAction(ConstraintInfo constraint, DataObject[] originalKey, IQuery context)
RowId AddRow(Row row)
Persists a new row to the table.
Defines the value of a ROWID object, that is a unique reference within a database system to a single ...
Definition: RowId.cs:24
DataObject GetValue(long rowNumber, int columnOffset)
Gets a single cell within the table that is located at the given column offset and row...
void AssertConstraints()
Performs all constraint integrity checks and actions to any modifications based on any changes that h...
int ColumnCount
Gets a count of the columns defined by this object.
Definition: TableInfo.cs:159
void SetRowNumber(int rowNumber)
Sets the number component of the ID of this column.
Definition: Row.cs:484
The simplest implementation of a transaction.
Definition: ITransaction.cs:30
DbObjectType
The kind of objects that can be handled by a database system and its managers
Definition: DbObjectType.cs:27
ColumnIndex GetIndex(int columnOffset)
Gets an index for given column that can be used to select values from this table. ...
bool RemoveRow(RowId rowId)
Deletes row identified by the given coordinates from the table.
Defines the metadata properties of a table existing within a database.
Definition: TableInfo.cs:41
int TableId
Gets the unique identifier of the table the row is contained.
Definition: RowId.cs:53
TableRowEventType
The kind of events that can happen on a table row during the life-time of a transaction.
An interface that defines contracts to alter the contents of a table.
RowEnumerator(TransactionTable table, IIndexEnumerator< int > enumerator)
RowId RowId
Gets the row unique identifier within the database.
Definition: Row.cs:101