BaseSqliteRepository.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using MediaBrowser.Model.Logging;
  6. using SQLitePCL.pretty;
  7. using System.Linq;
  8. using SQLitePCL;
  9. namespace Emby.Server.Implementations.Data
  10. {
  11. public abstract class BaseSqliteRepository : IDisposable
  12. {
  13. protected string DbFilePath { get; set; }
  14. protected ReaderWriterLockSlim WriteLock;
  15. protected ILogger Logger { get; private set; }
  16. protected BaseSqliteRepository(ILogger logger)
  17. {
  18. Logger = logger;
  19. WriteLock = AllowLockRecursion ?
  20. new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion) :
  21. new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
  22. }
  23. protected virtual bool AllowLockRecursion
  24. {
  25. get { return false; }
  26. }
  27. protected virtual bool EnableConnectionPooling
  28. {
  29. get { return true; }
  30. }
  31. static BaseSqliteRepository()
  32. {
  33. SQLite3.EnableSharedCache = false;
  34. int rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MEMSTATUS, 0);
  35. //CheckOk(rc);
  36. }
  37. private static bool _versionLogged;
  38. protected virtual SQLiteDatabaseConnection CreateConnection(bool isReadOnly = false)
  39. {
  40. if (!_versionLogged)
  41. {
  42. _versionLogged = true;
  43. Logger.Info("Sqlite version: " + SQLite3.Version);
  44. Logger.Info("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions.ToArray()));
  45. }
  46. ConnectionFlags connectionFlags;
  47. //isReadOnly = false;
  48. if (isReadOnly)
  49. {
  50. connectionFlags = ConnectionFlags.ReadOnly;
  51. //connectionFlags = ConnectionFlags.Create;
  52. //connectionFlags |= ConnectionFlags.ReadWrite;
  53. }
  54. else
  55. {
  56. connectionFlags = ConnectionFlags.Create;
  57. connectionFlags |= ConnectionFlags.ReadWrite;
  58. }
  59. if (EnableConnectionPooling)
  60. {
  61. connectionFlags |= ConnectionFlags.SharedCached;
  62. }
  63. else
  64. {
  65. connectionFlags |= ConnectionFlags.PrivateCache;
  66. }
  67. connectionFlags |= ConnectionFlags.NoMutex;
  68. var db = SQLite3.Open(DbFilePath, connectionFlags, null);
  69. var queries = new List<string>
  70. {
  71. "pragma default_temp_store = memory",
  72. "PRAGMA page_size=4096",
  73. "PRAGMA journal_mode=WAL",
  74. "PRAGMA temp_store=memory",
  75. "PRAGMA synchronous=Normal",
  76. //"PRAGMA cache size=-10000"
  77. };
  78. var cacheSize = CacheSize;
  79. if (cacheSize.HasValue)
  80. {
  81. }
  82. if (EnableExclusiveMode)
  83. {
  84. queries.Add("PRAGMA locking_mode=EXCLUSIVE");
  85. }
  86. //foreach (var query in queries)
  87. //{
  88. // db.Execute(query);
  89. //}
  90. db.ExecuteAll(string.Join(";", queries.ToArray()));
  91. return db;
  92. }
  93. protected virtual int? CacheSize
  94. {
  95. get
  96. {
  97. return null;
  98. }
  99. }
  100. protected virtual bool EnableExclusiveMode
  101. {
  102. get { return false; }
  103. }
  104. internal static void CheckOk(int rc)
  105. {
  106. string msg = "";
  107. if (raw.SQLITE_OK != rc)
  108. {
  109. throw CreateException((ErrorCode)rc, msg);
  110. }
  111. }
  112. internal static Exception CreateException(ErrorCode rc, string msg)
  113. {
  114. var exp = new Exception(msg);
  115. return exp;
  116. }
  117. private bool _disposed;
  118. protected void CheckDisposed()
  119. {
  120. if (_disposed)
  121. {
  122. throw new ObjectDisposedException(GetType().Name + " has been disposed and cannot be accessed.");
  123. }
  124. }
  125. public void Dispose()
  126. {
  127. _disposed = true;
  128. Dispose(true);
  129. GC.SuppressFinalize(this);
  130. }
  131. private readonly object _disposeLock = new object();
  132. /// <summary>
  133. /// Releases unmanaged and - optionally - managed resources.
  134. /// </summary>
  135. /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  136. protected virtual void Dispose(bool dispose)
  137. {
  138. if (dispose)
  139. {
  140. try
  141. {
  142. lock (_disposeLock)
  143. {
  144. using (WriteLock.Write())
  145. {
  146. CloseConnection();
  147. }
  148. }
  149. }
  150. catch (Exception ex)
  151. {
  152. Logger.ErrorException("Error disposing database", ex);
  153. }
  154. }
  155. }
  156. protected virtual void CloseConnection()
  157. {
  158. }
  159. protected List<string> GetColumnNames(IDatabaseConnection connection, string table)
  160. {
  161. var list = new List<string>();
  162. foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
  163. {
  164. if (row[1].SQLiteType != SQLiteType.Null)
  165. {
  166. var name = row[1].ToString();
  167. list.Add(name);
  168. }
  169. }
  170. return list;
  171. }
  172. protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
  173. {
  174. if (existingColumnNames.Contains(columnName, StringComparer.OrdinalIgnoreCase))
  175. {
  176. return;
  177. }
  178. connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL");
  179. }
  180. }
  181. public static class ReaderWriterLockSlimExtensions
  182. {
  183. private sealed class ReadLockToken : IDisposable
  184. {
  185. private ReaderWriterLockSlim _sync;
  186. public ReadLockToken(ReaderWriterLockSlim sync)
  187. {
  188. _sync = sync;
  189. sync.EnterReadLock();
  190. }
  191. public void Dispose()
  192. {
  193. if (_sync != null)
  194. {
  195. _sync.ExitReadLock();
  196. _sync = null;
  197. }
  198. }
  199. }
  200. private sealed class WriteLockToken : IDisposable
  201. {
  202. private ReaderWriterLockSlim _sync;
  203. public WriteLockToken(ReaderWriterLockSlim sync)
  204. {
  205. _sync = sync;
  206. sync.EnterWriteLock();
  207. }
  208. public void Dispose()
  209. {
  210. if (_sync != null)
  211. {
  212. _sync.ExitWriteLock();
  213. _sync = null;
  214. }
  215. }
  216. }
  217. public static IDisposable Read(this ReaderWriterLockSlim obj)
  218. {
  219. return new ReadLockToken(obj);
  220. }
  221. public static IDisposable Write(this ReaderWriterLockSlim obj)
  222. {
  223. return new WriteLockToken(obj);
  224. }
  225. }
  226. }