1 /* 2 * Entity - Entity is an object-relational mapping tool for the D programming language. Referring to the design idea of JPA. 3 * 4 * Copyright (C) 2015-2018 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs.cn 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.entity.eql.EqlQuery; 13 14 import hunt.entity; 15 16 import hunt.entity.EntityMetaInfo; 17 import hunt.entity.eql.EqlParse; 18 import hunt.entity.eql.ResultDes; 19 import hunt.entity.eql.EqlInfo; 20 import hunt.entity.eql.EqlCache; 21 22 import hunt.sql; 23 import hunt.logging; 24 import hunt.collection; 25 import hunt.Long; 26 27 // version(WITH_HUNT_TRACE) 28 // { 29 // import hunt.trace.Constrants; 30 // import hunt.trace.Plugin; 31 // import hunt.trace.Span; 32 // } 33 34 import std.algorithm; 35 import std.conv; 36 import std.format; 37 import std.traits; 38 import std.string; 39 import std.variant; 40 41 42 43 class EqlQuery(T...) 44 { 45 alias executeUpdate = exec; 46 alias ResultObj = T[0]; 47 48 private EntityManager _manager; 49 private ResultDes!(ResultObj) _resultDes; 50 51 private EqlParse _eqlParser; 52 private string _eql; 53 private string _countEql; 54 private Pageable _pageable; 55 private string[] _isExtracted; 56 private long _offset = -1; 57 private long _limit = -1; 58 private int _lastInsertId = -1; 59 private int _affectRows = 0; 60 private EntityMetaInfo _entityInfo; 61 // private enum EntityMetaInfo _entityInfo = extractEntityInfo!ResultObj(); 62 63 version (WITH_HUNT_TRACE) 64 { 65 private Span _span; 66 private string[string] _tags; 67 } 68 69 this(string eql, EntityManager em) 70 { 71 _manager = em; 72 _resultDes = new ResultDes!(ResultObj)(em); 73 _eql = eql; 74 _entityInfo = ResultObj.metaInfo; // extractEntityInfo!ResultObj(); 75 parseEql(); 76 } 77 78 this(string query_eql, Pageable page, EntityManager em) 79 { 80 _manager = em; 81 _resultDes = new ResultDes!(ResultObj)(em); 82 _eql = query_eql; 83 _pageable = page; 84 parseEql(); 85 } 86 87 version (WITH_HUNT_TRACE) 88 { 89 private void beginTrace(string name) 90 { 91 _tags.clear(); 92 _span = traceSpanBefore(name); 93 } 94 95 private void endTrace(string error = null) 96 { 97 if (_span !is null) 98 { 99 _tags["eql"] = _eql; 100 traceSpanAfter(_span, _tags, error); 101 } 102 } 103 } 104 105 private void parseEql() 106 { 107 version (WITH_HUNT_TRACE) 108 { 109 beginTrace("EQL PARSE"); 110 scope (exit) 111 endTrace(); 112 } 113 DatabaseOption opt = _manager.getDbOption(); 114 if (opt.isMysql()) 115 { 116 _eqlParser = new EqlParse(_eql, _entityInfo, DBType.MYSQL.name); 117 } 118 else if (opt.isPgsql()) 119 { 120 _eqlParser = new EqlParse(_eql, _entityInfo, DBType.POSTGRESQL.name); 121 } 122 // else if (opt.isSqlite()) 123 // { 124 // _eqlParser = new EqlParse(_eql, DBType.SQLITE.name); 125 // } 126 else 127 { 128 throw new Exception("not support dbtype : %s".format(opt.schemeName())); 129 } 130 version(HUNT_SQL_DEBUG) { 131 tracef("Raw eql: %s", _eql); 132 } 133 134 foreach (ObjType; T) { 135 extractInfo!ObjType(); 136 } 137 138 auto parsedEql = eqlCache.get(_eql); 139 if (parsedEql is null) 140 { 141 _eqlParser.parse(); 142 143 eqlCache.put(_eql, _eqlParser.getParsedEql()); 144 } 145 else 146 { 147 version(HUNT_ENTITY_DEBUG) trace("EQL Cache Hit"); 148 _eqlParser.setParsedEql(parsedEql); 149 } 150 151 // _countEql = PagerUtils.count(_eqlParser.getNativeSql, _eqlParser.getDBType()); 152 } 153 154 private void extractInfo(ObjType)() 155 { 156 if (_isExtracted.canFind(ObjType.stringof)) 157 return; 158 _isExtracted ~= ObjType.stringof; 159 160 static if (isAggregateType!(ObjType)) // && hasUDA!(ObjType, Table) 161 { 162 EqlInfo!(ObjType) entInfo = new EqlInfo!(ObjType)(_manager); 163 164 _eqlParser.putFields(entInfo.getEntityClassName(), entInfo.getFields); 165 _eqlParser.putClsTbName(entInfo.getEntityClassName(), entInfo.getTableName()); 166 167 _eqlParser.putJoinCond(entInfo.getJoinConds()); 168 if (ObjType.stringof == ResultObj.stringof) 169 { 170 _resultDes.setFields(entInfo.getFields); 171 } 172 } 173 else 174 { 175 // throw new Exception(" not support type : " ~ ObjType.stringof); 176 } 177 178 foreach (memberName; __traits(derivedMembers, ObjType)) 179 { 180 static if (__traits(getProtection, __traits(getMember, ObjType, memberName)) == "public") 181 { 182 alias memType = typeof(__traits(getMember, ObjType, memberName)); 183 static if (is(memType == class)) 184 { 185 { 186 auto sub_en = new EqlInfo!(memType)(_manager); 187 _eqlParser.putFields(sub_en.getEntityClassName(), sub_en.getFields); 188 _eqlParser.putClsTbName(sub_en.getEntityClassName(), sub_en.getTableName()); 189 _eqlParser._objType[ObjType.stringof ~ "." ~ memberName] = sub_en.getEntityClassName(); 190 191 extractInfo!memType(); 192 } 193 } 194 else if (isArray!memType) 195 { 196 } 197 } 198 } 199 } 200 201 /** 202 idx: It starts from 1. 203 */ 204 public EqlQuery setParameter(R = string)(int idx, R param) 205 { 206 if (_eqlParser !is null) 207 { 208 _eqlParser.setParameter!R(idx, param); 209 } 210 return this; 211 } 212 213 public EqlQuery setParameter(R = string)(string idx, R param) 214 { 215 if (_eqlParser !is null) 216 { 217 _eqlParser.setParameter!R(idx, param); 218 } 219 return this; 220 } 221 222 public EqlQuery setMaxResults(long maxResult) 223 { 224 if (_pageable) 225 { 226 throw new Exception("This method is not supported!"); 227 } 228 _limit = maxResult; 229 return this; 230 } 231 232 public EqlQuery setFirstResult(long startPosition) 233 { 234 if (_pageable) 235 { 236 throw new Exception("This method is not supported!"); 237 } 238 _offset = startPosition; 239 return this; 240 } 241 242 public string getExecSql() 243 { 244 auto sql = strip(_eqlParser.getNativeSql()); 245 if (endsWith(sql, ";")) 246 sql = sql[0 .. $ - 1]; 247 if (_pageable) 248 { 249 sql ~= " limit " ~ to!string(_pageable.getPageSize()); 250 auto offset = _pageable.getOffset(); 251 if (offset > 0) 252 sql ~= " offset " ~ to!string(offset); 253 } 254 else if (_limit != -1) 255 { 256 sql ~= " limit " ~ to!string(_limit); 257 if (_offset != -1) 258 sql ~= " offset " ~ to!string(_offset); 259 } 260 return sql; 261 } 262 263 int exec() 264 { 265 auto sql = getExecSql(); 266 version (WITH_HUNT_TRACE) 267 { 268 beginTrace("EqlQuery exec"); 269 scope (exit) 270 { 271 _tags["sql"] = sql; 272 endTrace(); 273 } 274 } 275 276 Statement stmt = _manager.getSession().prepare(sql); 277 string autoIncrementKey = _entityInfo.autoIncrementKey; 278 int r = stmt.execute(autoIncrementKey); 279 280 _lastInsertId = stmt.lastInsertId(); 281 _affectRows = stmt.affectedRows(); 282 283 //TODO update 时 返回的row line count 为 0 284 return r; 285 } 286 287 public int lastInsertId() 288 { 289 return _lastInsertId; 290 } 291 292 public int affectedRows() 293 { 294 return _affectRows; 295 } 296 297 public ResultObj getSingleResult() 298 { 299 Object[] ret = _getResultList(); 300 if (ret.length == 0) 301 return null; 302 return cast(ResultObj)(ret[0]); 303 } 304 305 public ResultObj[] getResultList() 306 { 307 Object[] ret = _getResultList(); 308 if (ret.length == 0) 309 { 310 return null; 311 } 312 return cast(ResultObj[]) ret; 313 } 314 315 public Page!ResultObj getPageResult() 316 { 317 if (_pageable) { 318 _countEql = PagerUtils.count(_eqlParser.getNativeSql, _eqlParser.getDBType()); 319 version(HUNT_SQL_DEBUG) info(_countEql); 320 } else { 321 throw new Exception("please use 'createPageQuery'"); 322 } 323 324 auto res = getResultList(); 325 return new Page!ResultObj(res, _pageable, count(_countEql)); 326 } 327 328 private Object[] _getResultList() 329 { 330 auto sql = getExecSql(); 331 version (WITH_HUNT_TRACE) 332 { 333 beginTrace("EqlQuery _getResultList"); 334 scope (exit) 335 { 336 _tags["sql"] = sql; 337 endTrace(); 338 } 339 } 340 341 Object[] ret; 342 long count = -1; 343 Statement stmt = _manager.getSession().prepare(sql); 344 RowSet res = stmt.query(); 345 if(res is null) { 346 warning("The result of query is empty"); 347 return null; 348 } 349 350 Row[] rows; 351 foreach (value; res) 352 { 353 rows ~= value; 354 } 355 356 foreach (size_t k, Row v; rows) 357 { 358 try 359 { 360 version(HUNT_ENTITY_DEBUG) { 361 warningf("Deserializing row %d", k); 362 } 363 364 // FIXME: Needing refactor or cleanup -@zhangxueping at 2020-11-03T10:21:26+08:00 365 // The parameter of canInit can be set 366 ResultObj t = _resultDes.deSerialize!(ResultObj, true)(rows, count, cast(int) k, null); 367 if (t is null) 368 { 369 if (count != -1) 370 { 371 ret ~= new Long(count); 372 } 373 else 374 { 375 throw new EntityException("empty row data"); 376 } 377 } 378 ret ~= t; 379 } 380 catch (Exception e) 381 { 382 version(HUNT_SQL_DEBUG) warning(e); 383 else version(HUNT_DEBUG) warning(e.msg); 384 throw new EntityException(e.msg); 385 } 386 387 } 388 return ret; 389 } 390 391 public RowSet getNativeResult() 392 { 393 auto sql = getExecSql(); 394 version (WITH_HUNT_TRACE) 395 { 396 beginTrace("EqlQuery getNativeResult"); 397 scope (exit) 398 { 399 _tags["sql"] = sql; 400 endTrace(); 401 } 402 } 403 404 auto stmt = _manager.getSession().prepare(sql); 405 return stmt.query(); 406 } 407 408 private long count(string sql) 409 { 410 version (WITH_HUNT_TRACE) 411 { 412 beginTrace("EqlQuery count"); 413 scope (exit) 414 { 415 _tags["sql"] = sql; 416 endTrace(); 417 } 418 } 419 long total = 0; 420 auto stmt = _manager.getSession().prepare(sql); 421 RowSet res = stmt.query(); 422 foreach (Row row; res) 423 { 424 Variant v = row.getValue(0); 425 total = to!int(v.toString()); 426 } 427 return total; 428 } 429 }