SqliteExtensions.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #pragma warning disable CS1591
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.Globalization;
  6. using Microsoft.Data.Sqlite;
  7. namespace Emby.Server.Implementations.Data
  8. {
  9. public static class SqliteExtensions
  10. {
  11. private const string DatetimeFormatUtc = "yyyy-MM-dd HH:mm:ss.FFFFFFFK";
  12. private const string DatetimeFormatLocal = "yyyy-MM-dd HH:mm:ss.FFFFFFF";
  13. /// <summary>
  14. /// An array of ISO-8601 DateTime formats that we support parsing.
  15. /// </summary>
  16. private static readonly string[] _datetimeFormats = new string[]
  17. {
  18. "THHmmssK",
  19. "THHmmK",
  20. "HH:mm:ss.FFFFFFFK",
  21. "HH:mm:ssK",
  22. "HH:mmK",
  23. DatetimeFormatUtc,
  24. "yyyy-MM-dd HH:mm:ssK",
  25. "yyyy-MM-dd HH:mmK",
  26. "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
  27. "yyyy-MM-ddTHH:mmK",
  28. "yyyy-MM-ddTHH:mm:ssK",
  29. "yyyyMMddHHmmssK",
  30. "yyyyMMddHHmmK",
  31. "yyyyMMddTHHmmssFFFFFFFK",
  32. "THHmmss",
  33. "THHmm",
  34. "HH:mm:ss.FFFFFFF",
  35. "HH:mm:ss",
  36. "HH:mm",
  37. DatetimeFormatLocal,
  38. "yyyy-MM-dd HH:mm:ss",
  39. "yyyy-MM-dd HH:mm",
  40. "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
  41. "yyyy-MM-ddTHH:mm",
  42. "yyyy-MM-ddTHH:mm:ss",
  43. "yyyyMMddHHmmss",
  44. "yyyyMMddHHmm",
  45. "yyyyMMddTHHmmssFFFFFFF",
  46. "yyyy-MM-dd",
  47. "yyyyMMdd",
  48. "yy-MM-dd"
  49. };
  50. public static IEnumerable<SqliteDataReader> Query(this SqliteConnection sqliteConnection, string commandText)
  51. {
  52. if (sqliteConnection.State != ConnectionState.Open)
  53. {
  54. sqliteConnection.Open();
  55. }
  56. using var command = sqliteConnection.CreateCommand();
  57. command.CommandText = commandText;
  58. using (var reader = command.ExecuteReader())
  59. {
  60. while (reader.Read())
  61. {
  62. yield return reader;
  63. }
  64. }
  65. }
  66. public static void Execute(this SqliteConnection sqliteConnection, string commandText)
  67. {
  68. using var command = sqliteConnection.CreateCommand();
  69. command.CommandText = commandText;
  70. command.ExecuteNonQuery();
  71. }
  72. public static string ToDateTimeParamValue(this DateTime dateValue)
  73. {
  74. var kind = DateTimeKind.Utc;
  75. return (dateValue.Kind == DateTimeKind.Unspecified)
  76. ? DateTime.SpecifyKind(dateValue, kind).ToString(
  77. GetDateTimeKindFormat(kind),
  78. CultureInfo.InvariantCulture)
  79. : dateValue.ToString(
  80. GetDateTimeKindFormat(dateValue.Kind),
  81. CultureInfo.InvariantCulture);
  82. }
  83. private static string GetDateTimeKindFormat(DateTimeKind kind)
  84. => (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
  85. public static bool TryReadDateTime(this SqliteDataReader reader, int index, out DateTime result)
  86. {
  87. if (reader.IsDBNull(index))
  88. {
  89. result = default;
  90. return false;
  91. }
  92. var dateText = reader.GetString(index);
  93. if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult))
  94. {
  95. // If the resulting DateTimeKind is Unspecified it is actually Utc.
  96. // This is required downstream for the Json serializer.
  97. if (dateTimeResult.Kind == DateTimeKind.Unspecified)
  98. {
  99. dateTimeResult = DateTime.SpecifyKind(dateTimeResult, DateTimeKind.Utc);
  100. }
  101. result = dateTimeResult;
  102. return true;
  103. }
  104. result = default;
  105. return false;
  106. }
  107. public static bool TryGetGuid(this SqliteDataReader reader, int index, out Guid result)
  108. {
  109. if (reader.IsDBNull(index))
  110. {
  111. result = default;
  112. return false;
  113. }
  114. result = reader.GetGuid(index);
  115. return true;
  116. }
  117. public static bool TryGetString(this SqliteDataReader reader, int index, out string result)
  118. {
  119. result = string.Empty;
  120. if (reader.IsDBNull(index))
  121. {
  122. return false;
  123. }
  124. result = reader.GetString(index);
  125. return true;
  126. }
  127. public static bool TryGetBoolean(this SqliteDataReader reader, int index, out bool result)
  128. {
  129. if (reader.IsDBNull(index))
  130. {
  131. result = default;
  132. return false;
  133. }
  134. result = reader.GetBoolean(index);
  135. return true;
  136. }
  137. public static bool TryGetInt32(this SqliteDataReader reader, int index, out int result)
  138. {
  139. if (reader.IsDBNull(index))
  140. {
  141. result = default;
  142. return false;
  143. }
  144. result = reader.GetInt32(index);
  145. return true;
  146. }
  147. public static bool TryGetInt64(this SqliteDataReader reader, int index, out long result)
  148. {
  149. if (reader.IsDBNull(index))
  150. {
  151. result = default;
  152. return false;
  153. }
  154. result = reader.GetInt64(index);
  155. return true;
  156. }
  157. public static bool TryGetSingle(this SqliteDataReader reader, int index, out float result)
  158. {
  159. if (reader.IsDBNull(index))
  160. {
  161. result = default;
  162. return false;
  163. }
  164. result = reader.GetFloat(index);
  165. return true;
  166. }
  167. public static bool TryGetDouble(this SqliteDataReader reader, int index, out double result)
  168. {
  169. if (reader.IsDBNull(index))
  170. {
  171. result = default;
  172. return false;
  173. }
  174. result = reader.GetDouble(index);
  175. return true;
  176. }
  177. public static void TryBind(this SqliteCommand statement, string name, Guid value)
  178. {
  179. statement.TryBind(name, value, true);
  180. }
  181. public static void TryBind(this SqliteCommand statement, string name, object? value, bool isBlob = false)
  182. {
  183. var preparedValue = value ?? DBNull.Value;
  184. if (statement.Parameters.Contains(name))
  185. {
  186. statement.Parameters[name].Value = preparedValue;
  187. }
  188. else
  189. {
  190. // Blobs aren't always detected automatically
  191. if (isBlob)
  192. {
  193. statement.Parameters.Add(new SqliteParameter(name, SqliteType.Blob) { Value = value });
  194. }
  195. else
  196. {
  197. statement.Parameters.AddWithValue(name, preparedValue);
  198. }
  199. }
  200. }
  201. public static void TryBindNull(this SqliteCommand statement, string name)
  202. {
  203. statement.TryBind(name, DBNull.Value);
  204. }
  205. public static IEnumerable<SqliteDataReader> ExecuteQuery(this SqliteCommand command)
  206. {
  207. using (var reader = command.ExecuteReader())
  208. {
  209. while (reader.Read())
  210. {
  211. yield return reader;
  212. }
  213. }
  214. }
  215. public static int SelectScalarInt(this SqliteCommand command)
  216. {
  217. var result = command.ExecuteScalar();
  218. // Can't be null since the method is used to retrieve Count
  219. return Convert.ToInt32(result!, CultureInfo.InvariantCulture);
  220. }
  221. public static SqliteCommand PrepareStatement(this SqliteConnection sqliteConnection, string sql)
  222. {
  223. var command = sqliteConnection.CreateCommand();
  224. command.CommandText = sql;
  225. return command;
  226. }
  227. }
  228. }