/// <summary> /// Open /// </summary> /// <param name="fname"></param> public void Open(string fname) { lock(this.TableBlocking) { RaiseExceptionIfOpened(); if(fname.ToLower().EndsWith(".hnd")) fname=fname.Substring(0,fname.Length-4); DatabaseFilePath=System.IO.Path.GetFullPath(fname)+".hnd"; // Initial values if(!File.Exists(this.DatabaseFilePath)) { try { fsDB = new FileStream(this.DatabaseFilePath,FileMode.Create,FileAccess.ReadWrite,FileShare.None,8*1024); } catch { throw new Exception("Can't create file."); } } else { try { fsDB = new FileStream(this.DatabaseFilePath,FileMode.Open,FileAccess.ReadWrite,FileShare.None,8*1024); } catch { throw new Exception("Database in use."); } } long len = (fsDB.Length/PageSize); len*=PageSize; if(fsDB.Length>len) { this.LogToFile("Warning","File size fixed."); fsDB.SetLength(len); } slFID2Pages = new SortedList(); TableName2TID = new Hashtable(); TID2Def = new Hashtable(); pcInit(); //PagesInUse = new SortedSet(); DeletedPages = new SortedSet(); br = new BinaryReader(fsDB,System.Text.Encoding.Unicode); bw = new BinaryWriter(fsDB,System.Text.Encoding.Unicode); // check log file if(true) { string lfn = DatabaseFilePath+".hlg"; if(File.Exists(lfn)) { FileStream lf = new FileStream(lfn,FileMode.Open,FileAccess.ReadWrite,FileShare.None); BinaryReader lfr = new BinaryReader(lf,System.Text.Encoding.Unicode); try { if((lfr.BaseStream.Length>0)&&lfr.ReadBoolean()) {// recover from last crash byte logtype = lfr.ReadByte(); if(logtype==0) {// delete pages op this.LogToFile("Warning","Deleted pages fixed."); ArrayList al = new ArrayList(); int cnt = lfr.ReadInt32(); for(int n=0;n<cnt;n++) { al.Add( lfr.ReadInt32() ); } for(int n=0;n<cnt;n++) { bw.BaseStream.Position=PageSize*( (int)al[n] ); bw.Write( true ); // deleted } bw.Flush(); lf.SetLength(0); lf.Flush(); } if(logtype==1) {// rollback pages this.LogToFile("Warning","Rollback modified pages."); int pcount = lfr.ReadInt32(); // num of pages for(int p=0;p<pcount;p++) { int page = lfr.ReadInt32(); fsDB.Position=PageSize*page; byte[] buf = lfr.ReadBytes( Database.PageSize ); bw.Write( buf ); } bw.Flush(); lf.SetLength(0); lf.Flush(); } } } catch { Close(); throw new Exception("Can't recover from last crash."); } finally { lf.Close(); } } } ArrayList pagePurgatory = new ArrayList(); Hashtable htFieldsByTID = new Hashtable();// contains Hastables by field seq Hashtable htDataByTID = new Hashtable(); // key: tid + fieldseq + dataseq = pageno Set ProcessedPages = new SortedSet(); #region 1st Pass: Scan deleted pages and table pages NextFID=-1; try { int pos=0; // page counter fsDB.Position=0; while(fsDB.Position<fsDB.Length) { // leemos info de página long ptr = br.BaseStream.Position; bool bPageIsDeleted = br.ReadBoolean(); if(bPageIsDeleted) { ProcessedPages.Add(pos); this.DeletedPages.Add(pos); } else { byte bPageType = br.ReadByte(); int fid = br.ReadInt32(); if(bPageType==TablePageType) { ProcessedPages.Add(pos); TableNameDef tnd = new TableNameDef(fid,pos); tnd.fseq=br.ReadInt32(); tnd.rownum=br.ReadInt64(); tnd.tname = br.ReadString(); TID2Def[fid]=tnd; TableName2TID[tnd.tname]=fid; } else if(bPageType==FieldPageType) {// Page is a field def, store it for further processing ProcessedPages.Add(pos); int tid = fid; //TableNameDef tnd = TID2Def[tid] as TableNameDef; Field fld = new Field(); fld.Read(br);// 4-field fld.tid=tid; fld.PageOfFieldSef=pos; if(!htFieldsByTID.ContainsKey(tid)) htFieldsByTID[tid]=new Hashtable(); Hashtable htFieldsBySeq = htFieldsByTID[tid] as Hashtable; // avoid repeated fields bool bAvoid=false; foreach(Field f in htFieldsBySeq.Values) { if(f.Name==fld.Name) { bAvoid=true; break; } } if(!bAvoid) { htFieldsBySeq[fld.seq]=fld; //tnd.fseq2FieldDef[fld.seq]=fld; } else { pagePurgatory.Add(pos); } } else if(bPageType==ContentPageType) { int tid = fid; if(!htDataByTID.ContainsKey(tid)) htDataByTID[tid]=new Hashtable(); Hashtable htDataByFSeq = htDataByTID[tid] as Hashtable; long fseq = br.ReadInt32(); // 4º seq of field if(!htDataByFSeq.ContainsKey(fseq)) htDataByFSeq[fseq]=new ArrayList(); ArrayList alDataByOrder = htDataByFSeq[fseq] as ArrayList; int seq = br.ReadInt32(); // 5º data page order while(alDataByOrder.Count<=seq) alDataByOrder.Add(-1); alDataByOrder[seq]=pos; } NextFID = Math.Max( NextFID, fid ); PeekPagesByFID(fid).Add(pos); } fsDB.Position = Database.PageSize + ptr; pos++; } NextFID++; } catch(Exception ex) { this.LogToFile(ex.Message,ex.StackTrace); this.Close(); throw new Exception("Database corrupted."); } #endregion #region 2nd Pass: Field integration // try // { foreach(int tid in htFieldsByTID.Keys) { TableNameDef tnd = TID2Def[tid] as TableNameDef; Hashtable htFieldsBySeq = htFieldsByTID[tid] as Hashtable; foreach(long seq in htFieldsBySeq.Keys) { tnd.fseq2FieldDef[seq]=htFieldsBySeq[seq]; } } // // int pos=0; // page counter // fsDB.Position=0; // while(fsDB.Position<fsDB.Length) // { // // leemos info de página // long ptr = br.BaseStream.Position; // if(!ProcessedPages.Contains(pos)) // { // bool bPageIsDeleted = br.ReadBoolean();// 1-deleted // if(bPageIsDeleted) // { // // skip // } // else // { // byte bPageType = br.ReadByte();// 2-type // int tid = br.ReadInt32(); // 3-fid of table // if(bPageType==FieldPageType) // { // ProcessedPages.Add(pos); // TableNameDef tnd = TID2Def[tid] as TableNameDef; // Field fld = new Field(); // fld.Read(br);// 4-field // fld.tid=tid; // fld.PageOfFieldSef=pos; // // // avoid repeated fields // bool bAvoid=false; // foreach(Field f in tnd.fseq2FieldDef.Values) // { // if(f.Name==fld.Name) // { // bAvoid=true; // break; // } // } // if(!bAvoid) // { // tnd.fseq2FieldDef[fld.seq]=fld; // } // else // { // pagePurgatory.Add(pos); // } // } // } // } // fsDB.Position = Database.PageSize + ptr; // pos++; // } // } // catch(Exception ex) // { // this.LogToFile(ex.Message,ex.StackTrace); // this.Close(); // throw new Exception("Database corrupted."); // } #endregion #region 3nd Pass: Locate data for fields try { foreach(int tid in htDataByTID.Keys) { TableNameDef tnd = TID2Def[tid] as TableNameDef; Hashtable htDataByFSeq = htDataByTID[tid] as Hashtable; foreach(long seq in htDataByFSeq.Keys) { ArrayList alDataByOrder = htDataByFSeq[seq] as ArrayList; if(!tnd.fseq2FieldDef.ContainsKey(seq)) { pagePurgatory.AddRange( alDataByOrder ); } else { Field fld = tnd.fseq2FieldDef[seq] as Field; fld.DataFID=alDataByOrder; } } } // int pos=0; // page counter // fsDB.Position=0; // while(fsDB.Position<fsDB.Length) // { // // leemos info de página // long ptr = br.BaseStream.Position; // if(!ProcessedPages.Contains(pos)) // { // bool bPageIsDeleted = br.ReadBoolean();// 1º deleted is on? // if(bPageIsDeleted) // { // // skip // } // else // { // byte bPageType = br.ReadByte();// 2º Type // int tid = br.ReadInt32();// 3º fid of table // if(bPageType==ContentPageType) // { // long fseq = br.ReadInt32(); // 4º seq of field // int seq = br.ReadInt32(); // 5º data page order // TableNameDef tnd = TID2Def[tid] as TableNameDef; // if(!tnd.fseq2FieldDef.ContainsKey(fseq)) // { // pagePurgatory.Add(pos); // } // Field fld = tnd.fseq2FieldDef[fseq] as Field; // while(fld.DataFID.Count<=seq) // fld.DataFID.Add(-1); // fld.DataFID[seq]=pos; // } // } // } // fsDB.Position = Database.PageSize + ptr; // pos++; // } foreach(TableNameDef tnd in TID2Def.Values) foreach(Field f in tnd.fseq2FieldDef.Values) foreach(int page in f.DataFID) if(page==-1) throw new Exception("Database corrupted."); } catch(Exception ex) { this.LogToFile(ex.Message,ex.StackTrace); this.Close(); throw new Exception("Database corrupted."); } #endregion foreach(TableNameDef tnd in TID2Def.Values) foreach(Field f in tnd.fseq2FieldDef.Values) { // grow if it is needed if(tnd.rownum>0) { int valSize = (int)f.DataSize(); long Capacity = (PageSize-ContentPageDataOffset)/valSize; ArrayList pages = f.DataFID; while((pages.Count*Capacity)<tnd.rownum) { int datapage = this.LockAvaiblePage(); bw.BaseStream.Position = (datapage*PageSize); bw.Write( true ); bw.Flush(); bw.Write( (byte)Database.ContentPageType ); bw.Write( tnd.TableFID ); bw.Write( (int)f.seq ); bw.Write( f.DataFID.Count ); bw.Flush(); for(int c=0;c<Capacity;c++) { bw.BaseStream.Position = (datapage*PageSize)+ContentPageDataOffset+c*valSize; f.WriteDefaultData(bw,false); } bw.Flush(); bw.BaseStream.Position = (datapage*PageSize); bw.Write( (bool)false ); bw.Flush(); pages.Add(datapage); PeekPagesByFID(tnd.TableFID).Add(datapage); this.InvalidatePage(datapage); } } } // Autoseq table this.AddTableIfNotExist(tblSequences); this.AddFieldIfNotExist(tblSequences, new Field("SEQNAME","",FieldIndexing.Unique,40)); this.AddFieldIfNotExist(tblSequences, new Field("SEQVALUE",(long)0,FieldIndexing.None)); this.AddFieldIfNotExist(tblSequences, new Field("SEQINCREMENT",(long)1,FieldIndexing.None)); this.AddFieldIfNotExist(tblSequences, new Field("SEQLOOP",false,FieldIndexing.None)); this.AddFieldIfNotExist(tblSequences, new Field("SEQMAXVALUE",long.MaxValue,FieldIndexing.None)); // Autoseq table this.AddTableIfNotExist(tblAlterTbl); this.AddFieldIfNotExist(tblAlterTbl, new Field("TNAME","",FieldIndexing.None,80)); this.AddFieldIfNotExist(tblAlterTbl, new Field("FSRC","",FieldIndexing.None,80)); this.AddFieldIfNotExist(tblAlterTbl, new Field("FTMP","",FieldIndexing.None,80)); this.AddFieldIfNotExist(tblAlterTbl, new Field("STATE",(int)1,FieldIndexing.None)); // Unknown bugfix -> Purge pages foreach(int i in pagePurgatory) { if(i==-1) continue; bw.BaseStream.Position = (i*PageSize); bw.Write( true ); bw.Flush(); } } GC.Collect(); GC.WaitForPendingFinalizers(); }
void DumpPage(StreamWriter sw,int page,int ident) { fsDB.Position=page*PageSize; bool deleted = br.ReadBoolean(); byte type = br.ReadByte(); int fid = br.ReadInt32(); sw.Write( new string(' ',ident) ); sw.Write( "Page num: "+page.ToString("N4")+" " ); sw.Write( "Deleted: "+deleted.ToString()+" " ); if(!deleted) { sw.Write( "TID: "+fid.ToString()+" " ); if(type==Database.TablePageType) { sw.Write( "Type: Table " ); sw.Write( "fseq: "+br.ReadInt32().ToString("G04")+" " ); sw.Write( "rownum: "+br.ReadInt64().ToString()+" " ); sw.Write( "tname: "+br.ReadString().ToString()+" " ); } else if(type==Database.FieldPageType) { sw.Write( "Type: Field " ); Field fld = new Field(); fld.Read(br);// 4-field sw.Write( "fseq: "+fld.seq.ToString()+" " ); sw.Write( "name: "+fld.Name.ToString()+" " ); sw.Write( "type: "+fld.type.ToString()+" " ); } else if(type==Database.ContentPageType) { sw.Write( "Type: Content " ); sw.Write( "fseq: "+br.ReadInt32().ToString("G04")+" " ); sw.Write( "PageOrder: "+br.ReadInt32().ToString("G04")+" " ); } } sw.WriteLine(""); }