| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 | using System;using System.Collections.Generic;using System.Threading;using System.Threading.Tasks;using MediaBrowser.Model.Logging;using SQLitePCL.pretty;using System.Linq;using SQLitePCL;namespace Emby.Server.Implementations.Data{    public abstract class BaseSqliteRepository : IDisposable    {        protected string DbFilePath { get; set; }        protected ReaderWriterLockSlim WriteLock;        protected ILogger Logger { get; private set; }        protected BaseSqliteRepository(ILogger logger)        {            Logger = logger;            WriteLock = AllowLockRecursion ?              new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion) :              new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);        }        protected virtual bool AllowLockRecursion        {            get { return false; }        }        static BaseSqliteRepository()        {            SQLite3.EnableSharedCache = false;            int rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MEMSTATUS, 0);            //CheckOk(rc);        }        private static bool _versionLogged;        private string _defaultWal;        protected SQLiteDatabaseConnection CreateConnection(bool isReadOnly = false, Action<SQLiteDatabaseConnection> onConnect = null)        {            if (!_versionLogged)            {                _versionLogged = true;                Logger.Info("Sqlite version: " + SQLite3.Version);                Logger.Info("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions.ToArray()));            }            ConnectionFlags connectionFlags;            if (isReadOnly)            {                //Logger.Info("Opening read connection");            }            else            {                //Logger.Info("Opening write connection");            }            isReadOnly = false;            if (isReadOnly)            {                connectionFlags = ConnectionFlags.ReadOnly;                //connectionFlags = ConnectionFlags.Create;                //connectionFlags |= ConnectionFlags.ReadWrite;            }            else            {                connectionFlags = ConnectionFlags.Create;                connectionFlags |= ConnectionFlags.ReadWrite;            }            connectionFlags |= ConnectionFlags.SharedCached;            connectionFlags |= ConnectionFlags.NoMutex;            var db = SQLite3.Open(DbFilePath, connectionFlags, null);            if (string.IsNullOrWhiteSpace(_defaultWal))            {                _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();            }            var queries = new List<string>            {                "PRAGMA default_temp_store=memory",                "pragma temp_store = memory",                "PRAGMA journal_mode=WAL"                //"PRAGMA cache size=-10000"            };            //var cacheSize = CacheSize;            //if (cacheSize.HasValue)            //{            //}            ////foreach (var query in queries)            ////{            ////    db.Execute(query);            ////}            //Logger.Info("synchronous: " + db.Query("PRAGMA synchronous").SelectScalarString().First());            //Logger.Info("temp_store: " + db.Query("PRAGMA temp_store").SelectScalarString().First());            //if (!string.Equals(_defaultWal, "wal", StringComparison.OrdinalIgnoreCase) || onConnect != null)            {                using (WriteLock.Write())                {                    db.ExecuteAll(string.Join(";", queries.ToArray()));                    if (onConnect != null)                    {                        onConnect(db);                    }                }            }            return db;        }        protected virtual int? CacheSize        {            get            {                return null;            }        }        internal static void CheckOk(int rc)        {            string msg = "";            if (raw.SQLITE_OK != rc)            {                throw CreateException((ErrorCode)rc, msg);            }        }        internal static Exception CreateException(ErrorCode rc, string msg)        {            var exp = new Exception(msg);            return exp;        }        private bool _disposed;        protected void CheckDisposed()        {            if (_disposed)            {                throw new ObjectDisposedException(GetType().Name + " has been disposed and cannot be accessed.");            }        }        public void Dispose()        {            _disposed = true;            Dispose(true);            GC.SuppressFinalize(this);        }        private readonly object _disposeLock = new object();        /// <summary>        /// Releases unmanaged and - optionally - managed resources.        /// </summary>        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>        protected virtual void Dispose(bool dispose)        {            if (dispose)            {                try                {                    lock (_disposeLock)                    {                        using (WriteLock.Write())                        {                            CloseConnection();                        }                    }                }                catch (Exception ex)                {                    Logger.ErrorException("Error disposing database", ex);                }            }        }        protected virtual void CloseConnection()        {        }        protected List<string> GetColumnNames(IDatabaseConnection connection, string table)        {            var list = new List<string>();            foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))            {                if (row[1].SQLiteType != SQLiteType.Null)                {                    var name = row[1].ToString();                    list.Add(name);                }            }            return list;        }        protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)        {            if (existingColumnNames.Contains(columnName, StringComparer.OrdinalIgnoreCase))            {                return;            }            connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL");        }    }    public static class ReaderWriterLockSlimExtensions    {        private sealed class ReadLockToken : IDisposable        {            private ReaderWriterLockSlim _sync;            public ReadLockToken(ReaderWriterLockSlim sync)            {                _sync = sync;                sync.EnterReadLock();            }            public void Dispose()            {                if (_sync != null)                {                    _sync.ExitReadLock();                    _sync = null;                }            }        }        private sealed class WriteLockToken : IDisposable        {            private ReaderWriterLockSlim _sync;            public WriteLockToken(ReaderWriterLockSlim sync)            {                _sync = sync;                sync.EnterWriteLock();            }            public void Dispose()            {                if (_sync != null)                {                    _sync.ExitWriteLock();                    _sync = null;                }            }        }        public static IDisposable Read(this ReaderWriterLockSlim obj)        {            return new ReadLockToken(obj);        }        public static IDisposable Write(this ReaderWriterLockSlim obj)        {            return new WriteLockToken(obj);        }    }}
 |