1 /** 2 * Copyright © DiamondMVC 2019 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.data.mapping.engines.mysql.mysqladapter; 7 8 import std.variant : Variant; 9 import std.algorithm : map; 10 import std.array : array; 11 12 import mysql; 13 14 import diamond.data.mapping.engines.sqlshared; 15 import diamond.data.mapping.engines.mysql.mysqlmodel; 16 import diamond.database : DbParam; 17 import diamond.data.mapping.engines.mysql; 18 19 /// CTFE string for mixin MySql connection setup with specialized parameters. 20 private enum MySqlConnectionNamedParametersSetup = q{ 21 auto useDbConnectionString = connectionString ? connectionString : super.connectionString; 22 23 // Prepare statement 24 string newSql; 25 DbParam[] newParams = null; 26 if (params) 27 { 28 newParams = prepareSql(query, params, newSql); 29 } 30 else 31 { 32 newSql = query; 33 } 34 35 auto pool = getPool(useDbConnectionString); 36 auto connection = pool.lockConnection(); 37 auto prepared = connection.prepare(newSql); 38 39 prepared.setArgs(newParams ? newParams : new DbParam[0]); 40 }; 41 42 /// CTFE string for mixin MySql connection setup. 43 private enum MySqlConnectionSetup = q{ 44 auto useDbConnectionString = connectionString ? connectionString : super.connectionString; 45 46 auto pool = getPool(useDbConnectionString); 47 auto connection = pool.lockConnection(); 48 auto prepared = connection.prepare(query); 49 50 prepared.setArgs(params ? params : new DbParam[0]); 51 }; 52 53 /// Gets a mysql adapter based on a model. 54 MySqlAdapter!TModel getMySqlAdapter(TModel)(string connectionString = null) 55 { 56 return new MySqlAdapter!TModel(connectionString); 57 } 58 59 // Wrapper for an empty mysql model. 60 private class EmptyMySqlModel 61 { 62 import vibe.data.serialization : ignore; 63 import mysql; 64 65 @ignore static const string table = ""; 66 67 Row row; 68 void readModel() { } 69 } 70 71 /// Wrapper around a raw mysql adapter. 72 final class MySqlRawAdapter : MySqlAdapter!EmptyMySqlModel 73 { 74 public: 75 /** 76 * Creates a new mysql raw adapter. 77 * Params: 78 * connectionString = The connection string of the adapter. 79 */ 80 this(string connectionString = null) 81 { 82 super(connectionString); 83 } 84 } 85 86 /// Wrapper around a mysql adapter. 87 class MySqlAdapter(TModel) : SqlAdapter!TModel 88 { 89 public: 90 final: 91 /** 92 * Creates a new mysql adapter. 93 * Params: 94 * connectionString = The connection string of the adapter. 95 */ 96 this(string connectionString = null) 97 { 98 super(connectionString ? connectionString : dbConnectionString); 99 } 100 101 /** 102 * Executes an sql statement. 103 * Params: 104 * query = The sql query. 105 * params = The parameters. 106 * connectionString = The connection string. (If null, it will select the default) 107 * Returns: 108 * The amount of rows affected. 109 */ 110 override ulong execute(string query, DbParam[string] params, string connectionString = null) 111 { 112 mixin(MySqlConnectionNamedParametersSetup); 113 114 return connection.exec(prepared); 115 } 116 117 /** 118 * Executes a raw sql statement. 119 * Params: 120 * query = The sql query. 121 * params = The parameters. 122 * connectionString = The connection string. (If null, it will select the default) 123 * Returns: 124 * The amount of rows affected. 125 */ 126 override ulong executeRaw(string query, DbParam[] params, string connectionString = null) 127 { 128 mixin(MySqlConnectionSetup); 129 130 return connection.exec(prepared); 131 } 132 133 /** 134 * Validates whether a row is selected from the query or not. 135 * Params: 136 * query = The sql query. 137 * params = The parameters. 138 * connectionString = The connection string. (If null, it will select the default) 139 * Returns: 140 * True if the row exists, false otherwise. 141 */ 142 override bool exists(string query, DbParam[string] params, string connectionString = null) 143 { 144 auto rows = execute(query, params, connectionString); 145 146 return cast(bool)rows; 147 } 148 149 /** 150 * Validates whether a row is selected from the raw query or not. 151 * Params: 152 * query = The sql query. 153 * params = The parameters. 154 * connectionString = The connection string. (If null, it will select the default) 155 * Returns: 156 * True if the row exists, false otherwise. 157 */ 158 override bool existsRaw(string query, DbParam[] params, string connectionString = null) 159 { 160 auto rows = executeRaw(query, params, connectionString); 161 162 return cast(bool)rows; 163 } 164 165 /** 166 * Executes a multi sql read. 167 * Params: 168 * query = The sql query. 169 * params = The parameters. 170 * connectionString = The connection string. (If null, it will select the default) 171 * Returns: 172 * A range filled models with the rows returned by the sql read. 173 */ 174 override TModel[] readMany(string query, DbParam[string] params, string connectionString = null) 175 { 176 params["table"] = TModel.table; 177 178 mixin(MySqlConnectionNamedParametersSetup); 179 180 return connection.query(prepared).map!((row) 181 { 182 auto model = new TModel; 183 model.row = row; 184 model.readModel(); 185 return model; 186 }).array; 187 } 188 189 /** 190 * Executes a raw multi sql read. 191 * Params: 192 * query = The sql query. 193 * params = The parameters. 194 * connectionString = The connection string. (If null, it will select the default) 195 * Returns: 196 * A range filled models with the rows returned by the sql read. 197 */ 198 override TModel[] readManyRaw(string query, DbParam[] params, string connectionString = null) 199 { 200 mixin(MySqlConnectionSetup); 201 202 return connection.query(prepared).map!((row) 203 { 204 auto model = new TModel; 205 model.row = row; 206 model.readModel(); 207 return model; 208 }).array; 209 } 210 211 /** 212 * Executes a single sql read. 213 * Params: 214 * query = The sql query. 215 * params = The parameters. 216 * connectionString = The connection string. (If null, it will select the default) 217 * Returns: 218 * The model of the first row read. 219 */ 220 override TModel readSingle(string query, DbParam[string] params, string connectionString = null) 221 { 222 params["table"] = TModel.table; 223 224 mixin(MySqlConnectionNamedParametersSetup); 225 226 auto row = connection.queryRow(prepared); 227 228 if (row.isNull) 229 { 230 return TModel.init; 231 } 232 233 auto model = new TModel; 234 model.row = row.get; 235 model.readModel(); 236 return model; 237 } 238 239 /** 240 * Executes a single raw sql read. 241 * Params: 242 * query = The sql query. 243 * params = The parameters. 244 * connectionString = The connection string. (If null, it will select the default) 245 * Returns: 246 * The model of the first row read. 247 */ 248 override TModel readSingleRaw(string query, DbParam[] params, string connectionString = null) 249 { 250 mixin(MySqlConnectionSetup); 251 252 auto row = connection.queryRow(prepared); 253 254 if (row.isNull) 255 { 256 return TModel.init; 257 } 258 259 auto model = new TModel; 260 model.row = row.get; 261 model.readModel(); 262 return model; 263 } 264 265 protected: 266 /** 267 * Executes a scalar sql statement. 268 * Params: 269 * query = The sql query. 270 * params = The parameters. 271 * connectionString = The connection string. (If null, it will select the default) 272 * Returns: 273 * The value of the statement. 274 */ 275 override Variant scalarImpl(string query, DbParam[string] params, string connectionString = null) 276 { 277 mixin(MySqlConnectionNamedParametersSetup); 278 279 Variant variant = Variant.init; 280 auto value = connection.queryValue(prepared); 281 282 if (value.isNull) 283 { 284 return variant; 285 } 286 287 return value.get; 288 } 289 290 /** 291 * Executes a raw scalar sql statement. 292 * Params: 293 * query = The sql query. 294 * params = The parameters. 295 * connectionString = The connection string. (If null, it will select the default) 296 * Returns: 297 * The value of the statement. 298 */ 299 override Variant scalarRawImpl(string query, DbParam[] params, string connectionString = null) 300 { 301 mixin(MySqlConnectionSetup); 302 303 Variant variant = Variant.init; 304 auto value = connection.queryValue(prepared); 305 306 if (value.isNull) 307 { 308 return variant; 309 } 310 311 return value.get; 312 } 313 314 /** 315 * Executes a scalar insert sql statement. 316 * Params: 317 * query = The sql query. 318 * params = The parameters. 319 * connectionString = The connection string. (If null, it will select the default) 320 * Returns: 321 * The id of inserted row. 322 */ 323 override Variant scalarInsertImpl(string query, DbParam[string] params, string connectionString = null) 324 { 325 auto rows = execute(query, params, connectionString); 326 327 if (!rows) 328 { 329 return Variant.init; 330 } 331 332 static const idSql = "SELECT last_insert_id()"; 333 334 return scalarRawImpl(idSql, null, connectionString); 335 } 336 337 /** 338 * Executes a raw scalar insert sql statement. 339 * Params: 340 * query = The sql query. 341 * params = The parameters. 342 * connectionString = The connection string. (If null, it will select the default) 343 * Returns: 344 * The id of the inserted row. 345 */ 346 override Variant scalarInsertRawImpl(string query, DbParam[] params, string connectionString = null) 347 { 348 auto rows = executeRaw(query, params, connectionString); 349 350 if (!rows) 351 { 352 return Variant.init; 353 } 354 355 static const idSql = "SELECT last_insert_id()"; 356 357 return scalarRawImpl(idSql, null, connectionString); 358 } 359 }