1 /* 2 * Database - Database abstraction layer for D programing language. 3 * 4 * Copyright (C) 2017 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.database.Statement; 13 14 import hunt.database.Database; 15 import hunt.database.DatabaseOption; 16 import hunt.database.base; 17 import hunt.database.query.Common; 18 19 import hunt.logging; 20 import hunt.String; 21 import hunt.Integer; 22 import hunt.Long; 23 import hunt.Double; 24 import hunt.Float; 25 import hunt.Byte; 26 import hunt.Short; 27 import hunt.Nullable; 28 29 import std.stdio; 30 import std.regex; 31 import std.variant; 32 33 /** 34 * 35 * See_Also: 36 * https://www.codemeright.com/blog/post/named-parameterized-query-java 37 * https://www.javaworld.com/article/2077706/named-parameters-for-preparedstatement.html 38 * https://github.com/marcosemiao/jdbc-named-parameters/tree/master/src/main/java/fr/ms/sql 39 */ 40 class Statement 41 { 42 private SqlConnection _sqlConn = null; 43 private string _sql; 44 private bool _isUsed = false; 45 private int _lastInsertId; 46 private int _affectRows; 47 private RowSet _rs; 48 private Object[string] _parameters; 49 50 private DatabaseOption _options; 51 52 this(SqlConnection db, DatabaseOption options) 53 { 54 _sqlConn = db; 55 _options = options; 56 } 57 58 this(SqlConnection db, string sql, DatabaseOption options) 59 { 60 _sqlConn = db; 61 _options = options; 62 prepare(sql); 63 } 64 65 void prepare(string sql) 66 { 67 assert(sql.length); 68 this._sql = sql; 69 _needReset = true; 70 71 } 72 73 private bool _needReset = false; 74 75 void setParameter(R)(string key, R param) 76 { 77 static if (is(R == int) || is(R == uint)) 78 { 79 _parameters[key] = new Integer(param); 80 } 81 else static if (is(R == string) || is(R == char) || is(R == byte[])) 82 { 83 _parameters[key] = new String(param); 84 } 85 else static if (is(R == bool)) 86 { 87 _parameters[key] = new Boolean(param); 88 } 89 else static if (is(R == double)) 90 { 91 _parameters[key] = new Double(param); 92 } 93 else static if (is(R == float)) 94 { 95 _parameters[key] = new Float(param); 96 } 97 else static if (is(R == short) || is(R == ushort)) 98 { 99 _parameters[key] = new Short(param); 100 } 101 else static if (is(R == long) || is(R == ulong)) 102 { 103 _parameters[key] = new Long(param); 104 } 105 else static if (is(R == byte) || is(R == ubyte)) 106 { 107 _parameters[key] = new Byte(param); 108 } 109 else static if (is(R == class)) 110 { 111 _parameters[key] = param; 112 } 113 else 114 { 115 throw new Exception("IllegalArgument not support : " ~ R.stringof); 116 } 117 _needReset = true; 118 } 119 120 // string sql() 121 // { 122 // auto conn = _sqlConn.getConnection(); 123 // scope (exit) 124 // _sqlConn.relaseConnection(conn); 125 // return sql(conn); 126 // } 127 128 private string sql(SqlConnection conn) 129 { 130 if (!_needReset) 131 return _str; 132 133 string str = _sql; 134 135 foreach (string k, Object v; _parameters) 136 { 137 auto re = regex(r":" ~ k ~ r"([^\w]*)", "g"); 138 if ((cast(String) v !is null) || (cast(Nullable!string) v !is null)) 139 { 140 str = str.replaceAll(re, "'" ~ v.toString() ~ "'" ~ "$1"); 141 142 // if (_sqlConn.getOption().isPgsql() || _sqlConn.getOption().isMysql()) { 143 // // str = str.replaceAll(re, conn.escapeLiteral(v.toString()) ~ "$1"); 144 // // str = str.replaceAll(re, v.toString() ~ "$1"); 145 // // warning(str ~ " " ~ v.toString() ~ "$1"); 146 // // } else if (_sqlConn.getOption().isMysql()) { 147 // // str = str.replaceAll(re, "'" ~ conn.escape(v.toString()) ~ "'" ~ "$1"); 148 // str = str.replaceAll(re, "'" ~ v.toString() ~ "'" ~ "$1"); 149 // } 150 // else 151 // { 152 // str = str.replaceAll(re, quoteSqlString(v.toString()) ~ "$1"); 153 // } 154 } 155 else 156 { 157 str = str.replaceAll(re, v.toString() ~ "$1"); 158 } 159 } 160 161 _needReset = false; 162 _str = str; 163 return str; 164 } 165 166 private string _str; 167 168 int execute() 169 { 170 string execSql = sql(_sqlConn); 171 172 version (HUNT_SQL_DEBUG) 173 logDebug(execSql); 174 175 _rs = _sqlConn.query(execSql); 176 _lastInsertId = 0; 177 178 if(_options.isPgsql()) { 179 Row row = _rs.lastRow(); 180 if(row !is null) { 181 _lastInsertId = row.getInteger(0); 182 } 183 } else if (_options.isMysql()) { 184 import hunt.database.driver.mysql.MySQLClient; 185 Variant value2 = _rs.property(MySQLClient.LAST_INSERTED_ID); 186 if(value2.type != typeid(int)) { 187 warning("Not expected type: ", value2.type); 188 } else { 189 _lastInsertId = value2.get!int(); 190 } 191 } 192 193 // import hunt.database.driver.mysql.MySQLClient; 194 // Variant value2 = _rs.property(MySQLClient.LAST_INSERTED_ID); 195 // if(value2.type != typeid(int)) { 196 // version(HUNT_DEBUG) warning("Not expected type: ", value2.type); 197 // _lastInsertId = 0; 198 // } else { 199 // _lastInsertId = value2.get!int(); 200 // } 201 202 _affectRows = _rs.rowCount(); 203 return _affectRows; 204 } 205 206 207 int execute(string lastIdName) 208 { 209 string execSql = sql(_sqlConn); 210 version (HUNT_SQL_DEBUG) logDebug(execSql); 211 212 _rs = _sqlConn.query(execSql); 213 _lastInsertId = 0; 214 215 if(_options.isPgsql()) { 216 Row row = _rs.lastRow(); 217 if(row !is null) { 218 _lastInsertId = row.getInteger(lastIdName); 219 } 220 } else if (_options.isMysql()) { 221 import hunt.database.driver.mysql.MySQLClient; 222 Variant value2 = _rs.property(MySQLClient.LAST_INSERTED_ID); 223 if(value2.type != typeid(int)) { 224 warning("Not expected type: ", value2.type); 225 } else { 226 _lastInsertId = value2.get!int(); 227 } 228 } 229 230 _affectRows = _rs.rowCount(); 231 return _affectRows; 232 } 233 234 int lastInsertId() 235 { 236 return _lastInsertId; 237 } 238 239 int affectedRows() 240 { 241 return _affectRows; 242 } 243 244 245 int count() 246 { 247 Row res = fetch(); 248 return res.getInteger(0); 249 } 250 251 Row fetch() 252 { 253 if (!_rs) 254 _rs = query(); 255 256 foreach(Row r; _rs) { 257 return r; 258 } 259 260 throw new DatabaseException("RowSet is empty"); 261 } 262 263 RowSet query() 264 { 265 string execSql = sql(null); 266 _rs = _sqlConn.query(execSql); 267 return _rs; 268 } 269 270 // void close() 271 // { 272 // version (HUNT_DEBUG) 273 // info("statement closed"); 274 // } 275 276 // private void isUsed() 277 // { 278 // // scope (exit) 279 // // _isUsed = true; 280 // if (_isUsed) 281 // throw new DatabaseException("statement was used"); 282 // _isUsed = true; 283 // } 284 285 }