Database.cs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Text;
  4. namespace etoy
  5. {
  6. class Database : IDisposable
  7. {
  8. const string SQLITE = "sqlite";
  9. const int SQLITE_OK = 0;
  10. IntPtr _db;
  11. bool _connected;
  12. public Database(string file)
  13. {
  14. Connect(file);
  15. }
  16. void Connect(string file)
  17. {
  18. if (_connected)
  19. throw new Exception("There is already an open connection");
  20. Encoding.UTF8.GetBytes(file);
  21. SQLiteOpenFlags flag = SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create;
  22. if (sqlite3_open_v2(GetNullTerminatedUtf8(file), out _db, (int)flag, null) != SQLITE_OK)
  23. throw new Exception("Could not open database file: " + file);
  24. _connected = true;
  25. }
  26. public unsafe void Exec(string sql)
  27. {
  28. int r = sqlite3_exec(_db, GetNullTerminatedUtf8(sql), OnSqliteCallback, IntPtr.Zero, out IntPtr errmsg);
  29. if (r != SQLITE_OK)
  30. throw new Exception($"Exec code({r}), {Marshal.PtrToStringAnsi(errmsg)}, sql: {sql}");
  31. }
  32. public void Close()
  33. {
  34. if (!_connected) return;
  35. _connected = false;
  36. if (_db != IntPtr.Zero)
  37. sqlite3_close(_db);
  38. }
  39. public void Dispose()
  40. {
  41. Close();
  42. }
  43. static byte[] GetNullTerminatedUtf8(string s)
  44. {
  45. var utf8Length = Encoding.UTF8.GetByteCount(s);
  46. var bytes = new byte[utf8Length + 1];
  47. Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0);
  48. return bytes;
  49. }
  50. enum SQLiteOpenFlags
  51. {
  52. ReadOnly = 1,
  53. ReadWrite = 2,
  54. Create = 4
  55. }
  56. unsafe int OnSqliteCallback(IntPtr state, int columnCount, void** columnValues, void** columnNames)
  57. {
  58. for (int i = 0; i < columnCount; i++)
  59. {
  60. var name = new IntPtr(*(columnNames + i));
  61. var value = new IntPtr(*(columnValues + i));
  62. Console.WriteLine($"{Marshal.PtrToStringAnsi(name)} = {Marshal.PtrToStringAnsi(value)}");
  63. }
  64. Console.WriteLine();
  65. return 0;
  66. }
  67. [DllImport(SQLITE, EntryPoint = "sqlite3_open_v2")]
  68. static extern int sqlite3_open_v2(byte[] filename, out IntPtr db, int flags, [MarshalAs(UnmanagedType.LPStr)] string zvfs);
  69. [DllImport(SQLITE, EntryPoint = "sqlite3_close")]
  70. static extern int sqlite3_close(IntPtr db);
  71. [DllImport(SQLITE, EntryPoint = "sqlite3_exec", CallingConvention = CallingConvention.StdCall)]
  72. static extern int sqlite3_exec(IntPtr db, byte[] sql, SqliteExecCallback callback, IntPtr state, out IntPtr errmsg);
  73. unsafe delegate int SqliteExecCallback(IntPtr state, int columnCount, void** columnValues, void** columnNames);
  74. }
  75. }