using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SQLite; using System.IO; using System.Text; namespace RyzStudio.Data.SQLite { public class SQLiteDatabase { public SQLiteConnection DBConnection { get; protected set; } = null; public string DBLocation { get; protected set; } = null; public string LastError { get; protected set; } = null; public int LastInsertID { get { if (this.DBConnection == null) { return 0; } DataTable dt = this.DoQuery("SELECT last_insert_rowid() AS rv;"); if (dt == null) { return 0; } if (dt.Rows.Count <= 0) { return 0; } int n; if (!int.TryParse(dt.Rows[0]["rv"].ToString(), out n)) { n = 0; } return n; } } protected string[] requiredTableList = new string[0]; protected const string appSettingsTableName = "ryz_app_xxxx_config"; public bool Create() { this.LastError = string.Empty; this.DBLocation = ":memory:"; try { this.DBConnection = new SQLiteConnection(string.Concat("Data Source=\"", this.DBLocation, "\";Version=3;UTF8Encoding=True;")); this.DBConnection.Open(); } catch (Exception exc) { this.LastError = exc.Message; return false; } return true; } public bool Create(string filename, bool overwriteFile = false, string password = null, bool useAppSettings = false) { bool rs = create(filename, overwriteFile, password); if (useAppSettings) { if (!rs) { return false; } rs = prepareDatabase(); if (!rs) { return false; } return this.HasRequiredTables(); } else { return rs; } } public bool LoadFile(string filename, string password = null) { this.LastError = string.Empty; if (!File.Exists(filename)) { return false; } this.DBLocation = filename; try { this.DBConnection = new SQLiteConnection(string.Concat("Data Source=\"", filename, "\";Version=3;UTF8Encoding=True;", (password == null) ? string.Empty : string.Concat("Password=", encode64(password), ";"))); this.DBConnection.Open(); } catch (Exception exc) { this.LastError = exc.Message; return false; } return true; } public void Close() { this.LastError = string.Empty; if (this.DBConnection != null) { try { this.DBConnection.Cancel(); this.DBConnection.Close(); this.DBConnection.Dispose(); this.DBConnection = null; SQLiteConnection.ClearAllPools(); GC.Collect(); } catch (Exception exc) { this.LastError = exc.Message; } } } public DataTable DoQuery(string query) { this.LastError = string.Empty; if (this.DBConnection == null) { return null; } try { SQLiteCommand command = new SQLiteCommand(query, this.DBConnection); SQLiteDataReader dr = command.ExecuteReader(); DataTable dt = new DataTable(); dt.Load(dr); return dt; } catch (Exception exc) { this.LastError = exc.Message; return null; } } public DataTable DoQuery(string query, params string[] args) => DoQuery(prepareQuery(query, args)); public int DoQueryCount(string query) { this.LastError = string.Empty; if (this.DBConnection == null) { return -1; } DataTable dt = this.DoQuery(query); if (dt == null) { return -1; } return dt.Rows.Count; } public int DoQueryCount(string query, params string[] args) => this.DoQueryCount(prepareQuery(query, args)); public bool DoQueryExist(string query) => (this.DoQueryCount(query) > 0); public bool DoQueryExist(string query, params string[] args) => this.DoQueryExist(prepareQuery(query, args)); public string DoQuerySingle(string query) { this.LastError = string.Empty; if (this.DBConnection == null) { return string.Empty; } DataTable dt = this.DoQuery(query); if (dt == null) { return string.Empty; } if (dt.Columns.Count <= 0) { return string.Empty; } if (dt.Rows.Count <= 0) { return string.Empty; } if (dt.Rows[0][0] is byte[]) { return Encoding.UTF8.GetString(dt.Rows[0][0] as byte[]); } else { return dt.Rows[0][0].ToString(); } } public string DoQuerySingle(string query, params string[] args) => this.DoQuerySingle(prepareQuery(query, args)); public int DoNonQuery(string query) { this.LastError = string.Empty; if (this.DBConnection == null) { return -1; } int rv = 0; try { SQLiteCommand command = new SQLiteCommand(query, this.DBConnection); rv = command.ExecuteNonQuery(); } catch (Exception exc) { this.LastError = exc.Message; rv = -1; } return rv; } public int DoNonQuery(string query, params string[] args) => this.DoNonQuery(prepareQuery(query, args)); public bool HasTable(string tableName) { this.LastError = string.Empty; if (this.DBConnection == null) { return false; } int rv = this.DoQueryCount("SELECT 1 FROM sqlite_master WHERE type='table' AND name='" + escapeSQL(tableName) + "'"); return (rv > 0); } public bool HasRequiredTables() { bool rv = true; foreach (string tbl in requiredTableList) { if (string.IsNullOrEmpty(tbl)) { continue; } if (!this.HasTable(tbl)) { rv = false; break; } } return rv; } public bool SetConfig(string name, bool value) => this.SetConfig(name, (value ? "1" : "0")); public bool SetConfig(string name, int value) => this.SetConfig(name, value.ToString()); public bool SetConfig(string name, string value) { prepareAppSettings(); string sql = string.Empty; int rv = this.DoQueryCount("SELECT 1 FROM " + appSettingsTableName + " WHERE cfg_name='" + escapeSQL(name) + "'"); if (rv <= 0) { sql = "INSERT INTO " + appSettingsTableName + " (cfg_name, cfg_value) VALUES ('[^1]', '[^2]');"; } else { sql = "UPDATE " + appSettingsTableName + " SET cfg_value='[^2]' WHERE cfg_name='[^1]';"; } sql = prepareQuery(sql, new string[] { name, value }); return this.DoNonQuery(sql) > 0; } public string GetConfig(string name, string defaultValue = null) { prepareAppSettings(); bool rv = this.DoQueryExist("SELECT 1 FROM " + appSettingsTableName + " WHERE cfg_name='" + escapeSQL(name) + "';"); if (!rv) { return defaultValue; } return this.DoQuerySingle("SELECT cfg_value FROM " + appSettingsTableName + " WHERE cfg_name='" + escapeSQL(name) + "';"); } public int GetIntConfig(string name, int defaultValue = 0) { string rv = this.GetConfig(name); if (string.IsNullOrWhiteSpace(rv)) { return defaultValue; } int n; if (!int.TryParse(rv, out n)) { n = defaultValue; } return n; } public bool GetBoolConfig(string name, bool defaultValue = false) { string rv = this.GetConfig(name); if (string.IsNullOrWhiteSpace(rv)) { return defaultValue; } if (rv.Equals("1")) { return true; } if (rv.Equals("true", StringComparison.CurrentCultureIgnoreCase)) { return true; } return false; } protected bool create(string filename, bool overwriteFile, string password) { this.LastError = string.Empty; if (File.Exists(filename)) { if (overwriteFile) { try { File.Delete(filename); } catch (Exception exc) { this.LastError = exc.Message; return false; } try { SQLiteConnection.CreateFile(filename); } catch (Exception exc) { this.LastError = exc.Message; return false; } return this.LoadFile(filename, password); } else { return this.LoadFile(filename, password); } } else { try { SQLiteConnection.CreateFile(filename); } catch (Exception exc) { this.LastError = exc.Message; return false; } return this.LoadFile(filename, password); } } protected string prepareQuery(string query, params string[] arguments) { string rv = query; if (string.IsNullOrWhiteSpace(rv)) { return string.Empty; } for (int i = 0; i < arguments.Length; i++) { rv = rv.Replace("[^" + (i + 1).ToString() + "]", escapeSQL(arguments[i])); } return rv; } protected string escapeSQL(string q) => q.Replace("'", "''").Trim(); protected string escapeValue(string text) => text.Replace("\"", "\\\"").Replace("\t", "\\t").Replace("\r", " \\r").Replace("\n", "\\n"); protected string encode64(string text) => Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text)); protected bool prepareAppSettings() { if (this.HasTable(appSettingsTableName)) { return true; } int rv = this.DoNonQuery("CREATE TABLE " + appSettingsTableName + " (cfg_name TEXT, cfg_value TEXT)"); return rv > 0; } protected virtual bool prepareDatabase() { return true; } } }