1 /**
2 * SQLite wrapper
3 * 
4 * Copyright:
5 * (C) 2012 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.utils.sqlite3.sqlite3;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_SQLITE):
15 
16 import std.exception;
17 import etc.c.sqlite3;
18 import botan.utils.types;
19 import botan.utils.types;
20 import std.string : toStringz, fromStringz;
21 import std.datetime;
22 
23 class sqlite3_database
24 {
25 public:
26     this(in string db_filename)
27     {
28 		int rc = sqlite3_open(db_filename.toStringz, &m_db);
29         if (!rc)
30 			sqlite3_busy_timeout(m_db, 500);
31         if (rc)
32         {
33             const string err_msg = fromStringz(sqlite3_errmsg(m_db)).to!string;
34             sqlite3_close(m_db);
35             m_db = null;
36             throw new Exception("sqlite3_open failed - " ~ err_msg);
37         }
38     }
39     this(in string file);
40 
41     ~this()
42     {
43         if (m_db)
44             sqlite3_close(m_db);
45         m_db = null;
46     }
47 
48     size_t rowCount(in string table_name)
49     {
50         sqlite3_statement stmt = sqlite3_statement(this, "select count(*) from " ~ table_name);
51         
52         if (stmt.step())
53             return stmt.getSizeT(0);
54         else
55             throw new Exception("Querying size of table " ~ table_name ~ " failed");
56     }
57 
58     void createTable(in string table_schema)
59     {
60         char* errmsg = null;
61 		int rc;
62 		int iter;
63 		import core.thread;
64 		do {
65 			rc = sqlite3_exec(m_db, table_schema.toStringz, null, null, &errmsg);
66 			if (rc != SQLITE_OK) {
67 				Thread.sleep(30.msecs);
68 				continue;
69 			}
70 		} while (++iter < 5);
71 
72 		if (rc != SQLITE_OK) {
73 			import std.conv : to;
74             const string err_msg = fromStringz(errmsg).to!string;
75             sqlite3_free(errmsg);
76             sqlite3_close(m_db);
77             m_db = null;
78             throw new Exception("sqlite3_exec for table failed - " ~ err_msg);
79         }
80     }
81 private:
82     sqlite3* m_db;
83 }
84 
85 struct sqlite3_statement
86 {
87 public:
88     this(sqlite3_database db, in string base_sql)
89     {
90         int rc = sqlite3_prepare_v2(db.m_db, base_sql.toStringz, -1, &m_stmt, null);
91         
92         if (rc != SQLITE_OK)
93             throw new Exception("sqlite3_prepare failed " ~ base_sql ~ ", code " ~ to!string(rc));
94     }
95 
96     void bind(int column, in string val)
97     {
98         int rc = sqlite3_bind_text(m_stmt, column, val.toStringz, -1, SQLITE_TRANSIENT);
99         if (rc != SQLITE_OK)
100             throw new Exception("sqlite3_bind_text failed, code " ~ to!string(rc));
101     }
102 
103     void bind(int column, int val)
104     {
105         int rc = sqlite3_bind_int(m_stmt, column, val);
106         if (rc != SQLITE_OK)
107             throw new Exception("sqlite3_bind_int failed, code " ~ to!string(rc));
108     }
109 
110     void bind(int column, SysTime time)
111     {
112         const int timeval = cast(int)time.toUnixTime();
113         bind(column, timeval);
114     }
115 
116     void bind()(int column, auto const ref Vector!ubyte val)
117     {
118         int rc = sqlite3_bind_blob(m_stmt, column, cast(void*)val.ptr, cast(int)val.length, SQLITE_TRANSIENT);
119         if (rc != SQLITE_OK)
120             throw new Exception("sqlite3_bind_text failed, code " ~ to!string(rc));
121     }
122 
123     Pair!(const(ubyte)*, size_t) getBlob(int column)
124     {
125         assert(sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB,
126                      "Return value is a blob");
127         
128         const void* session_blob = sqlite3_column_blob(m_stmt, column);
129         const int session_blob_size = sqlite3_column_bytes(m_stmt, column);
130         
131         assert(session_blob_size >= 0, "Blob size is non-negative");
132         
133         return makePair(cast(const(ubyte)*)(session_blob),
134                     cast(size_t)(session_blob_size));
135     }
136 
137     size_t getSizeT(int column)
138     {
139         assert(sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER,
140                      "Return count is an integer");
141         
142         const int sessions_int = sqlite3_column_int(m_stmt, column);
143         
144         assert(sessions_int >= 0, "Expected size_t is non-negative");
145         
146         return cast(size_t)(sessions_int);
147     }
148 
149     void spin()
150     {
151         while (step()) {}
152     }
153 
154     bool step()
155     {
156         return (sqlite3_step(m_stmt) == SQLITE_ROW);
157     }
158 
159     sqlite3_stmt* stmt() { return m_stmt; }
160 
161     ~this()
162     {
163         sqlite3_finalize(m_stmt);
164     }
165 private:
166     sqlite3_stmt* m_stmt;
167 }