kopia lustrzana https://github.com/dl2alf/AirScout
1123 wiersze
37 KiB
C#
1123 wiersze
37 KiB
C#
|
// based on the SQLiteHelper poject
|
|||
|
// Version 1.2
|
|||
|
// Date: 2014-03-27
|
|||
|
// http://sh.codeplex.com
|
|||
|
// Dedicated to Public Domain
|
|||
|
//
|
|||
|
// modified by DL2ALF 2016
|
|||
|
|
|||
|
using System.Collections.Generic;
|
|||
|
//
|
|||
|
// SqliteDatabase.cs
|
|||
|
//
|
|||
|
// SQLite3 support simultaneous for Windows/Linux (Mono) without code change/recompilation
|
|||
|
// Derived from Mono.Data.SqliteClient data access components for .Net
|
|||
|
//
|
|||
|
// Copyright (C) 2018 DL2ALF
|
|||
|
//
|
|||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|||
|
// a copy of this software and associated documentation files (the
|
|||
|
// "Software"), to deal in the Software without restriction, including
|
|||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|||
|
// permit persons to whom the Software is furnished to do so, subject to
|
|||
|
// the following conditions:
|
|||
|
//
|
|||
|
// The above copyright notice and this permission notice shall be
|
|||
|
// included in all copies or substantial portions of the Software.
|
|||
|
//
|
|||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
//using System.Data.Common;
|
|||
|
|
|||
|
using System.IO;
|
|||
|
using System.Text;
|
|||
|
using System;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Globalization;
|
|||
|
using System.Threading;
|
|||
|
using System.Reflection;
|
|||
|
using System.Data;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using System.Data.SQLite;
|
|||
|
using System.Linq;
|
|||
|
|
|||
|
namespace System.Data.SQLite
|
|||
|
{
|
|||
|
|
|||
|
public class SQLiteDatabase
|
|||
|
{
|
|||
|
// contains real filename on disk if InMemory database
|
|||
|
public string DiskFileName = "";
|
|||
|
|
|||
|
string dblocation;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// local database location
|
|||
|
/// ":memory:", if in-memory database
|
|||
|
/// </summary>
|
|||
|
public string DBLocation
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (isinmemory)
|
|||
|
return ":memory:";
|
|||
|
else
|
|||
|
return dblocation;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool isinmemory = false;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// indicates a in-memory database
|
|||
|
/// </summary>
|
|||
|
public bool IsInMemory
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return isinmemory;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// local database connection string
|
|||
|
/// </summary>
|
|||
|
public string DBConnectionString
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
SQLiteConnectionStringBuilder b = new SQLiteConnectionStringBuilder();
|
|||
|
b.Version = 3;
|
|||
|
b.PageSize = 4096;
|
|||
|
b.UseUTF16Encoding = true;
|
|||
|
b.DataSource = DBLocation;
|
|||
|
return b.ConnectionString;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// local database logical file size in MB
|
|||
|
/// </summary>
|
|||
|
public double DBSize
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (isinmemory)
|
|||
|
return 0;
|
|||
|
else
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
FileInfo fi = new FileInfo(dblocation);
|
|||
|
return fi.Length / (long)1024.0 / (long)1024.0;
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Holds the database status
|
|||
|
/// Is set to UNDEFINED by default
|
|||
|
/// // Can be used by external procedure to manage database update process
|
|||
|
/// </summary>
|
|||
|
public DATABASESTATUS Status { get; set; }
|
|||
|
|
|||
|
public SQLiteConnection DBConnection = null;
|
|||
|
public SQLiteCommand DBCommand = null;
|
|||
|
|
|||
|
public SQLiteDatabase() : this(null) { }
|
|||
|
public SQLiteDatabase(string filename)
|
|||
|
{
|
|||
|
if (String.IsNullOrEmpty(filename))
|
|||
|
{
|
|||
|
// create a default database file name
|
|||
|
filename = "db.db3";
|
|||
|
}
|
|||
|
// handle special filenames
|
|||
|
if (filename == ":memory:")
|
|||
|
{
|
|||
|
isinmemory = true;
|
|||
|
dblocation = filename;
|
|||
|
return;
|
|||
|
}
|
|||
|
// check if filename contains path
|
|||
|
string directory = Path.GetDirectoryName(filename);
|
|||
|
if (String.IsNullOrEmpty(directory))
|
|||
|
{
|
|||
|
// create default database directory name
|
|||
|
// collect entry assembly info
|
|||
|
Assembly ass = Assembly.GetCallingAssembly();
|
|||
|
string name = ass.GetName().Name;
|
|||
|
string product = "";
|
|||
|
string company = "";
|
|||
|
object[] attribs;
|
|||
|
attribs = ass.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
|
|||
|
if (attribs.Length > 0)
|
|||
|
{
|
|||
|
company = ((AssemblyCompanyAttribute)attribs[0]).Company;
|
|||
|
}
|
|||
|
attribs = ass.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
|
|||
|
if (attribs.Length > 0)
|
|||
|
{
|
|||
|
product = ((AssemblyProductAttribute)attribs[0]).Product;
|
|||
|
}
|
|||
|
// create database path
|
|||
|
string dir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
|||
|
if (!String.IsNullOrEmpty(company))
|
|||
|
dir = Path.Combine(dir, company);
|
|||
|
if (!String.IsNullOrEmpty(product))
|
|||
|
dir = Path.Combine(dir, product);
|
|||
|
directory = Path.Combine(dir, "Database");
|
|||
|
filename = Path.Combine(directory, filename);
|
|||
|
}
|
|||
|
dblocation = filename;
|
|||
|
// create an empty database if not file found
|
|||
|
if (!File.Exists(dblocation))
|
|||
|
{
|
|||
|
// check for existing directory first
|
|||
|
string dir = Path.GetDirectoryName(dblocation);
|
|||
|
if (!Directory.Exists(dir))
|
|||
|
{
|
|||
|
Directory.CreateDirectory(dir);
|
|||
|
}
|
|||
|
}
|
|||
|
// set status to UNDEFINED after initialization
|
|||
|
Status = DATABASESTATUS.UNDEFINED;
|
|||
|
}
|
|||
|
|
|||
|
public void Open()
|
|||
|
{
|
|||
|
if (DBConnection != null)
|
|||
|
throw new InvalidOperationException("Cannot open database twice.");
|
|||
|
// check if database is initailly created
|
|||
|
DBConnection = new SQLiteConnection(DBConnectionString);
|
|||
|
DBCommand = new SQLiteCommand();
|
|||
|
DBCommand.Connection = DBConnection;
|
|||
|
Console.WriteLine("SQLiteDatabase.Open: connectionstring=" + DBConnection.ConnectionString);
|
|||
|
DBConnection.Open();
|
|||
|
}
|
|||
|
|
|||
|
public void Close()
|
|||
|
{
|
|||
|
if (DBConnection == null)
|
|||
|
throw new InvalidOperationException("Database is closed already.");
|
|||
|
DBConnection.Close();
|
|||
|
DBConnection = null;
|
|||
|
DBCommand = null;
|
|||
|
}
|
|||
|
|
|||
|
#region DB Info
|
|||
|
|
|||
|
public DataTable GetTableStatus()
|
|||
|
{
|
|||
|
return Select("SELECT * FROM sqlite_master;");
|
|||
|
}
|
|||
|
|
|||
|
public DataTable GetTableList()
|
|||
|
{
|
|||
|
DataTable dt = GetTableStatus();
|
|||
|
DataTable dt2 = new DataTable();
|
|||
|
dt2.Columns.Add("Tables");
|
|||
|
for (int i = 0; i < dt.Rows.Count; i++)
|
|||
|
{
|
|||
|
string t = dt.Rows[i]["name"] + "";
|
|||
|
if (t != "sqlite_sequence")
|
|||
|
dt2.Rows.Add(t);
|
|||
|
}
|
|||
|
return dt2;
|
|||
|
}
|
|||
|
|
|||
|
public bool TableExists (string tablename)
|
|||
|
{
|
|||
|
DataTable dt = Select("SELECT * FROM sqlite_master WHERE NAME = '" + tablename + "'");
|
|||
|
return ((dt != null) && (dt.Rows.Count > 0));
|
|||
|
}
|
|||
|
|
|||
|
public int TableRowCount(string tablename)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
DataTable dt = Select("SELECT Count(*) FROM " + tablename);
|
|||
|
return System.Convert.ToInt32(dt.Rows[0][0]);
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
public DataTable GetColumnStatus(string tableName)
|
|||
|
{
|
|||
|
return Select(string.Format("PRAGMA table_info(`{0}`);", tableName));
|
|||
|
}
|
|||
|
|
|||
|
public DataTable ShowDatabase()
|
|||
|
{
|
|||
|
return Select("PRAGMA database_list;");
|
|||
|
}
|
|||
|
|
|||
|
public int GetUserVersion()
|
|||
|
{
|
|||
|
DataTable dt = Select("PRAGMA user_version;");
|
|||
|
if ((dt != null) && (dt.Rows.Count > 0))
|
|||
|
return System.Convert.ToInt32(dt.Rows[0][0]);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
public void SetUserVerion(int version)
|
|||
|
{
|
|||
|
Execute("PRAGMA user_version = " + version.ToString());
|
|||
|
}
|
|||
|
|
|||
|
public int GetSchemaVersion()
|
|||
|
{
|
|||
|
DataTable dt = Select("PRAGMA schema_version;");
|
|||
|
if ((dt != null) && (dt.Rows.Count > 0))
|
|||
|
return System.Convert.ToInt32(dt.Rows[0][0]);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
public void SetSchemaVerion(int version)
|
|||
|
{
|
|||
|
Execute("PRAGMA schema_version = " + version.ToString());
|
|||
|
}
|
|||
|
|
|||
|
public string GetTextEncoding()
|
|||
|
{
|
|||
|
DataTable dt = Select("PRAGMA encoding;");
|
|||
|
if ((dt != null) && (dt.Rows.Count > 0))
|
|||
|
return (dt.Rows[0][0].ToString());
|
|||
|
else return String.Empty;
|
|||
|
}
|
|||
|
|
|||
|
public void SetTextEncoding(string encoding)
|
|||
|
{
|
|||
|
Execute("PRAGMA encoding = '" + encoding + "'");
|
|||
|
}
|
|||
|
|
|||
|
public AUTOVACUUMMODE GetAutoVacuum()
|
|||
|
{
|
|||
|
DataTable dt = Select("PRAGMA auto_vacuum;");
|
|||
|
if ((dt != null) && (dt.Rows.Count > 0))
|
|||
|
return (AUTOVACUUMMODE)System.Convert.ToInt32(dt.Rows[0][0]);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
public void SetAutoVacuum (AUTOVACUUMMODE mode)
|
|||
|
{
|
|||
|
// changes the auto vacuum pragma
|
|||
|
// command requires a VACUUM of the database to change layout
|
|||
|
// CAUTION: this requires time and disk space!
|
|||
|
try
|
|||
|
{
|
|||
|
Execute("PRAGMA auto_vacuum = " + ((int)mode).ToString() + "; VACUUM");
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Console.WriteLine(ex.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Query
|
|||
|
|
|||
|
public void BeginTransaction()
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = "BEGIN TRANSACTION;";
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Commit()
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = "COMMIT;";
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Rollback()
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = "ROLLBACK";
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// simple sql select command returns a DataTable
|
|||
|
public DataTable Select (string sql)
|
|||
|
{
|
|||
|
DataTable dt = new DataTable();
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sql;
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
SQLiteDataAdapter da = new SQLiteDataAdapter(DBCommand);
|
|||
|
da.Fill(dt);
|
|||
|
}
|
|||
|
// Linux/Mono hack --> cut column names to its right length
|
|||
|
for (int i = 0; i < dt.Columns.Count; i++)
|
|||
|
{
|
|||
|
int j = 0;
|
|||
|
while ((j < dt.Columns[i].ColumnName.Length) && (Char.IsLetter(dt.Columns[i].ColumnName[j]) || Char.IsDigit(dt.Columns[i].ColumnName[j])))
|
|||
|
j++;
|
|||
|
dt.Columns[i].ColumnName = dt.Columns[i].ColumnName.Substring(0, j);
|
|||
|
Console.Write("'" + dt.Columns[i].ColumnName + "[" + dt.Columns[i].ColumnName.Length + "]', ");
|
|||
|
}
|
|||
|
Console.WriteLine();
|
|||
|
return dt;
|
|||
|
}
|
|||
|
|
|||
|
public DataTable Select (SQLiteCommand cmd)
|
|||
|
{
|
|||
|
DataTable dt = new DataTable();
|
|||
|
lock (cmd)
|
|||
|
{
|
|||
|
SQLiteDataAdapter da = new SQLiteDataAdapter(cmd);
|
|||
|
da.Fill(dt);
|
|||
|
}
|
|||
|
return dt;
|
|||
|
}
|
|||
|
|
|||
|
public void Execute(SQLiteCommand cmd)
|
|||
|
{
|
|||
|
cmd.ExecuteNonQuery();
|
|||
|
}
|
|||
|
|
|||
|
public void Execute(string sql)
|
|||
|
{
|
|||
|
Execute(sql, new List<SQLiteParameter>());
|
|||
|
}
|
|||
|
|
|||
|
public void Execute(string sql, Dictionary<string, object> dicParameters = null)
|
|||
|
{
|
|||
|
List<SQLiteParameter> lst = GetParametersList(dicParameters);
|
|||
|
Execute(sql, lst);
|
|||
|
}
|
|||
|
|
|||
|
public void Execute(string sql, IEnumerable<SQLiteParameter> parameters = null)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sql;
|
|||
|
if (parameters != null)
|
|||
|
{
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
foreach (var param in parameters)
|
|||
|
{
|
|||
|
DBCommand.Parameters.Add(param);
|
|||
|
}
|
|||
|
}
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int ExecuteNonQuery (SQLiteCommand cmd)
|
|||
|
{
|
|||
|
return cmd.ExecuteNonQuery();
|
|||
|
}
|
|||
|
|
|||
|
public object ExecuteScalar(string sql)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sql;
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
return DBCommand.ExecuteScalar();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public object ExecuteScalar (SQLiteCommand cmd)
|
|||
|
{
|
|||
|
List<SQLiteParameter> lst = new List<SQLiteParameter>();
|
|||
|
foreach (SQLiteParameter par in cmd.Parameters)
|
|||
|
lst.Add(par);
|
|||
|
return ExecuteScalar(cmd.CommandText, lst);
|
|||
|
}
|
|||
|
|
|||
|
public object ExecuteScalar(string sql, Dictionary<string, object> dicParameters = null)
|
|||
|
{
|
|||
|
List<SQLiteParameter> lst = GetParametersList(dicParameters);
|
|||
|
return ExecuteScalar(sql, lst);
|
|||
|
}
|
|||
|
|
|||
|
public object ExecuteScalar(string sql, IEnumerable<SQLiteParameter> parameters = null)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sql;
|
|||
|
if (parameters != null)
|
|||
|
{
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
foreach (var parameter in parameters)
|
|||
|
{
|
|||
|
DBCommand.Parameters.Add(parameter);
|
|||
|
}
|
|||
|
}
|
|||
|
return DBCommand.ExecuteScalar();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public dataType ExecuteScalar<dataType>(string sql, Dictionary<string, object> dicParameters = null)
|
|||
|
{
|
|||
|
List<SQLiteParameter> lst = null;
|
|||
|
if (dicParameters != null)
|
|||
|
{
|
|||
|
lst = new List<SQLiteParameter>();
|
|||
|
foreach (KeyValuePair<string, object> kv in dicParameters)
|
|||
|
{
|
|||
|
lst.Add(new SQLiteParameter(kv.Key, kv.Value));
|
|||
|
}
|
|||
|
}
|
|||
|
return ExecuteScalar<dataType>(sql, lst);
|
|||
|
}
|
|||
|
|
|||
|
public dataType ExecuteScalar<dataType>(string sql, IEnumerable<SQLiteParameter> parameters = null)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sql;
|
|||
|
if (parameters != null)
|
|||
|
{
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
foreach (var parameter in parameters)
|
|||
|
{
|
|||
|
DBCommand.Parameters.Add(parameter);
|
|||
|
}
|
|||
|
}
|
|||
|
return (dataType)Convert.ChangeType(DBCommand.ExecuteScalar(), typeof(dataType));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public dataType ExecuteScalar<dataType>(string sql)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sql;
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
return (dataType)Convert.ChangeType(DBCommand.ExecuteScalar(), typeof(dataType));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private List<SQLiteParameter> GetParametersList(Dictionary<string, object> dicParameters)
|
|||
|
{
|
|||
|
List<SQLiteParameter> lst = new List<SQLiteParameter>();
|
|||
|
if (dicParameters != null)
|
|||
|
{
|
|||
|
foreach (KeyValuePair<string, object> kv in dicParameters)
|
|||
|
{
|
|||
|
lst.Add(new SQLiteParameter(kv.Key, kv.Value));
|
|||
|
}
|
|||
|
}
|
|||
|
return lst;
|
|||
|
}
|
|||
|
|
|||
|
public string Escape(string data)
|
|||
|
{
|
|||
|
data = data.Replace("'", "''");
|
|||
|
data = data.Replace("\\", "\\\\");
|
|||
|
return data;
|
|||
|
}
|
|||
|
|
|||
|
public void InsertDataRow(string tableName, DataRow row)
|
|||
|
{
|
|||
|
Dictionary<string, object> dict = new Dictionary<string, object>();
|
|||
|
foreach (DataColumn col in row.Table.Columns)
|
|||
|
{
|
|||
|
dict.Add(col.ColumnName, row[col]);
|
|||
|
}
|
|||
|
Insert(tableName, dict);
|
|||
|
}
|
|||
|
|
|||
|
public void InsertOrReplaceDataRow(string tableName, DataRow row)
|
|||
|
{
|
|||
|
Dictionary<string, object> dict = new Dictionary<string, object>();
|
|||
|
for (int i = 0; i < row.ItemArray.Length; i++)
|
|||
|
{
|
|||
|
dict.Add(row.Table.Columns[i].ColumnName, row[i]);
|
|||
|
}
|
|||
|
InsertOrReplace(tableName, dict);
|
|||
|
}
|
|||
|
|
|||
|
public void Insert(string tableName, Dictionary<string, object> dic)
|
|||
|
{
|
|||
|
StringBuilder sbCol = new System.Text.StringBuilder();
|
|||
|
StringBuilder sbVal = new System.Text.StringBuilder();
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv in dic)
|
|||
|
{
|
|||
|
if (sbCol.Length == 0)
|
|||
|
{
|
|||
|
sbCol.Append("INSERT INTO ");
|
|||
|
sbCol.Append(tableName);
|
|||
|
sbCol.Append("(");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sbCol.Append(",");
|
|||
|
}
|
|||
|
|
|||
|
sbCol.Append("`");
|
|||
|
sbCol.Append(kv.Key);
|
|||
|
sbCol.Append("`");
|
|||
|
|
|||
|
if (sbVal.Length == 0)
|
|||
|
{
|
|||
|
sbVal.Append(" VALUES(");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sbVal.Append(", ");
|
|||
|
}
|
|||
|
|
|||
|
sbVal.Append("@v");
|
|||
|
sbVal.Append(kv.Key);
|
|||
|
}
|
|||
|
|
|||
|
sbCol.Append(") ");
|
|||
|
sbVal.Append(");");
|
|||
|
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sbCol.ToString() + sbVal.ToString();
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
foreach (KeyValuePair<string, object> kv in dic)
|
|||
|
{
|
|||
|
SQLiteParameter par = new SQLiteParameter("@v" + kv.Key, kv.Value);
|
|||
|
DBCommand.Parameters.Add(par);
|
|||
|
}
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void InsertOrReplace(string tableName, Dictionary<string, object> dic)
|
|||
|
{
|
|||
|
StringBuilder sbCol = new System.Text.StringBuilder();
|
|||
|
StringBuilder sbVal = new System.Text.StringBuilder();
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv in dic)
|
|||
|
{
|
|||
|
if (sbCol.Length == 0)
|
|||
|
{
|
|||
|
sbCol.Append("INSERT OR REPLACE INTO ");
|
|||
|
sbCol.Append(tableName);
|
|||
|
sbCol.Append("(");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sbCol.Append(",");
|
|||
|
}
|
|||
|
|
|||
|
sbCol.Append("`");
|
|||
|
sbCol.Append(kv.Key);
|
|||
|
sbCol.Append("`");
|
|||
|
|
|||
|
if (sbVal.Length == 0)
|
|||
|
{
|
|||
|
sbVal.Append(" VALUES(");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sbVal.Append(", ");
|
|||
|
}
|
|||
|
|
|||
|
sbVal.Append("@v");
|
|||
|
sbVal.Append(kv.Key);
|
|||
|
}
|
|||
|
|
|||
|
sbCol.Append(") ");
|
|||
|
sbVal.Append(");");
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sbCol.ToString() + sbVal.ToString();
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
foreach (KeyValuePair<string, object> kv in dic)
|
|||
|
{
|
|||
|
SQLiteParameter par = new SQLiteParameter("@v" + kv.Key, kv.Value);
|
|||
|
DBCommand.Parameters.Add(par);
|
|||
|
}
|
|||
|
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Update(string tableName, Dictionary<string, object> dicData, string colCond, object varCond)
|
|||
|
{
|
|||
|
Dictionary<string, object> dic = new Dictionary<string, object>();
|
|||
|
dic[colCond] = varCond;
|
|||
|
Update(tableName, dicData, dic);
|
|||
|
}
|
|||
|
|
|||
|
public void Update(string tableName, Dictionary<string, object> dicData, Dictionary<string, object> dicCond)
|
|||
|
{
|
|||
|
if (dicData.Count == 0)
|
|||
|
throw new Exception("dicData is empty.");
|
|||
|
|
|||
|
StringBuilder sbData = new System.Text.StringBuilder();
|
|||
|
|
|||
|
Dictionary<string, object> _dicTypeSource = new Dictionary<string, object>();
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv1 in dicData)
|
|||
|
{
|
|||
|
_dicTypeSource[kv1.Key] = null;
|
|||
|
}
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv2 in dicCond)
|
|||
|
{
|
|||
|
if (!_dicTypeSource.ContainsKey(kv2.Key))
|
|||
|
_dicTypeSource[kv2.Key] = null;
|
|||
|
}
|
|||
|
|
|||
|
sbData.Append("UPDATE `");
|
|||
|
sbData.Append(tableName);
|
|||
|
sbData.Append("` SET ");
|
|||
|
|
|||
|
bool firstRecord = true;
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv in dicData)
|
|||
|
{
|
|||
|
if (firstRecord)
|
|||
|
firstRecord = false;
|
|||
|
else
|
|||
|
sbData.Append(",");
|
|||
|
|
|||
|
sbData.Append("`");
|
|||
|
sbData.Append(kv.Key);
|
|||
|
sbData.Append("` = ");
|
|||
|
|
|||
|
sbData.Append("@v");
|
|||
|
sbData.Append(kv.Key);
|
|||
|
}
|
|||
|
|
|||
|
sbData.Append(" WHERE ");
|
|||
|
|
|||
|
firstRecord = true;
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv in dicCond)
|
|||
|
{
|
|||
|
if (firstRecord)
|
|||
|
firstRecord = false;
|
|||
|
else
|
|||
|
{
|
|||
|
sbData.Append(" AND ");
|
|||
|
}
|
|||
|
|
|||
|
sbData.Append("`");
|
|||
|
sbData.Append(kv.Key);
|
|||
|
sbData.Append("` = ");
|
|||
|
|
|||
|
sbData.Append("@c");
|
|||
|
sbData.Append(kv.Key);
|
|||
|
}
|
|||
|
|
|||
|
sbData.Append(";");
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sbData.ToString();
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
foreach (KeyValuePair<string, object> kv in dicData)
|
|||
|
{
|
|||
|
SQLiteParameter par = new SQLiteParameter("@v" + kv.Key, kv.Value);
|
|||
|
DBCommand.Parameters.Add(par);
|
|||
|
}
|
|||
|
|
|||
|
foreach (KeyValuePair<string, object> kv in dicCond)
|
|||
|
{
|
|||
|
SQLiteParameter par = new SQLiteParameter("@v" + kv.Key, kv.Value);
|
|||
|
DBCommand.Parameters.Add(par);
|
|||
|
}
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public long LastInsertRowId()
|
|||
|
{
|
|||
|
return ExecuteScalar<long>("SELECT last_insert_rowid();");
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Utilities
|
|||
|
|
|||
|
public void CreateTable(DataTable dt)
|
|||
|
{
|
|||
|
if (String.IsNullOrEmpty(dt.TableName))
|
|||
|
throw new InvalidOperationException("Table name cannot be empty.");
|
|||
|
StringBuilder sb = new System.Text.StringBuilder();
|
|||
|
sb.Append("CREATE TABLE IF NOT EXISTS `");
|
|||
|
sb.Append(dt.TableName);
|
|||
|
sb.AppendLine("`(");
|
|||
|
|
|||
|
bool firstRecord = true;
|
|||
|
|
|||
|
foreach (DataColumn col in dt.Columns)
|
|||
|
{
|
|||
|
if (col.ColumnName.Trim().Length == 0)
|
|||
|
{
|
|||
|
throw new Exception("Column name cannot be blank.");
|
|||
|
}
|
|||
|
|
|||
|
if (firstRecord)
|
|||
|
firstRecord = false;
|
|||
|
else
|
|||
|
sb.AppendLine(",");
|
|||
|
|
|||
|
sb.Append(col.ColumnName);
|
|||
|
sb.Append(" ");
|
|||
|
|
|||
|
if (col.AutoIncrement)
|
|||
|
{
|
|||
|
|
|||
|
sb.Append("INTEGER PRIMARY KEY AUTOINCREMENT");
|
|||
|
continue;
|
|||
|
}
|
|||
|
if ((col.DataType.IsPrimitive || col.DataType.IsValueType) || (col.DataType == typeof(string)))
|
|||
|
{
|
|||
|
// append data type --> replace STRING with TEXT and DOUBLE with REAL
|
|||
|
sb.Append(col.DataType.Name.ToUpper().Replace("STRING", "TEXT").Replace("DOUBLE", "REAL"));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// complex type --> append as BLOB
|
|||
|
sb.Append("BLOB");
|
|||
|
}
|
|||
|
if (col.Unique)
|
|||
|
sb.Append(" UNIQUE");
|
|||
|
if (!col.AllowDBNull)
|
|||
|
sb.Append(" NOT NULL");
|
|||
|
if (col.DefaultValue.GetType() != typeof(DBNull))
|
|||
|
{
|
|||
|
sb.Append(" DEFAULT ");
|
|||
|
|
|||
|
if (col.DataType == typeof(string) || col.DataType == typeof(DateTime))
|
|||
|
{
|
|||
|
sb.Append("'");
|
|||
|
sb.Append(col.DefaultValue.ToString());
|
|||
|
sb.Append("'");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sb.Append(col.DefaultValue.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//append primary keys if any
|
|||
|
if ((dt.PrimaryKey != null) && (dt.PrimaryKey.Length > 0))
|
|||
|
{
|
|||
|
sb.Append(", PRIMARY KEY (");
|
|||
|
firstRecord = true;
|
|||
|
foreach (DataColumn col in dt.PrimaryKey)
|
|||
|
{
|
|||
|
if (col.ColumnName.Trim().Length == 0)
|
|||
|
{
|
|||
|
throw new Exception("Column name cannot be blank.");
|
|||
|
}
|
|||
|
|
|||
|
if (firstRecord)
|
|||
|
firstRecord = false;
|
|||
|
else
|
|||
|
sb.AppendLine(",");
|
|||
|
|
|||
|
sb.Append(col.ColumnName);
|
|||
|
sb.Append(" ");
|
|||
|
}
|
|||
|
sb.Append(")");
|
|||
|
}
|
|||
|
sb.AppendLine(");");
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sb.ToString();
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void ClearTable(string tablename)
|
|||
|
{
|
|||
|
BeginTransaction();
|
|||
|
Execute("DELETE FROM " + tablename);
|
|||
|
Commit();
|
|||
|
}
|
|||
|
|
|||
|
public void InsertOrReplaceTable(DataTable dt)
|
|||
|
{
|
|||
|
// inserts all records from a data table or updates if exists
|
|||
|
// does not remove data rows if not in source table
|
|||
|
// will work with a unique key only
|
|||
|
if (String.IsNullOrEmpty(dt.TableName))
|
|||
|
throw new InvalidOperationException("Table name cannot be empty.");
|
|||
|
// check for primary key in database table
|
|||
|
DataTable info = GetColumnStatus(dt.TableName);
|
|||
|
bool hasprimarykey = false;
|
|||
|
foreach (DataRow row in info.Rows)
|
|||
|
{
|
|||
|
if (System.Convert.ToInt32(row["pk"]) > 0)
|
|||
|
{
|
|||
|
hasprimarykey = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!hasprimarykey)
|
|||
|
if ((dt.PrimaryKey == null) || (dt.PrimaryKey.Length == 0))
|
|||
|
throw new InvalidOperationException("Data table must have a primary key.");
|
|||
|
BeginTransaction();
|
|||
|
foreach (DataRow row in dt.Rows)
|
|||
|
InsertOrReplaceDataRow(dt.TableName, row);
|
|||
|
Commit();
|
|||
|
}
|
|||
|
|
|||
|
public void RenameTable(string tableFrom, string tableTo)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = string.Format("ALTER TABLE `{0}` RENAME TO `{1}`;", tableFrom, tableTo);
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void CopyAllData(string tableFrom, string tableTo)
|
|||
|
{
|
|||
|
// get at least one row from tables
|
|||
|
DataTable src = Select(string.Format("SELECT * FROM `{0}` LIMIT 1;", tableFrom));
|
|||
|
// empty source table??
|
|||
|
if (src.Rows.Count == 0)
|
|||
|
return;
|
|||
|
DataTable dest;
|
|||
|
// empty dest table??
|
|||
|
if (TableRowCount(tableTo) == 0)
|
|||
|
{
|
|||
|
// add just one dummy row
|
|||
|
Execute("INSERT INTO " + tableTo + " DEFAULT VALUES");
|
|||
|
// get the structure in a DataTable
|
|||
|
dest = Select(string.Format("SELECT * FROM `{0}` LIMIT 1;", tableTo));
|
|||
|
// remove the dummy row
|
|||
|
Execute("DELETE * FROM " + tableTo);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dest = Select(string.Format("SELECT * FROM `{0}` LIMIT 1;", tableTo));
|
|||
|
}
|
|||
|
Dictionary<string, bool> dic = new Dictionary<string, bool>();
|
|||
|
|
|||
|
foreach (DataColumn dc in src.Columns)
|
|||
|
{
|
|||
|
if (dest.Columns.Contains(dc.ColumnName))
|
|||
|
{
|
|||
|
if (!dic.ContainsKey(dc.ColumnName))
|
|||
|
{
|
|||
|
dic[dc.ColumnName] = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
foreach (DataColumn dc in dest.Columns)
|
|||
|
{
|
|||
|
if (src.Columns.Contains(dc.ColumnName))
|
|||
|
{
|
|||
|
if (!dic.ContainsKey(dc.ColumnName))
|
|||
|
{
|
|||
|
dic[dc.ColumnName] = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
StringBuilder sb = new System.Text.StringBuilder();
|
|||
|
|
|||
|
foreach (KeyValuePair<string, bool> kv in dic)
|
|||
|
{
|
|||
|
if (sb.Length > 0)
|
|||
|
sb.Append(",");
|
|||
|
|
|||
|
sb.Append("`");
|
|||
|
sb.Append(kv.Key);
|
|||
|
sb.Append("`");
|
|||
|
}
|
|||
|
|
|||
|
StringBuilder sb2 = new System.Text.StringBuilder();
|
|||
|
sb2.Append("INSERT INTO `");
|
|||
|
sb2.Append(tableTo);
|
|||
|
sb2.Append("`(");
|
|||
|
sb2.Append(sb.ToString());
|
|||
|
sb2.Append(") SELECT ");
|
|||
|
sb2.Append(sb.ToString());
|
|||
|
sb2.Append(" FROM `");
|
|||
|
sb2.Append(tableFrom);
|
|||
|
sb2.Append("`;");
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = sb2.ToString();
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void DropTable(string table)
|
|||
|
{
|
|||
|
lock (DBCommand)
|
|||
|
{
|
|||
|
DBCommand.CommandText = string.Format("DROP TABLE IF EXISTS `{0}`", table);
|
|||
|
DBCommand.Parameters.Clear();
|
|||
|
DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void UpdateTableStructure(string targetTable, DataTable newStructure)
|
|||
|
{
|
|||
|
newStructure.TableName = targetTable + "_temp";
|
|||
|
|
|||
|
CreateTable(newStructure);
|
|||
|
|
|||
|
CopyAllData(targetTable, newStructure.TableName);
|
|||
|
|
|||
|
DropTable(targetTable);
|
|||
|
|
|||
|
RenameTable(newStructure.TableName, targetTable);
|
|||
|
}
|
|||
|
|
|||
|
public void AttachDatabase(string database, string alias)
|
|||
|
{
|
|||
|
Execute(string.Format("ATTACH '{0}' AS {1};", database, alias));
|
|||
|
}
|
|||
|
|
|||
|
public void DetachDatabase(string alias)
|
|||
|
{
|
|||
|
Execute(string.Format("DETACH {0};", alias));
|
|||
|
}
|
|||
|
|
|||
|
public void BackupDatabase(string filename)
|
|||
|
{
|
|||
|
//backups a database to file
|
|||
|
// destination database must be open
|
|||
|
if (DBConnection == null)
|
|||
|
throw new InvalidOperationException("Database must be open for backup.");
|
|||
|
try
|
|||
|
{
|
|||
|
// return on empty filename
|
|||
|
if (String.IsNullOrEmpty(filename))
|
|||
|
return;
|
|||
|
// check for existing directory first --> create on if not
|
|||
|
string dir = Path.GetDirectoryName(filename);
|
|||
|
if (!Directory.Exists(dir))
|
|||
|
{
|
|||
|
Directory.CreateDirectory(dir);
|
|||
|
}
|
|||
|
// simply delete destination db if already exists
|
|||
|
if (File.Exists(filename))
|
|||
|
File.Delete(filename);
|
|||
|
// get sql for creating structures
|
|||
|
string sql = "select sql from sqlite_master where name not like 'sqlite_%'";
|
|||
|
DataTable structure = this.Select(sql);
|
|||
|
// get user tables
|
|||
|
sql = "select name from sqlite_master where type = 'table' and name not like 'sqlite_%'";
|
|||
|
DataTable usertables = this.Select(sql);
|
|||
|
// crate identically structures in backup db
|
|||
|
SQLiteDatabase backupdb = new SQLiteDatabase(filename);
|
|||
|
backupdb.Open();
|
|||
|
foreach (DataRow row in structure.Rows)
|
|||
|
{
|
|||
|
backupdb.DBCommand.CommandText = row[0].ToString();
|
|||
|
backupdb.DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
// close the backup db
|
|||
|
backupdb.Close();
|
|||
|
// attach backup db to source db
|
|||
|
this.AttachDatabase(filename, "dest");
|
|||
|
this.BeginTransaction();
|
|||
|
// copy all user tables
|
|||
|
foreach (DataRow row in usertables.Rows)
|
|||
|
{
|
|||
|
this.DBCommand.CommandText = "insert into dest." + row[0].ToString() + " select * from main." + row[0].ToString();
|
|||
|
this.DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
this.Commit();
|
|||
|
this.DetachDatabase("dest");
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Console.WriteLine(ex.Message);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static SQLiteDatabase CreateInMemoryDB(string filename)
|
|||
|
{
|
|||
|
// returns a copy of a database in memory
|
|||
|
// create an empty memory db first
|
|||
|
SQLiteDatabase memdb = new SQLiteDatabase(":memory:");
|
|||
|
try
|
|||
|
{
|
|||
|
// open an existing on disk db
|
|||
|
SQLiteDatabase ondiskdb = new SQLiteDatabase(filename);
|
|||
|
ondiskdb.Open();
|
|||
|
// get user & schema version
|
|||
|
int user_version = ondiskdb.GetUserVersion();
|
|||
|
int schema_version = ondiskdb.GetSchemaVersion();
|
|||
|
// get text encoding
|
|||
|
string text_encoding = ondiskdb.GetTextEncoding();
|
|||
|
// get sql for creating structures
|
|||
|
string sql = "select sql from sqlite_master where name not like 'sqlite_%'";
|
|||
|
DataTable structure = ondiskdb.Select(sql);
|
|||
|
// get user tables
|
|||
|
sql = "select name from sqlite_master where type = 'table' and name not like 'sqlite_%'";
|
|||
|
DataTable usertables = ondiskdb.Select(sql);
|
|||
|
// crate identically structures in memmory db
|
|||
|
memdb.Open();
|
|||
|
foreach (DataRow row in structure.Rows)
|
|||
|
{
|
|||
|
memdb.DBCommand.CommandText = row[0].ToString();
|
|||
|
memdb.DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
// set user & schema version
|
|||
|
memdb.SetUserVerion(user_version);
|
|||
|
memdb.SetSchemaVerion(schema_version);
|
|||
|
// set text encoding
|
|||
|
memdb.SetTextEncoding(text_encoding);
|
|||
|
user_version = memdb.GetUserVersion();
|
|||
|
text_encoding = memdb.GetTextEncoding();
|
|||
|
// close on disk db
|
|||
|
ondiskdb.Close();
|
|||
|
// attach on disk db to memory db
|
|||
|
memdb.AttachDatabase(filename, "ondisk");
|
|||
|
memdb.BeginTransaction();
|
|||
|
foreach (DataRow row in usertables.Rows)
|
|||
|
{
|
|||
|
memdb.DBCommand.CommandText = "insert into main." + row[0].ToString() + " select * from ondisk." + row[0].ToString();
|
|||
|
memdb.DBCommand.ExecuteNonQuery();
|
|||
|
}
|
|||
|
memdb.Commit();
|
|||
|
memdb.DetachDatabase("ondisk");
|
|||
|
memdb.DiskFileName = filename;
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Console.WriteLine(ex.Message);
|
|||
|
}
|
|||
|
return memdb;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
}
|
|||
|
}
|