/// <summary> /// Default constructor. /// </summary> /// <param name="ownerDb">Table that owns this row.</param> /// <param name="rowStartDataPage">Data page on what row starts.</param> internal LDB_Record(DbFile ownerDb,DataPage rowStartDataPage) { m_pOwnerDb = ownerDb; m_pDataPage = rowStartDataPage; ParseRowInfo(); }
/// <summary> /// Updates this record values. /// </summary> /// <param name="rowValues">Row new values.</param> private void UpdateRecord(object[] rowValues) { bool unlock = true; // Table is already locked, don't lock it if(m_pOwnerDb.TableLocked){ unlock = false; } else{ m_pOwnerDb.LockTable(15); } // Create new record byte[] rowData = LDB_Record.CreateRecord(m_pOwnerDb,rowValues); DataPage[] dataPages = this.DataPages; // Clear old data ?? do we need that // for(int i=0;i<dataPages.Length;i++){ // dataPages[i].Data = new byte[1000]; // } // We haven't enough room to store row, get needed data pages if((int)Math.Ceiling(rowData.Length / (double)m_pOwnerDb.DataPageDataAreaSize) > dataPages.Length){ int dataPagesNeeded = (int)Math.Ceiling(rowData.Length / (double)m_pOwnerDb.DataPageDataAreaSize) - dataPages.Length; DataPage[] additionalDataPages = m_pOwnerDb.GetDataPages(dataPages[dataPages.Length - 1].Pointer,dataPagesNeeded); // Append new data pages to existing data pages chain dataPages[dataPages.Length - 1].NextDataPagePointer = additionalDataPages[0].Pointer; DataPage[] newVal = new DataPage[dataPages.Length + additionalDataPages.Length]; Array.Copy(dataPages,0,newVal,0,dataPages.Length); Array.Copy(additionalDataPages,0,newVal,dataPages.Length,additionalDataPages.Length); dataPages = newVal; } // Store new record DbFile.StoreDataToDataPages(m_pOwnerDb.DataPageDataAreaSize,rowData,dataPages); // Update row info ParseRowInfo(); if(unlock){ m_pOwnerDb.UnlockTable(); } }
/// <summary> /// Gets specified column data. /// </summary> /// <param name="columnIndex">Column index.</param> /// <returns></returns> private object GetColumnData(int columnIndex) { // Get column data start offset int columnStartOffset = 4 * m_pOwnerDb.Columns.Count; for(int i=0;i<columnIndex;i++){ columnStartOffset += m_ColumnValueSize[i]; } int dataLength = m_ColumnValueSize[columnIndex]; int startDataPage = (int)Math.Floor(columnStartOffset / (double)m_pOwnerDb.DataPageDataAreaSize); int offsetInStartDataPage = columnStartOffset - (startDataPage * m_pOwnerDb.DataPageDataAreaSize); int dataOffset = 0; int currentDataPageIndex = 0; byte[] columnData = new byte[dataLength]; DataPage currentDataPage = this.DataPage; while(dataOffset < dataLength){ // We haven't reached to data page on what data starts, just go to next continuing data page if(currentDataPageIndex < startDataPage){ // Get next continuing data page currentDataPage = new DataPage(m_pOwnerDb.DataPageDataAreaSize,m_pOwnerDb,currentDataPage.NextDataPagePointer); currentDataPageIndex++; } // We need all this data page data + addtitional data pages data else if((dataLength - dataOffset + offsetInStartDataPage) > currentDataPage.StoredDataLength){ currentDataPage.ReadData(columnData,dataOffset,m_pOwnerDb.DataPageDataAreaSize - offsetInStartDataPage,offsetInStartDataPage); dataOffset += m_pOwnerDb.DataPageDataAreaSize - offsetInStartDataPage; // Get next continuing data page currentDataPage = new DataPage(m_pOwnerDb.DataPageDataAreaSize,m_pOwnerDb,currentDataPage.NextDataPagePointer); currentDataPageIndex++; offsetInStartDataPage = 0; } // This data page has all data we need else{ currentDataPage.ReadData(columnData,dataOffset,dataLength - (int)dataOffset,offsetInStartDataPage); dataOffset += dataLength - (int)dataOffset; offsetInStartDataPage = 0; } } // Convert to column data type return ConvertFromInternalData(m_pOwnerDb.Columns[columnIndex],columnData); }
/// <summary> /// Gets specified number of free data pages. If free data pages won't exist, creates new ones. /// Data pages are marked as used and OwnerDataPagePointer and NextDataPagePointer is set as needed. /// </summary> /// <param name="ownerDataPagePointer">Owner data page pointer that own first requested data page. If no owner then this value is 0.</param> /// <param name="count">Number of data pages wanted.</param> internal DataPage[] GetDataPages(long ownerDataPagePointer,int count) { if(!this.TableLocked){ throw new Exception("Table must be locked to acess GetDataPages() method !"); } ArrayList freeDataPages = new ArrayList(); // Get free data pages count from table header byte[] freeDataPagesCount = new byte[8]; SetFilePosition(52); ReadFromFile(freeDataPagesCount,0,freeDataPagesCount.Length); long nFreeDataPages = ldb_Utils.ByteToLong(freeDataPagesCount,0); // We have plenty free data pages and enough for count requested, find requested count free data pages if(nFreeDataPages > 1000 && nFreeDataPages > count){ long nextDataPagePointer = m_DatapagesStartOffset + 1; while(freeDataPages.Count < count){ DataPage dataPage = new DataPage(m_DataPageDataAreaSize,this,nextDataPagePointer); if(!dataPage.Used){ dataPage.Used = true; freeDataPages.Add(dataPage); } nextDataPagePointer += m_DataPageDataAreaSize + 33; } // Decrease free data pages count in table header SetFilePosition(52); ReadFromFile(ldb_Utils.LongToByte(nFreeDataPages - count),0,8); } // Just create new data pages else{ for(int i=0;i<count;i++){ byte[] dataPage = DataPage.CreateDataPage(m_DataPageDataAreaSize,true,0,0,0,new byte[m_DataPageDataAreaSize]); GoToFileEnd(); long dataPageStartPointer = GetFilePosition(); WriteToFile(dataPage,0,dataPage.Length); freeDataPages.Add(new DataPage(m_DataPageDataAreaSize,this,dataPageStartPointer)); } } // Relate data pages (chain) for(int i=0;i<freeDataPages.Count;i++){ // First data page if(i == 0){ // Owner data page poitner specified for first data page if(ownerDataPagePointer > 0){ ((DataPage)freeDataPages[i]).OwnerDataPagePointer = ownerDataPagePointer; } // There is continuing data page if(freeDataPages.Count > 1){ ((DataPage)freeDataPages[i]).NextDataPagePointer = ((DataPage)freeDataPages[i + 1]).Pointer; } } // Last data page else if(i == (freeDataPages.Count - 1)){ ((DataPage)freeDataPages[i]).OwnerDataPagePointer = ((DataPage)freeDataPages[i - 1]).Pointer; } // Middle range data page else{ ((DataPage)freeDataPages[i]).OwnerDataPagePointer = ((DataPage)freeDataPages[i - 1]).Pointer; ((DataPage)freeDataPages[i]).NextDataPagePointer = ((DataPage)freeDataPages[i + 1]).Pointer; } } DataPage[] retVal = new DataPage[freeDataPages.Count]; freeDataPages.CopyTo(retVal); return retVal; }
/// <summary> /// Stores data to specified data pages. /// </summary> /// <param name="dataPageDataAreaSize">Data page data area size.</param> /// <param name="data">Data to store.</param> /// <param name="dataPages">Data pages where to store data.</param> internal static void StoreDataToDataPages(int dataPageDataAreaSize,byte[] data,DataPage[] dataPages) { if((int)Math.Ceiling(data.Length / (double)dataPageDataAreaSize) > dataPages.Length){ throw new Exception("There isn't enough data pages to store data ! Data needs '" + (int)Math.Ceiling(data.Length / (double)dataPageDataAreaSize) + "' , but available '" + dataPages.Length + "'."); } //--- Store data to data page(s) -----------------------// long position = 0; for(int i=0;i<dataPages.Length;i++){ if((data.Length - position) > dataPageDataAreaSize){ byte[] d = new byte[dataPageDataAreaSize]; Array.Copy(data,position,d,0,d.Length); dataPages[i].WriteData(d); position += dataPageDataAreaSize; } else{ byte[] d = new byte[data.Length - position]; Array.Copy(data,position,d,0,d.Length); dataPages[i].WriteData(d); } } //------------------------------------------------------// }
/// <summary> /// Gets next record. Returns true if end of file reached and there are no more records. /// </summary> /// <returns>Returns true if end of file reached and there are no more records.</returns> public bool NextRecord() { if(!this.IsDatabaseOpen){ throw new Exception("Database isn't open, please open database first !"); } //--- Find next record ---------------------------------------------------// long nextRowStartOffset = 0; if(m_pCurrentRecord == null){ nextRowStartOffset = m_DatapagesStartOffset; } else{ nextRowStartOffset = m_pCurrentRecord.DataPage.Pointer + m_DataPageDataAreaSize + 33; } while(true){ if(m_FileLength > nextRowStartOffset){ DataPage dataPage = new DataPage(m_DataPageDataAreaSize,this,nextRowStartOffset); // We want datapage with used space if(dataPage.Used && dataPage.OwnerDataPagePointer < 1){ m_pCurrentRecord = new LDB_Record(this,dataPage); break; } } else{ return true; } nextRowStartOffset += m_DataPageDataAreaSize + 33; } //-------------------------------------------------------------------------// return false; }