1 module mysql.mysql; 2 3 import mysql.binding; 4 5 public import mysql.mysql_result; 6 public import mysql.mysql_row; 7 import mysql.query_interface; 8 9 import std.stdio; 10 import std.exception; 11 import std.typecons; 12 13 class MysqlDatabaseException : Exception { 14 this(string msg, string file = __FILE__, size_t line = __LINE__) { 15 super(msg, file, line); 16 } 17 } 18 19 class Mysql { 20 private string _dbname; 21 private MYSQL* mysql; 22 private string lastErrorMsg; 23 24 this(string host, string user, string pass, string db) { 25 initMysql(); 26 connect(host, 0, user, pass, db, null); 27 } 28 29 this(string host, uint port, string user, string pass, string db, string charset = null) { 30 initMysql(); 31 connect(host, port, user, pass, db, null, charset); 32 } 33 34 this(string host, string user, string pass) { 35 initMysql(); 36 connect(host, user, pass); 37 } 38 39 this() { 40 initMysql(); 41 } 42 43 private void initMysql () { 44 mysql = enforceEx!(MysqlDatabaseException)(mysql_init(null), "Couldn't init mysql"); 45 setReconnect(true); 46 } 47 48 void connect(string host, uint port, string user, string pass, string db, string unixSocket, string charset = null) { 49 enforceEx!(MysqlDatabaseException)( 50 mysql_real_connect(mysql, 51 toCstring(host), 52 toCstring(user), 53 toCstring(pass), 54 toCstring(db), 55 port, 56 unixSocket ? toCstring(unixSocket) : null, 57 0), 58 error() 59 ); 60 61 _dbname = db; 62 63 if (charset != null) setCharset(charset); 64 } 65 66 void connect(string host, uint port, string user, string pass, string db, string charset="utf8") { 67 connect(host, port, user, pass, db, null, charset); 68 } 69 70 void connect(string host, string user, string pass, string db) { 71 connect(host, 0, user, pass, db, null); 72 } 73 74 void connect(string host, string user, string pass) { 75 connect(host, 0, user, pass, null, null); 76 } 77 78 int selectDb(string newDbName) { 79 auto res = mysql_select_db(mysql, toCstring(newDbName)); 80 _dbname = newDbName; 81 return res; 82 } 83 84 string dbname() { 85 return _dbname; 86 } 87 88 int setOption(mysql_option option, const void* value) { 89 return mysql_options(mysql, option, &value); 90 } 91 92 int setReconnect(bool value) { 93 return setOption(mysql_option.MYSQL_OPT_RECONNECT, &value); 94 } 95 96 int setConnectTimeout(int value) { 97 return setOption(mysql_option.MYSQL_OPT_CONNECT_TIMEOUT, cast(const(char*))value); 98 } 99 100 int setCharset(string charset) { 101 return mysql_set_character_set(mysql, toCstring(charset)); 102 } 103 104 string charset() { 105 return fromCstring(mysql_character_set_name(mysql)); 106 } 107 108 static ulong clientVersion() { 109 return mysql_get_client_version(); 110 } 111 112 static string clientVersionString() { 113 return fromCstring(mysql_get_client_info()); 114 } 115 116 void startTransaction() { 117 query("START TRANSACTION"); 118 } 119 120 string error() { 121 return fromCstring(mysql_error(mysql)); 122 } 123 124 void close() { 125 if (mysql) { 126 mysql_close(mysql); 127 mysql = null; 128 } 129 } 130 131 ~this() { 132 close(); 133 } 134 135 // MYSQL API call 136 int lastInsertId() { 137 return cast(int) mysql_insert_id(mysql); 138 } 139 140 // MYSQL API call 141 int affectedRows() { 142 return cast(int) mysql_affected_rows(mysql); 143 } 144 145 // MYSQL API call 146 string escape(string str) { 147 ubyte[] buffer = new ubyte[str.length * 2 + 1]; 148 buffer.length = mysql_real_escape_string(mysql, buffer.ptr, cast(cstring) str.ptr, cast(uint) str.length); 149 150 return cast(string) buffer; 151 } 152 153 // MYSQL API call 154 MysqlResult queryImpl(string sql) { 155 enforceEx!(MysqlDatabaseException)( 156 !mysql_query(mysql, toCstring(sql)), 157 error() ~ " :::: " ~ sql); 158 159 return new MysqlResult(mysql_store_result(mysql), sql); 160 } 161 162 // To be used with commands that do not return a result (INSERT, UPDATE, etc...) 163 bool execImpl(string sql) { 164 bool success = false; 165 166 if (mysql_query(mysql, toCstring(sql)) == 0) { 167 success = true; 168 this.lastErrorMsg = ""; 169 } else { 170 this.lastErrorMsg = error() ~ " :::: " ~ sql; 171 } 172 173 return success; 174 } 175 176 // MYSQL API call 177 int ping() { 178 return mysql_ping(mysql); 179 } 180 181 // MYSQL API call 182 string stat() { 183 return fromCstring(mysql_stat(mysql)); 184 } 185 186 // ====== helpers ====== 187 188 // Smart interface thing. 189 // accept multiple attributes and make replacement of '?' in sql 190 // like this: 191 // auto row = mysql.query("select * from table where id = ?", 10); 192 MysqlResult query(T...)(string sql, T t) { 193 return queryImpl(QueryInterface.makeQuery(this, sql, t)); 194 } 195 196 bool exec(T...)(string sql, T t) { 197 return execImpl(QueryInterface.makeQuery(this, sql, t)); 198 } 199 200 string dbErrorMsg() { 201 return this.lastErrorMsg; 202 } 203 204 // simply make mysq.query().front 205 // and if no rows then raise an exception 206 Nullable!MysqlRow queryOneRow(string file = __FILE__, size_t line = __LINE__, T...)(string sql, T t) { 207 auto res = query(sql, t); 208 if (res.empty) { 209 return Nullable!MysqlRow.init; 210 } 211 auto row = res.front; 212 213 return Nullable!MysqlRow(row); 214 } 215 216 /* 217 ResultByDataObject!R queryDataObject(R = DataObject, T...)(string sql, T t) { 218 // modify sql for the best data object grabbing 219 sql = fixupSqlForDataObjectUse(sql); 220 221 auto magic = query(sql, t); 222 return ResultByDataObject!R(cast(MysqlResult) magic, this); 223 } 224 225 226 ResultByDataObject!R queryDataObjectWithCustomKeys(R = DataObject, T...)(string[string] keyMapping, string sql, T t) { 227 sql = fixupSqlForDataObjectUse(sql, keyMapping); 228 229 auto magic = query(sql, t); 230 return ResultByDataObject!R(cast(MysqlResult) magic, this); 231 } 232 */ 233 } 234 235 /* 236 struct ResultByDataObject(ObjType) if (is(ObjType : DataObject)) { 237 MysqlResult result; 238 Mysql mysql; 239 240 this(MysqlResult r, Mysql mysql) { 241 result = r; 242 auto fields = r.fields(); 243 this.mysql = mysql; 244 245 foreach(i, f; fields) { 246 string tbl = fromCstring(f.org_table is null ? f.table : f.org_table, f.org_table is null ? f.table_length : f.org_table_length); 247 mappings[fromCstring(f.name)] = tuple( 248 tbl, 249 fromCstring(f.org_name is null ? f.name : f.org_name, f.org_name is null ? f.name_length : f.org_name_length)); 250 } 251 252 253 } 254 255 Tuple!(string, string)[string] mappings; 256 257 ulong length() { return result.length; } 258 bool empty() { return result.empty; } 259 void popFront() { result.popFront(); } 260 ObjType front() { 261 return new ObjType(mysql, result.front.toAA, mappings); 262 } 263 // would it be good to add a new() method? would be valid even if empty 264 // it'd just fill in the ID's at random and allow you to do the rest 265 266 @disable this(this) { } 267 } 268 */ 269 270 271 class EmptyResultException : Exception { 272 this(string message, string file = __FILE__, size_t line = __LINE__) { 273 super(message, file, line); 274 } 275 } 276 277 278 /* 279 void main() { 280 auto mysql = new Mysql("localhost", "uname", "password", "test"); 281 scope(exit) delete mysql; 282 283 mysql.query("INSERT INTO users (id, password) VALUES (?, ?)", 10, "lol"); 284 285 foreach(row; mysql.query("SELECT * FROM users")) { 286 writefln("%s %s %s %s", row["id"], row[0], row[1], row["username"]); 287 } 288 } 289 */ 290 291 /+ 292 mysql.linq.tablename.field[key] // select field from tablename where id = key 293 294 mysql.link["name"].table.field[key] // select field from table where name = key 295 296 297 auto q = mysql.prepQuery("select id from table where something"); 298 q.sort("name"); 299 q.limit(start, count); 300 q.page(3, pagelength = ?); 301 302 q.execute(params here); // returns the same Result range as query 303 +/ 304 305 /* 306 void main() { 307 auto db = new Mysql("localhost", "uname", "password", "test"); 308 foreach(item; db.queryDataObject("SELECT users.*, username 309 FROM users, password_manager_accounts 310 WHERE password_manager_accounts.user_id = users.id LIMIT 5")) { 311 writefln("item: %s, %s", item.id, item.username); 312 item.first = "new"; 313 item.last = "new2"; 314 item.username = "kill"; 315 //item.commitChanges(); 316 } 317 } 318 */ 319 320 321 /* 322 Copyright: Adam D. Ruppe, 2009 - 2011 323 License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 324 Authors: Adam D. Ruppe, with contributions from Nick Sabalausky 325 326 Copyright Adam D. Ruppe 2009 - 2011. 327 Distributed under the Boost Software License, Version 1.0. 328 (See accompanying file LICENSE_1_0.txt or copy at 329 http://www.boost.org/LICENSE_1_0.txt) 330 */ 331