using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Data.Sqlite; namespace SqliteDotNet { [Serializable] public class ColumNotFoundException : Exception { public ColumNotFoundException() { } public ColumNotFoundException(string message) : base(message) { } public ColumNotFoundException(string message, Exception inner) : base(message, inner) { } protected ColumNotFoundException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } public class Sqlite { public const int Error = 1; public const int Done = 101; public const int Ok = 0; public const int Row = 100; } public class SqliteGcBlob { private SqliteDataReader _currentReader; private int _ordinal; public SqliteGcBlob(SqliteDataReader currentReader, int v) { _currentReader = currentReader; _ordinal = v; } public byte[] Read() { if (_currentReader.IsDBNull(_ordinal)) { return null; } return _currentReader.GetFieldValue(_ordinal); } } public class SqliteDb { public SqliteDb() { InternalConnection = new Microsoft.Data.Sqlite.SqliteConnection(); } internal Microsoft.Data.Sqlite.SqliteConnection InternalConnection { get; } public void Close() { InternalConnection.Close(); this.FileName = null; } public string FileName { get; private set; } public string GetName() => FileName; public int Open(string unitTestVmPath) { InternalConnection.ConnectionString = "Data Source=" + unitTestVmPath; InternalConnection.Open(); if (InternalConnection.State == System.Data.ConnectionState.Open) { this.FileName = unitTestVmPath; return 0; } return 1; } public void GenerateDatabase(string dumpFileName, string dbPath) { string content = File.ReadAllText(dumpFileName); var db = new SqliteDb(); try { db.Open(dbPath); using (var cmd = db.InternalConnection.CreateCommand()) { cmd.CommandText = content; cmd.ExecuteNonQuery(); } } finally { db.Close(); } } } public class SqliteVm { private SqliteDb _db; private bool _noThrow; private Microsoft.Data.Sqlite.SqliteCommand _currentStmt; private Microsoft.Data.Sqlite.SqliteDataReader _currentReader; private Dictionary _nameIndexMap; public SqliteVm(SqliteDb db, bool noThrow) { _db = db; _noThrow = noThrow; _nameIndexMap = new Dictionary(); } public int NumCols() => _nameIndexMap.Count; public string ColumnName(int ordinal) => _currentReader.GetName(ordinal); public string ColumnType(string name) => _currentReader.GetDataTypeName(_nameIndexMap[name]); private string _lastCommand; private Exception _lastException; public int Execute(string sql) { try { _currentStmt = _db.InternalConnection.CreateCommand(); _currentStmt.CommandText = sql; _currentStmt.Prepare(); _lastCommand = _currentStmt.CommandText; _currentReader = _currentStmt.ExecuteReader(); //Compile name index map for the purposes of column binding below for (int i = 0; i < _currentReader.FieldCount; i++) { var name = _currentReader.GetName(i); _nameIndexMap[name] = i; } return NextRow(); } catch (Exception ex) { _lastException = ex; return Sqlite.Error; } } public string GetErrMsg() => _lastException.Message; public string GetQueryTail() => _lastCommand; public void SqlFinalize() { if (_currentReader != null) { _currentReader.Close(); _currentReader = null; } if (_currentStmt != null) { _currentStmt.Dispose(); _currentStmt = null; } } public int GetInt(string columnName) { if (_nameIndexMap.ContainsKey(columnName)) { return _currentReader.GetFieldValue(_nameIndexMap[columnName]); } else if (!_noThrow) { throw new ColumNotFoundException(columnName); } return default(int); } public double GetDouble(string columnName) { if (_nameIndexMap.ContainsKey(columnName)) { return _currentReader.GetFieldValue(_nameIndexMap[columnName]); } else if (!_noThrow) { throw new ColumNotFoundException(columnName); } return double.NaN; } public string GetString(string columnName) { if (_nameIndexMap.ContainsKey(columnName)) { var value = _currentReader[_nameIndexMap[columnName]]; switch(value) { case byte[] b: return Encoding.UTF8.GetString(b); default: return value.ToString(); } } else if (!_noThrow) { throw new ColumNotFoundException(columnName); } return string.Empty; } public int NextRow() { bool b = _currentReader.Read(); return b ? Sqlite.Row : -1; } public SqliteGcBlob GetBlob(string columnName) { SqliteGcBlob blob = null; if (_nameIndexMap.ContainsKey(columnName)) { blob = new SqliteGcBlob(_currentReader, _nameIndexMap[columnName]); } else if (!_noThrow) { throw new ColumNotFoundException(columnName); } return blob; } } }