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