SqliteExtensions.cs 13 KB

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