/// <summary> /// Opens the transaction log for writing. /// </summary> private void OpenScript() { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } try { // opens the log file exclusively for writing _file = new FileStream(sFileScript, FileMode.Append, FileAccess.Write, FileShare.None); // todo: use a compressed stream wScript = new StreamWriter(_file, System.Text.Encoding.UTF8); #if !POCKETPC tRunner = new Thread(new ThreadStart(Run)); tRunner.IsBackground = true; tRunner.Start(); #endif } catch (Exception e) { LogHelper.Publish("Unexpected error on OpenScript.", e); TracingHelper.Error(TracingHelper.FILE_IO_ERROR, sFileScript); } }
/// <summary> /// Close the transaction log file. /// </summary> private void CloseScript() { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } try { if (wScript != null) { Stop(); wScript.Close(); wScript = null; _file = null; } } catch (Exception e) { LogHelper.Publish("Unexpected error on CloseScript.", e); TracingHelper.Error(TracingHelper.FILE_IO_ERROR, sFileScript); } }
/// <summary> /// Load properties from file. /// </summary> private void LoadProperties() { try { XmlTextReader reader = new XmlTextReader(sFileProperties); //Read the tokens from the reader while (reader.Read()) { if (XmlNodeType.Element == reader.NodeType) { sFileScript = reader.GetAttribute("LogFile"); sFileCache = reader.GetAttribute("DataFile"); sFileBackup = reader.GetAttribute("Backup"); sModified = reader.GetAttribute("Modified"); sVersion = reader.GetAttribute("Version"); bReadOnly = reader.GetAttribute("ReadOnly").ToLower().Equals("true"); } } reader.Close(); } catch (Exception e) { Console.WriteLine("Property File Exeception:", e.ToString()); } if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } }
/// <summary> /// Save database properties. /// </summary> private void SaveProperties() { lock ( SyncLock ) { //WATYF: //Added check for ".new" file, delete if exists. //Changed writer creation to include ".new" suffix on sFileProperties FileInfo fi = new FileInfo(sFileProperties + ".new"); if (fi.Exists) { fi.Delete(); } XmlTextWriter writer = new XmlTextWriter(sFileProperties + ".new", null); writer.Formatting = Formatting.Indented; writer.Indentation = 4; writer.WriteStartDocument(); writer.WriteComment("SharpHSQL Configuration"); writer.WriteProcessingInstruction("Instruction", "Configuration Record"); writer.WriteStartElement("Properties", ""); writer.WriteStartAttribute("LogFile", ""); writer.WriteString(sFileScript); writer.WriteEndAttribute(); writer.WriteStartAttribute("DataFile", ""); writer.WriteString(sFileCache); writer.WriteEndAttribute(); writer.WriteStartAttribute("Backup", ""); writer.WriteString(sFileBackup); writer.WriteEndAttribute(); writer.WriteStartAttribute("Version", ""); writer.WriteString(sVersion); writer.WriteEndAttribute(); writer.WriteStartAttribute("ReadOnly", ""); if (bReadOnly == true) { writer.WriteString("true"); } else { writer.WriteString("false"); } writer.WriteEndAttribute(); writer.WriteStartAttribute("Modified", ""); writer.WriteString(sModified); writer.WriteEndElement(); writer.WriteEndDocument(); writer.Flush(); writer.Close(); //WATYF: Added RenameNewToCurrent RenameNewToCurrent(sFileProperties); CloseProperties(); if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } } }
/// <summary> /// Checks if the database is already open. /// </summary> /// <returns>True if the databse file is open.</returns> private bool IsAlreadyOpen() { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } FileStream fs = null; try { // try to open the log file exclusively for writing fs = new FileStream(sFileScript, FileMode.Append, FileAccess.Write, FileShare.None); } catch (Exception) { return(true); } finally { if (fs != null) { fs.Close(); fs = null; } } return(false); }
/// <summary> /// Close the transaction log. /// </summary> /// <param name="compact"></param> public void Close(bool compact) { lock ( SyncLock ) { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } if (bReadOnly) { return; } // no more scripting CloseScript(); // create '.script.new' (for this the cache may be still required) WriteScript(compact); // flush the cache (important: after writing the script) cCache.Flush(); // create '.backup.new' using the '.data' Backup(); // we have the new files sModified = "yes-new-files"; SaveProperties(); // old files can be removed and new files renamed RenameNewToCurrent(sFileScript); RenameNewToCurrent(sFileBackup); // now its done completely sModified = "no"; SaveProperties(); CloseProperties(); if (compact) { // stop the runner thread of this process (just for security) Stop(); // delete the .data so then a new file is created (new FileInfo(sFileCache)).Delete(); (new FileInfo(sFileBackup)).Delete(); // all files are closed now; simply open & close this database Database db = new Database(sName); db.Log.Close(false); } } }
/// <summary> /// Script database to file. /// </summary> /// <param name="full"></param> private void WriteScript(bool full) { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } // create script in '.new' file (new FileInfo(sFileScript + ".new")).Delete(); // script; but only positions of cached tables, not full ScriptToFile(dDatabase, sFileScript + ".new", full, cSystem); }
/// <summary> /// Restores a previous backup. /// </summary> private void RestoreBackup() { if (TracingHelper.TraceEnabled) { TracingHelper.Write("Not closed last time!"); } if (!(new FileInfo(sFileBackup)).Exists) { // the backup don't exists because it was never made or is empty // the cache file must be deleted in this case (new FileInfo(sFileCache)).Delete(); return; } try { DateTime time = DateTime.Now; BinaryReader f = new BinaryReader(new FileStream(sFileBackup, FileMode.Open, FileAccess.Read)); BinaryWriter cache = new BinaryWriter(new FileStream(sFileCache, FileMode.OpenOrCreate, FileAccess.Write)); byte[] b = new byte[COPY_BLOCK_SIZE]; while (true) { int l = f.Read(b, 0, COPY_BLOCK_SIZE); if (l == 0) { break; } cache.Write(b, 0, l); } cache.Close(); f.Close(); TimeSpan execution = DateTime.Now.Subtract(time); if (TracingHelper.TraceEnabled) { TracingHelper.Write((Int64)execution.TotalMilliseconds); } } catch (Exception e) { LogHelper.Publish("Unexpected error on RestoreBackup.", e); throw TracingHelper.Error(TracingHelper.FILE_IO_ERROR, sFileBackup); } }
/// <summary> /// Performs a backup of the current database. /// </summary> private void Backup() { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } // if there is no cache file then backup is not necessary if (!(new FileInfo(sFileCache)).Exists) { return; } try { DateTime time = DateTime.Now; // create a '.new' file; rename later BinaryWriter f = new BinaryWriter(new FileStream(sFileBackup + ".new", FileMode.OpenOrCreate, FileAccess.Write)); byte[] b = new byte[COPY_BLOCK_SIZE]; BinaryReader fin = new BinaryReader(new FileStream(sFileCache, FileMode.Open, FileAccess.Read)); while (true) { int l = fin.Read(b, 0, COPY_BLOCK_SIZE); if (l == 0) { break; } f.Write(b, 0, l); } f.Close(); fin.Close(); TimeSpan execution = DateTime.Now.Subtract(time); if (TracingHelper.TraceEnabled) { TracingHelper.Write((Int64)execution.TotalMilliseconds); } } catch (Exception e) { LogHelper.Publish("Unexpected error on Backup.", e); throw TracingHelper.Error(TracingHelper.FILE_IO_ERROR, sFileBackup); } }
/// <summary> /// Close the database properties file. /// </summary> private void CloseProperties() { try { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } } catch (Exception e) { throw TracingHelper.Error(TracingHelper.FILE_IO_ERROR, sFileProperties + " " + e); } }
/// <summary> /// Database class constructor. /// </summary> /// <param name="name">The database name to open or create.</param> public Database(string name) { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } _name = name; _table = new ArrayList(); _access = new Access(); _channel = new Hashtable(); _alias = new Hashtable(); _referentialIntegrity = true; Library.Register(_alias); _databaseInfo = new DatabaseInformation(this, _table, _access); bool newdatabase = false; Channel sys = new Channel(this, new User(null, null, true, null), true, false, 0); RegisterChannel(sys); if (name.Equals(".")) { newdatabase = true; } else { _log = new Log(this, sys, name); newdatabase = _log.Open(); } if (newdatabase) { Execute("CREATE USER SA PASSWORD \"\" ADMIN", sys); } //_access.grant("PUBLIC", "CLASS \"SharpHSQL.Library\"", Access.ALL); }
/// <summary> /// Creates a new properties files. /// </summary> private void Create() { if (TracingHelper.TraceEnabled) { TracingHelper.Write(sName); } XmlTextWriter writer = new XmlTextWriter(sFileProperties, null); writer.Formatting = Formatting.Indented; writer.Indentation = 4; writer.WriteStartDocument(); writer.WriteComment("SharpHSQL Configuration"); writer.WriteProcessingInstruction("Instruction", "Configuration Record"); writer.WriteStartElement("Properties", ""); writer.WriteStartAttribute("LogFile", ""); writer.WriteString(sFileScript); writer.WriteEndAttribute(); writer.WriteStartAttribute("DataFile", ""); writer.WriteString(sFileCache); writer.WriteEndAttribute(); writer.WriteStartAttribute("Backup", ""); writer.WriteString(sFileBackup); writer.WriteEndAttribute(); writer.WriteStartAttribute("Version", Assembly.GetExecutingAssembly().GetName().Version.ToString()); writer.WriteString("1.0"); writer.WriteEndAttribute(); writer.WriteStartAttribute("ReadOnly", ""); writer.WriteString("false"); writer.WriteEndAttribute(); writer.WriteStartAttribute("Modified", ""); writer.WriteString("no"); writer.WriteEndElement(); writer.WriteEndDocument(); writer.Flush(); writer.Close(); SaveProperties(); }
/// <summary> /// Opens the transaction log and runs it. /// </summary> private void RunScript() { if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } if (!(new FileInfo(sFileScript)).Exists) { return; } bRestoring = true; dDatabase.IsReferentialIntegrity = false; ArrayList channel = new ArrayList(); channel.Add(cSystem); Channel current = cSystem; int size = 1; try { DateTime time = DateTime.Now; StreamReader r = new StreamReader(sFileScript); while (true) { string s = r.ReadLine(); if (s == null) { break; } if (s.StartsWith("/*C")) { int id = Int32.Parse(s.Substring(3, (s.IndexOf('*', 4) - 3))); if (id > (channel.Count - 1)) { current = new Channel(cSystem, id); channel.Add(current); dDatabase.RegisterChannel(current); } else { current = (Channel)channel[id]; } s = s.Substring(s.IndexOf('/', 1) + 1); } if (!s.Equals("")) { dDatabase.Execute(s, current); } if (s.Equals("DISCONNECT")) { int id = current.Id; current = new Channel(cSystem, id); channel.RemoveAt(id); channel.Insert(id, current); } } r.Close(); for (int i = 0; i < size; i++) { current = (Channel)channel[i]; if (current != null) { current.Rollback(); } } TimeSpan execution = DateTime.Now.Subtract(time); if (TracingHelper.TraceEnabled) { TracingHelper.Write((Int64)execution.TotalMilliseconds); } } catch (IOException e) { throw TracingHelper.Error(TracingHelper.FILE_IO_ERROR, sFileScript + " " + e); } dDatabase.IsReferentialIntegrity = true; bRestoring = false; }
/// <summary> /// Script the database objects to a file. /// </summary> /// <param name="db"></param> /// <param name="file"></param> /// <param name="full"></param> /// <param name="channel"></param> public static void ScriptToFile(Database db, string file, bool full, Channel channel) { if ((new FileInfo(file)).Exists) { // there must be no such file; overwriting not allowed for security throw TracingHelper.Error(TracingHelper.FILE_IO_ERROR, file); } try { DateTime time = DateTime.Now; // only ddl commands; needs not so much memory Result r; if (full) { // no drop, no insert, and no positions for cached tables r = db.GetScript(false, false, false, channel); } else { // no drop, no insert, but positions for cached tables r = db.GetScript(false, false, true, channel); } Record n = r.Root; StreamWriter w = new StreamWriter(file); while (n != null) { writeLine(w, (string)n.Data[0]); n = n.Next; } // inserts are done separetely to save memory ArrayList tables = db.Tables; for (int i = 0; i < tables.Count; i++) { Table t = (Table)tables[i]; // cached tables have the index roots set in the ddl script if (full || !t.IsCached) { Index primary = t.PrimaryIndex; Node x = primary.First(); while (x != null) { writeLine(w, t.GetInsertStatement(x.GetData())); x = primary.Next(x); } } } w.Close(); TimeSpan execution = DateTime.Now.Subtract(time); if (TracingHelper.TraceEnabled) { TracingHelper.Write((Int64)execution.TotalMilliseconds); } } catch (IOException e) { TracingHelper.Error(TracingHelper.FILE_IO_ERROR, file + " " + e); } }
/// <summary> /// Opens the database files. /// </summary> /// <returns>True if operation is sucessful.</returns> public bool Open() { lock ( SyncLock ) { bool newdata = false; if (TracingHelper.TraceEnabled) { TracingHelper.Write(); } if (!(new FileInfo(sFileProperties)).Exists) { Create(); // this is a new database newdata = true; } // todo: some parts are not necessary for read-only access LoadProperties(); if (bReadOnly == true) { dDatabase.SetReadOnly(); cCache = new Cache(sFileCache); cCache.Open(true); RunScript(); return(false); } bool needbackup = false; if (sModified.Equals("yes-new-files")) { RenameNewToCurrent(sFileScript); RenameNewToCurrent(sFileBackup); } else if (sModified.Equals("yes")) { if (IsAlreadyOpen()) { throw TracingHelper.Error(TracingHelper.DATABASE_ALREADY_IN_USE); } // recovering after a crash (or forgot to close correctly) RestoreBackup(); needbackup = true; } sModified = "yes"; SaveProperties(); cCache = new Cache(sFileCache); cCache.Open(false); RunScript(); if (needbackup) { Close(false); sModified = "yes"; SaveProperties(); cCache.Open(false); } OpenScript(); // this is a existing database return(newdata); } }
/// <summary> /// Executes an SQL statement and return the results. /// </summary> /// <param name="statement">The SQL statement to execute.</param> /// <param name="channel">The channel to use.</param> /// <returns>The Result object.</returns> public Result Execute(string statement, Channel channel) { if (TracingHelper.TraceEnabled) { TracingHelper.Write(statement); } Tokenizer c = new Tokenizer(statement); Parser p = new Parser(this, c, channel); Result rResult = new Result(); string newStatement = string.Empty; int updateCount = 0; try { if (_log != null && _log.cCache != null) { _log.cCache.CleanUp(); } if (TracingHelper.AssertEnabled) { TracingHelper.Assert(!channel.IsNestedTransaction); } TracingHelper.Check(channel != null, TracingHelper.ACCESS_IS_DENIED); TracingHelper.Check(!_shutDown, TracingHelper.DATABASE_IS_SHUTDOWN); while (true) { int begin = c.Position; bool script = false; string sToken = c.GetString(); if (sToken.Equals("")) { break; } switch (sToken) { case "SELECT": rResult = p.ProcessSelect(); break; case "INSERT": rResult = p.ProcessInsert(); break; case "UPDATE": rResult = p.ProcessUpdate(); break; case "DELETE": rResult = p.ProcessDelete(); break; case "ALTER": rResult = p.ProcessAlter(); break; case "CREATE": rResult = ProcessCreate(c, channel); script = true; break; case "DROP": rResult = ProcessDrop(c, channel); script = true; break; case "GRANT": rResult = ProcessGrantOrRevoke(c, channel, true); script = true; break; case "REVOKE": rResult = ProcessGrantOrRevoke(c, channel, false); script = true; break; case "CONNECT": rResult = ProcessConnect(c, channel); break; case "DISCONNECT": rResult = ProcessDisconnect(c, channel); break; case "SET": rResult = ProcessSet(c, channel); script = true; break; case "SCRIPT": rResult = ProcessScript(c, channel); break; case "COMMIT": rResult = ProcessCommit(c, channel); script = true; break; case "ROLLBACK": rResult = ProcessRollback(c, channel); script = true; break; case "SHUTDOWN": rResult = ProcessShutdown(c, channel); break; case "CHECKPOINT": rResult = ProcessCheckpoint(channel); break; case "CALL": rResult = p.ProcessCall(); break; case "SHOW": rResult = ProcessShow(c, channel); break; case "DECLARE": rResult = p.ProcessDeclare(); script = true; break; case ";": continue; default: throw TracingHelper.Error(TracingHelper.UnexpectedToken, sToken); } if (rResult != null && rResult.UpdateCount > updateCount) { updateCount = rResult.UpdateCount; } if (script && _log != null) { int end = c.Position; _log.Write(channel, c.GetPart(begin, end)); } } } catch (Exception e) { rResult = new Result(TracingHelper.GetMessage(e) + " in statement [" + statement + "]"); } if (rResult != null && rResult.UpdateCount < updateCount) { rResult.SetUpdateCount(updateCount); } return(rResult); }