SqliteExtensions.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. #nullable disable
  2. #pragma warning disable CS1591
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Globalization;
  8. using SQLitePCL.pretty;
  9. namespace Emby.Server.Implementations.Data
  10. {
  11. public static class SqliteExtensions
  12. {
  13. private const string DatetimeFormatUtc = "yyyy-MM-dd HH:mm:ss.FFFFFFFK";
  14. private const string DatetimeFormatLocal = "yyyy-MM-dd HH:mm:ss.FFFFFFF";
  15. /// <summary>
  16. /// An array of ISO-8601 DateTime formats that we support parsing.
  17. /// </summary>
  18. private static readonly string[] _datetimeFormats = new string[]
  19. {
  20. "THHmmssK",
  21. "THHmmK",
  22. "HH:mm:ss.FFFFFFFK",
  23. "HH:mm:ssK",
  24. "HH:mmK",
  25. DatetimeFormatUtc,
  26. "yyyy-MM-dd HH:mm:ssK",
  27. "yyyy-MM-dd HH:mmK",
  28. "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
  29. "yyyy-MM-ddTHH:mmK",
  30. "yyyy-MM-ddTHH:mm:ssK",
  31. "yyyyMMddHHmmssK",
  32. "yyyyMMddHHmmK",
  33. "yyyyMMddTHHmmssFFFFFFFK",
  34. "THHmmss",
  35. "THHmm",
  36. "HH:mm:ss.FFFFFFF",
  37. "HH:mm:ss",
  38. "HH:mm",
  39. DatetimeFormatLocal,
  40. "yyyy-MM-dd HH:mm:ss",
  41. "yyyy-MM-dd HH:mm",
  42. "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
  43. "yyyy-MM-ddTHH:mm",
  44. "yyyy-MM-ddTHH:mm:ss",
  45. "yyyyMMddHHmmss",
  46. "yyyyMMddHHmm",
  47. "yyyyMMddTHHmmssFFFFFFF",
  48. "yyyy-MM-dd",
  49. "yyyyMMdd",
  50. "yy-MM-dd"
  51. };
  52. public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries)
  53. {
  54. if (queries == null)
  55. {
  56. throw new ArgumentNullException(nameof(queries));
  57. }
  58. connection.RunInTransaction(conn =>
  59. {
  60. conn.ExecuteAll(string.Join(';', queries));
  61. });
  62. }
  63. public static Guid ReadGuidFromBlob(this IResultSetValue result)
  64. {
  65. return new Guid(result.ToBlob());
  66. }
  67. public static string ToDateTimeParamValue(this DateTime dateValue)
  68. {
  69. var kind = DateTimeKind.Utc;
  70. return (dateValue.Kind == DateTimeKind.Unspecified)
  71. ? DateTime.SpecifyKind(dateValue, kind).ToString(
  72. GetDateTimeKindFormat(kind),
  73. CultureInfo.InvariantCulture)
  74. : dateValue.ToString(
  75. GetDateTimeKindFormat(dateValue.Kind),
  76. CultureInfo.InvariantCulture);
  77. }
  78. private static string GetDateTimeKindFormat(DateTimeKind kind)
  79. => (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
  80. public static DateTime ReadDateTime(this IResultSetValue result)
  81. {
  82. var dateText = result.ToString();
  83. return DateTime.ParseExact(
  84. dateText,
  85. _datetimeFormats,
  86. DateTimeFormatInfo.InvariantInfo,
  87. DateTimeStyles.None).ToUniversalTime();
  88. }
  89. public static bool TryReadDateTime(this IReadOnlyList<IResultSetValue> reader, int index, out DateTime result)
  90. {
  91. var item = reader[index];
  92. if (item.IsDbNull())
  93. {
  94. result = default;
  95. return false;
  96. }
  97. var dateText = item.ToString();
  98. if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out var dateTimeResult))
  99. {
  100. result = dateTimeResult.ToUniversalTime();
  101. return true;
  102. }
  103. result = default;
  104. return false;
  105. }
  106. public static bool TryGetGuid(this IReadOnlyList<IResultSetValue> reader, int index, out Guid result)
  107. {
  108. var item = reader[index];
  109. if (item.IsDbNull())
  110. {
  111. result = default;
  112. return false;
  113. }
  114. result = item.ReadGuidFromBlob();
  115. return true;
  116. }
  117. public static bool IsDbNull(this IResultSetValue result)
  118. {
  119. return result.SQLiteType == SQLiteType.Null;
  120. }
  121. public static string GetString(this IReadOnlyList<IResultSetValue> result, int index)
  122. {
  123. return result[index].ToString();
  124. }
  125. public static bool TryGetString(this IReadOnlyList<IResultSetValue> reader, int index, out string result)
  126. {
  127. result = null;
  128. var item = reader[index];
  129. if (item.IsDbNull())
  130. {
  131. return false;
  132. }
  133. result = item.ToString();
  134. return true;
  135. }
  136. public static bool GetBoolean(this IReadOnlyList<IResultSetValue> result, int index)
  137. {
  138. return result[index].ToBool();
  139. }
  140. public static bool TryGetBoolean(this IReadOnlyList<IResultSetValue> reader, int index, out bool result)
  141. {
  142. var item = reader[index];
  143. if (item.IsDbNull())
  144. {
  145. result = default;
  146. return false;
  147. }
  148. result = item.ToBool();
  149. return true;
  150. }
  151. public static bool TryGetInt32(this IReadOnlyList<IResultSetValue> reader, int index, out int result)
  152. {
  153. var item = reader[index];
  154. if (item.IsDbNull())
  155. {
  156. result = default;
  157. return false;
  158. }
  159. result = item.ToInt();
  160. return true;
  161. }
  162. public static long GetInt64(this IReadOnlyList<IResultSetValue> result, int index)
  163. {
  164. return result[index].ToInt64();
  165. }
  166. public static bool TryGetInt64(this IReadOnlyList<IResultSetValue> reader, int index, out long result)
  167. {
  168. var item = reader[index];
  169. if (item.IsDbNull())
  170. {
  171. result = default;
  172. return false;
  173. }
  174. result = item.ToInt64();
  175. return true;
  176. }
  177. public static bool TryGetSingle(this IReadOnlyList<IResultSetValue> reader, int index, out float result)
  178. {
  179. var item = reader[index];
  180. if (item.IsDbNull())
  181. {
  182. result = default;
  183. return false;
  184. }
  185. result = item.ToFloat();
  186. return true;
  187. }
  188. public static bool TryGetDouble(this IReadOnlyList<IResultSetValue> reader, int index, out double result)
  189. {
  190. var item = reader[index];
  191. if (item.IsDbNull())
  192. {
  193. result = default;
  194. return false;
  195. }
  196. result = item.ToDouble();
  197. return true;
  198. }
  199. public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
  200. {
  201. return result[index].ReadGuidFromBlob();
  202. }
  203. [Conditional("DEBUG")]
  204. private static void CheckName(string name)
  205. {
  206. throw new ArgumentException("Invalid param name: " + name, nameof(name));
  207. }
  208. public static void TryBind(this IStatement statement, string name, double value)
  209. {
  210. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  211. {
  212. bindParam.Bind(value);
  213. }
  214. else
  215. {
  216. CheckName(name);
  217. }
  218. }
  219. public static void TryBind(this IStatement statement, string name, string value)
  220. {
  221. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  222. {
  223. if (value == null)
  224. {
  225. bindParam.BindNull();
  226. }
  227. else
  228. {
  229. bindParam.Bind(value);
  230. }
  231. }
  232. else
  233. {
  234. CheckName(name);
  235. }
  236. }
  237. public static void TryBind(this IStatement statement, string name, bool value)
  238. {
  239. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  240. {
  241. bindParam.Bind(value);
  242. }
  243. else
  244. {
  245. CheckName(name);
  246. }
  247. }
  248. public static void TryBind(this IStatement statement, string name, float value)
  249. {
  250. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  251. {
  252. bindParam.Bind(value);
  253. }
  254. else
  255. {
  256. CheckName(name);
  257. }
  258. }
  259. public static void TryBind(this IStatement statement, string name, int value)
  260. {
  261. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  262. {
  263. bindParam.Bind(value);
  264. }
  265. else
  266. {
  267. CheckName(name);
  268. }
  269. }
  270. public static void TryBind(this IStatement statement, string name, Guid value)
  271. {
  272. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  273. {
  274. Span<byte> byteValue = stackalloc byte[16];
  275. value.TryWriteBytes(byteValue);
  276. bindParam.Bind(byteValue);
  277. }
  278. else
  279. {
  280. CheckName(name);
  281. }
  282. }
  283. public static void TryBind(this IStatement statement, string name, DateTime value)
  284. {
  285. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  286. {
  287. bindParam.Bind(value.ToDateTimeParamValue());
  288. }
  289. else
  290. {
  291. CheckName(name);
  292. }
  293. }
  294. public static void TryBind(this IStatement statement, string name, long value)
  295. {
  296. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  297. {
  298. bindParam.Bind(value);
  299. }
  300. else
  301. {
  302. CheckName(name);
  303. }
  304. }
  305. public static void TryBind(this IStatement statement, string name, ReadOnlySpan<byte> value)
  306. {
  307. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  308. {
  309. bindParam.Bind(value);
  310. }
  311. else
  312. {
  313. CheckName(name);
  314. }
  315. }
  316. public static void TryBindNull(this IStatement statement, string name)
  317. {
  318. if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
  319. {
  320. bindParam.BindNull();
  321. }
  322. else
  323. {
  324. CheckName(name);
  325. }
  326. }
  327. public static void TryBind(this IStatement statement, string name, DateTime? value)
  328. {
  329. if (value.HasValue)
  330. {
  331. TryBind(statement, name, value.Value);
  332. }
  333. else
  334. {
  335. TryBindNull(statement, name);
  336. }
  337. }
  338. public static void TryBind(this IStatement statement, string name, Guid? value)
  339. {
  340. if (value.HasValue)
  341. {
  342. TryBind(statement, name, value.Value);
  343. }
  344. else
  345. {
  346. TryBindNull(statement, name);
  347. }
  348. }
  349. public static void TryBind(this IStatement statement, string name, double? value)
  350. {
  351. if (value.HasValue)
  352. {
  353. TryBind(statement, name, value.Value);
  354. }
  355. else
  356. {
  357. TryBindNull(statement, name);
  358. }
  359. }
  360. public static void TryBind(this IStatement statement, string name, int? value)
  361. {
  362. if (value.HasValue)
  363. {
  364. TryBind(statement, name, value.Value);
  365. }
  366. else
  367. {
  368. TryBindNull(statement, name);
  369. }
  370. }
  371. public static void TryBind(this IStatement statement, string name, float? value)
  372. {
  373. if (value.HasValue)
  374. {
  375. TryBind(statement, name, value.Value);
  376. }
  377. else
  378. {
  379. TryBindNull(statement, name);
  380. }
  381. }
  382. public static void TryBind(this IStatement statement, string name, bool? value)
  383. {
  384. if (value.HasValue)
  385. {
  386. TryBind(statement, name, value.Value);
  387. }
  388. else
  389. {
  390. TryBindNull(statement, name);
  391. }
  392. }
  393. public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(this IStatement statement)
  394. {
  395. while (statement.MoveNext())
  396. {
  397. yield return statement.Current;
  398. }
  399. }
  400. }
  401. }