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