/// <summary> /// Attempts to insert a row into a btree /// </summary> /// <param name="insert">The row to insert</param> /// <returns>True if successful, otherwise false</returns> public bool InsertRow(RowInsert insert) { if (insert == null) { throw new ArgumentNullException(nameof(insert)); } bool isSuccessful = false; BTreeContainer container; BTreeAddress address = insert.Table.BTreeAddress; if (_cache.ContainsKey(insert.Table.BTreeAddress)) { if (_cache.TryGetValue(address, out container)) { isSuccessful = container.TryInsertRow(insert); } } else { AddContainerToCache(address); if (_cache.TryGetValue(address, out container)) { isSuccessful = container.TryInsertRow(insert); } } return(isSuccessful); }
/// <summary> /// Pulls pages from disk into the tree and then attempts to insert the row into the first page it can fit into /// and then updates the page on disk /// </summary> /// <param name="row">The row to insert</param> private void InsertRowIntoFirstAvailablePage(RowInsert row) { int nextPageId; Page page; do { // to do: we need to keep track of the page ids that we haven't loaded // into memory nextPageId = GetNextAvailablePageId(); page = _storage.GetPage(nextPageId, _address); AddPageToTree(page); if (TreeIsFullyLoaded()) { break; } // to do: what if the pages on disk are filled up and we need to add a brand new page here? // in other words, we can never exit the loop? }while (!page.CanInsertRow(row.Size)); if (TreeIsFullyLoaded() && !page.CanInsertRow(row.Size)) { AddRowToNewPage(row); } else { page.AddRow(row, GetMaxRowId() + 1); UpdatePageOnDisk(page); } }
public XactLine(RowInsert row) { XactId = row.XactId; TableId = row.Table.TableId; IsReconciled = false; Action = XactLineAction.Insert; Data = row.Values; LineNumber = 0; Offset = 0; }
/// <summary> /// Creates a new page and then adds the row to it; then adds the page to the tree and to storage /// </summary> /// <param name="row">The row to add</param> private void AddRowToNewPage(RowInsert row) { var page = new Page(GetNextPageId(), _address.TableId, _address.DatabaseId, _schema, _process); if (page.AddRow(row, GetMaxRowId() + 1)) { AddPageToTree(page); AddPageToStorage(page); } }
/// <summary> /// Marks a insert xaction as reconciled in the file /// </summary> /// <param name="row">The row that was to be inserted</param> /// <returns>True if successful, otherwise false</returns> public bool MarkInsertXactAsReconciled(RowInsert row) { bool isSuccessful; _locker.EnterWriteLock(); // this sucks string[] lines = File.ReadAllLines(FileName()); var xacts = new List <XactLine>(lines.Length); foreach (var line in lines) { if (line.StartsWith("version")) { continue; } if (line.Length == 0) { continue; } var xact = new XactLine(line); if (xact.XactId == row.XactId) { xact.IsReconciled = true; } xacts.Add(xact); } string[] linesToWrite = new string[lines.Length]; int i = 0; foreach (var x in xacts) { linesToWrite[i] = x.ToString(); i++; } File.WriteAllLines(FileName(), linesToWrite); // remove the xaction from the pending open transactions Guid value; isSuccessful = _unreconciledXacts.TryRemove(row.XactId, out value); _locker.ExitWriteLock(); return(isSuccessful); }
/// <summary> /// Updates the db indexes if applicable for a row insert /// </summary> /// <param name="row">The row to b inserted</param> /// <returns>True if successful, otherwise false</returns> private bool UpdateIndexesForInsert(RowInsert row) { Table2 table = GetTable(row.Table.BTreeAddress); if (table.HasIndexes) { // need to update indexes if appropriate } //throw new NotImplementedException(); return(true); }
/// <summary> /// Tries to add the row data to the btree and also update the data file and db directory data file on disk /// </summary> /// <param name="row">The row to be added</param> /// <returns>True if the operation was successful, otherwise false</returns> public bool TryInsertRow(RowInsert row) { if (row.Table.TableId != _address.TableId || row.Table.DatabaseId != _address.DatabaseId) { throw new InvalidOperationException("attempted to add row to incorrect btree"); } bool result = false; if (GetContainerState() == BTreeContainerState.Ready) { SetContainerState(BTreeContainerState.LockedForInsert); lock (_treeLock) { int nextPageId = 0; if (TreeIsEmpty()) { nextPageId = GetNextAvailablePageId(); // brand new table, need to add the 1st page. if (nextPageId == 0) { AddRowToNewPage(row); result = true; } else { InsertRowIntoFirstAvailablePage(row); result = true; } } else { if (!TreeIsFullyLoaded()) { InsertRowIntoFirstAvailablePage(row); result = true; } else { AddRowToNewPage(row); result = true; } } } SetContainerState(BTreeContainerState.Ready); } return(result); }
/// <summary> /// Adds a row to this FrostDb instance /// </summary> /// <param name="rowForm">The row data to add</param> /// <returns>True if successful, otherwise false</returns> private bool AddRowLocally(RowForm2 rowForm) { RowInsert rowToInsert = new RowInsert(rowForm.Values, this.Schema, rowForm.Participant.Id, !rowForm.IsLocal(_process), rowForm.Address); if (_storage.RecordTransactionInLog(rowToInsert)) { if (_cache.InsertRow(rowToInsert)) { if (_storage.UpdateIndexes(rowToInsert)) { _cache.SyncTreeToDisk(rowToInsert.Address); _storage.MarkTransactionAsReconciledInLog(rowToInsert); } } } //throw new NotImplementedException(); return(true); }
/// <summary> /// Attempts to write to the xact log for this database the row to be inserted. Will insert the xact as un-reconciled. This method is blocking. /// </summary> /// <param name="row">The row to be inserted.</param> /// <returns>True if successful, otherwise false.</returns> public bool WriteTransactionForInsert(RowInsert row) { bool isSuccessful; if (IsPendingReconciliation(row.XactId)) { throw new InvalidOperationException($"xact {row.XactId.ToString()} is already in progress"); } if (DoesFileExist()) { _locker.EnterWriteLock(); _unreconciledXacts.TryAdd(row.XactId, row.XactId); // to do - need to come up with xact file format // xact xactId tableId isReconciled <action> { Insert | Update | Delete } <data> { RowValues | RowId, RowValues | RowId } var item = new XactLine(row); using (var file = File.AppendText(FileName())) { file.WriteLine(item.ToString()); file.Flush(); } _locker.ExitWriteLock(); isSuccessful = true; } else { isSuccessful = false; } // to do - is this right? return(isSuccessful); }
public void SetRowData(RowInsert row) { row.Values.ToBinaryFormat(); }
/// <summary> /// Updates this insert action in the xact log that it has been reconciled against cache. /// </summary> /// <param name="row">The row that was reconciled</param> /// <returns>True if successful in writing to log, otherwise false</returns> public bool MarkTransactionAsReconciledInLog(RowInsert row) { return(_xactFile.MarkInsertXactAsReconciled(row)); }
/// <summary> /// Records this insert action in the xact log. Will record it as unreconciled against cache. /// </summary> /// <param name="row">The row to record</param> /// <returns>True if successful in writing to log, otherwise false</returns> public bool RecordTransactionInLog(RowInsert row) { return(_xactFile.WriteTransactionForInsert(row)); }
public bool UpdateIndexes(RowInsert row) { return(UpdateIndexesForInsert(row)); }
/// <summary> /// Attempts to add a row to this page and then reconcile the xact on disk /// </summary> /// <param name="row">The row to be added</param> /// <param name="rowId">The id for this row. This should be the next available rowId</param> /// <returns>True if succesful, otherwise false</returns> public bool AddRow(RowInsert row, int rowId) { bool result = false; if (row == null) { throw new ArgumentNullException(nameof(row)); } row.OrderByByteFormat(); // rent 1 byte[] preamble = RentByteArrayFromPool(DatabaseConstants.SIZE_OF_ROW_PREAMBLE); Row2.BuildRowPreamble(ref preamble, rowId, !row.IsReferenceInsert); byte[] rowData = null; if (row.IsReferenceInsert) { // need to save off the GUID id // rent 2 rowData = RentByteArrayFromPool(DatabaseConstants.PARTICIPANT_ID_SIZE); row.ToBinaryFormat(ref rowData); } else { // need to save off the size of the row + all the actual row data // rent 2 rowData = RentByteArrayFromPool(row.Size + DatabaseConstants.SIZE_OF_ROW_SIZE); row.ToBinaryFormat(ref rowData); } // rent 3 byte[] totalRowData = RentByteArrayFromPool(preamble.Length + rowData.Length); // combine the preamble and the row data together Array.Copy(preamble, 0, totalRowData, 0, preamble.Length); Array.Copy(rowData, 0, totalRowData, preamble.Length, rowData.Length); // to do: add totalRowData to this Page's data if (CanInsertNewRow(totalRowData.Length)) { Array.Copy(totalRowData, 0, _data, GetNextAvailableRowOffset(), totalRowData.Length); _totalBytesUsed += totalRowData.Length; _totalRows++; SaveTotalRows(); result = true; } else { // we need to add this row to the next page because we are out of room on this page // throw exception here or... ? } _pendingXacts.Add(row.XactId); // return 1, 2, 3 ReturnByteArrayToPool(ref preamble); ReturnByteArrayToPool(ref rowData); ReturnByteArrayToPool(ref totalRowData); return(result); }