Manager.hx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. /*
  2. * Copyright (C)2005-2017 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package sys.db;
  23. import Reflect;
  24. import sys.db.Connection;
  25. import sys.db.RecordInfos;
  26. /**
  27. Record Manager : the persistent object database manager. See the tutorial on
  28. Haxe website to learn how to use Record.
  29. **/
  30. #if !macro @:build(sys.db.RecordMacros.addRtti()) #end
  31. @:deprecated("This class will be removed soon, please install the record-macros library")
  32. class Manager<T : Object> {
  33. /* ----------------------------- STATICS ------------------------------ */
  34. public static var cnx(default, set) : Connection;
  35. public static var lockMode : String;
  36. private static inline var cache_field = "__cache__";
  37. private static var object_cache : haxe.ds.StringMap<Object> = new haxe.ds.StringMap();
  38. private static var init_list : List<Manager<Dynamic>> = new List();
  39. private static var KEYWORDS = {
  40. var h = new haxe.ds.StringMap();
  41. for( k in "ADD|ALL|ALTER|ANALYZE|AND|AS|ASC|ASENSITIVE|BEFORE|BETWEEN|BIGINT|BINARY|BLOB|BOTH|BY|CALL|CASCADE|CASE|CHANGE|CHAR|CHARACTER|CHECK|COLLATE|COLUMN|CONDITION|CONSTRAINT|CONTINUE|CONVERT|CREATE|CROSS|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DEC|DECIMAL|DECLARE|DEFAULT|DELAYED|DELETE|DESC|DESCRIBE|DETERMINISTIC|DISTINCT|DISTINCTROW|DIV|DOUBLE|DROP|DUAL|EACH|ELSE|ELSEIF|ENCLOSED|ESCAPED|EXISTS|EXIT|EXPLAIN|FALSE|FETCH|FLOAT|FLOAT4|FLOAT8|FOR|FORCE|FOREIGN|FROM|FULLTEXT|GRANT|GROUP|HAVING|HIGH_PRIORITY|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IF|IGNORE|IN|INDEX|INFILE|INNER|INOUT|INSENSITIVE|INSERT|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERVAL|INTO|IS|ITERATE|JOIN|KEY|KEYS|KILL|LEADING|LEAVE|LEFT|LIKE|LIMIT|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOW_PRIORITY|MATCH|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MIDDLEINT|MINUTE_MICROSECOND|MINUTE_SECOND|MOD|MODIFIES|NATURAL|NOT|NO_WRITE_TO_BINLOG|NULL|NUMERIC|ON|OPTIMIZE|OPTION|OPTIONALLY|OR|ORDER|OUT|OUTER|OUTFILE|PRECISION|PRIMARY|PROCEDURE|PURGE|READ|READS|REAL|REFERENCES|REGEXP|RELEASE|RENAME|REPEAT|REPLACE|REQUIRE|RESTRICT|RETURN|REVOKE|RIGHT|RLIKE|SCHEMA|SCHEMAS|SECOND_MICROSECOND|SELECT|SENSITIVE|SEPARATOR|SET|SHOW|SMALLINT|SONAME|SPATIAL|SPECIFIC|SQL|SQLEXCEPTION|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_CALC_FOUND_ROWS|SQL_SMALL_RESULT|SSL|STARTING|STRAIGHT_JOIN|TABLE|TERMINATED|THEN|TINYBLOB|TINYINT|TINYTEXT|TO|TRAILING|TRIGGER|TRUE|UNDO|UNION|UNIQUE|UNLOCK|UNSIGNED|UPDATE|USAGE|USE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|WHEN|WHERE|WHILE|WITH|WRITE|XOR|YEAR_MONTH|ZEROFILL|ASENSITIVE|CALL|CONDITION|CONNECTION|CONTINUE|CURSOR|DECLARE|DETERMINISTIC|EACH|ELSEIF|EXIT|FETCH|GOTO|INOUT|INSENSITIVE|ITERATE|LABEL|LEAVE|LOOP|MODIFIES|OUT|READS|RELEASE|REPEAT|RETURN|SCHEMA|SCHEMAS|SENSITIVE|SPECIFIC|SQL|SQLEXCEPTION|SQLSTATE|SQLWARNING|TRIGGER|UNDO|UPGRADE|WHILE".split("|") )
  42. h.set(k.toLowerCase(),true);
  43. h;
  44. }
  45. private static function set_cnx( c : Connection ) {
  46. cnx = c;
  47. lockMode = (c != null && c.dbName() == "MySQL") ? " FOR UPDATE" : "";
  48. return c;
  49. }
  50. /* ---------------------------- BASIC API ----------------------------- */
  51. var table_infos : RecordInfos;
  52. var table_name : String;
  53. var table_keys : Array<String>;
  54. var class_proto : { prototype : Dynamic };
  55. public function new( classval : Class<T> ) {
  56. var m : Array<Dynamic> = haxe.rtti.Meta.getType(classval).rtti;
  57. if( m == null ) throw "Missing @rtti for class " + Type.getClassName(classval);
  58. table_infos = haxe.Unserializer.run(m[0]);
  59. table_name = quoteField(table_infos.name);
  60. table_keys = table_infos.key;
  61. // set the manager and ready for further init
  62. class_proto = cast classval;
  63. #if neko
  64. class_proto.prototype._manager = this;
  65. init_list.add(this);
  66. #end
  67. }
  68. public function all( ?lock: Bool ) : List<T> {
  69. return unsafeObjects("SELECT * FROM " + table_name,lock);
  70. }
  71. public macro function get(ethis,id,?lock:haxe.macro.Expr.ExprOf<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<T> #end {
  72. return RecordMacros.macroGet(ethis,id,lock);
  73. }
  74. public macro function select(ethis, cond, ?options, ?lock:haxe.macro.Expr.ExprOf<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<T> #end {
  75. return RecordMacros.macroSearch(ethis, cond, options, lock, true);
  76. }
  77. public macro function search(ethis, cond, ?options, ?lock:haxe.macro.Expr.ExprOf<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<List<T>> #end {
  78. return RecordMacros.macroSearch(ethis, cond, options, lock);
  79. }
  80. public macro function count(ethis, cond) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<Int> #end {
  81. return RecordMacros.macroCount(ethis, cond);
  82. }
  83. public macro function delete(ethis, cond, ?options) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<Void> #end {
  84. return RecordMacros.macroDelete(ethis, cond, options);
  85. }
  86. public function dynamicSearch( x : {}, ?lock : Bool ) : List<T> {
  87. var s = new StringBuf();
  88. s.add("SELECT * FROM ");
  89. s.add(table_name);
  90. s.add(" WHERE ");
  91. addCondition(s,x);
  92. return unsafeObjects(s.toString(),lock);
  93. }
  94. function quote( s : String ) : String {
  95. return getCnx().quote( s );
  96. }
  97. /* -------------------------- RECORDOBJECT API -------------------------- */
  98. function doUpdateCache( x : T, name : String, v : Dynamic ) {
  99. var cache : { v : Dynamic } = Reflect.field(x, "cache_" + name);
  100. // if the cache has not been fetched (for instance if the field was set by reflection)
  101. // then we directly use the new value
  102. if( cache == null )
  103. return v;
  104. var v = doSerialize(name, cache.v);
  105. // don't set it since the value might change again later
  106. // Reflect.setField(x, name, v);
  107. return v;
  108. }
  109. static function getFieldName(field:RecordField):String
  110. {
  111. return switch (field.t) {
  112. case DData | DEnum(_):
  113. "data_" + field.name;
  114. case _:
  115. field.name;
  116. }
  117. }
  118. function doInsert( x : T ) {
  119. unmake(x);
  120. var s = new StringBuf();
  121. var fields = new List();
  122. var values = new List();
  123. var cache = Reflect.field(x,cache_field);
  124. if (cache == null)
  125. {
  126. Reflect.setField(x,cache_field,cache = {});
  127. }
  128. for( f in table_infos.fields ) {
  129. var name = f.name,
  130. fieldName = getFieldName(f);
  131. var v:Dynamic = Reflect.field(x,fieldName);
  132. if( v != null ) {
  133. fields.add(quoteField(name));
  134. switch( f.t ) {
  135. case DData: v = doUpdateCache(x, name, v);
  136. default:
  137. }
  138. values.add(v);
  139. } else if( !f.isNull ) {
  140. // if the field is not defined, give it a default value on insert
  141. switch( f.t ) {
  142. case DUInt, DTinyInt, DInt, DSingle, DFloat, DFlags(_), DBigInt, DTinyUInt, DSmallInt, DSmallUInt, DMediumInt, DMediumUInt, DEnum(_):
  143. Reflect.setField(x, fieldName, 0);
  144. case DBool:
  145. Reflect.setField(x, fieldName, false);
  146. case DTinyText, DText, DString(_), DSmallText, DSerialized:
  147. Reflect.setField(x, fieldName, "");
  148. case DSmallBinary, DNekoSerialized, DLongBinary, DBytes(_), DBinary:
  149. Reflect.setField(x, fieldName, haxe.io.Bytes.alloc(0));
  150. case DDate, DDateTime, DTimeStamp:
  151. // default date might depend on database
  152. case DId, DUId, DBigId, DNull, DInterval, DEncoded, DData:
  153. // no default value for these
  154. }
  155. }
  156. Reflect.setField(cache, name, v);
  157. }
  158. s.add("INSERT INTO ");
  159. s.add(table_name);
  160. if (fields.length > 0 || cnx.dbName() != "SQLite")
  161. {
  162. s.add(" (");
  163. s.add(fields.join(","));
  164. s.add(") VALUES (");
  165. var first = true;
  166. for( v in values ) {
  167. if( first )
  168. first = false;
  169. else
  170. s.add(", ");
  171. getCnx().addValue(s,v);
  172. }
  173. s.add(")");
  174. } else {
  175. s.add(" DEFAULT VALUES");
  176. }
  177. unsafeExecute(s.toString());
  178. untyped x._lock = true;
  179. // table with one key not defined : suppose autoincrement
  180. if( table_keys.length == 1 && Reflect.field(x,table_keys[0]) == null )
  181. Reflect.setField(x,table_keys[0],getCnx().lastInsertId());
  182. addToCache(x);
  183. }
  184. inline function isBinary( t : RecordInfos.RecordType ) {
  185. return switch( t ) {
  186. case DSmallBinary, DNekoSerialized, DLongBinary, DBytes(_), DBinary: true;
  187. //case DData: true // -- disabled for implementation purposes
  188. default: false;
  189. };
  190. }
  191. inline function hasBinaryChanged( a : haxe.io.Bytes, b : haxe.io.Bytes ) {
  192. return a != b && (a == null || b == null || a.compare(b) != 0);
  193. }
  194. function doUpdate( x : T ) {
  195. if( untyped !x._lock )
  196. throw "Cannot update a not locked object";
  197. var upd = getUpdateStatement(x);
  198. if (upd == null) return;
  199. unsafeExecute(upd);
  200. }
  201. function getUpdateStatement( x : T ):Null<String>
  202. {
  203. unmake(x);
  204. var s = new StringBuf();
  205. s.add("UPDATE ");
  206. s.add(table_name);
  207. s.add(" SET ");
  208. var cache = Reflect.field(x,cache_field);
  209. var mod = false;
  210. for( f in table_infos.fields ) {
  211. if (table_keys.indexOf(f.name) >= 0)
  212. continue;
  213. var name = f.name,
  214. fieldName = getFieldName(f);
  215. var v : Dynamic = Reflect.field(x,fieldName);
  216. var vc : Dynamic = Reflect.field(cache,name);
  217. if( cache == null || v != vc ) {
  218. switch( f.t ) {
  219. case DSmallBinary, DNekoSerialized, DLongBinary, DBytes(_), DBinary:
  220. if ( !hasBinaryChanged(v,vc) )
  221. continue;
  222. case DData:
  223. v = doUpdateCache(x, name, v);
  224. if( !hasBinaryChanged(v,vc) )
  225. continue;
  226. default:
  227. }
  228. if( mod )
  229. s.add(", ");
  230. else
  231. mod = true;
  232. s.add(quoteField(name));
  233. s.add(" = ");
  234. getCnx().addValue(s,v);
  235. if ( cache != null )
  236. Reflect.setField(cache,name,v);
  237. }
  238. }
  239. if( !mod )
  240. return null;
  241. s.add(" WHERE ");
  242. addKeys(s,x);
  243. return s.toString();
  244. }
  245. function doDelete( x : T ) {
  246. var s = new StringBuf();
  247. s.add("DELETE FROM ");
  248. s.add(table_name);
  249. s.add(" WHERE ");
  250. addKeys(s,x);
  251. unsafeExecute(s.toString());
  252. removeFromCache(x);
  253. }
  254. function doLock( i : T ) {
  255. if( untyped i._lock )
  256. return;
  257. var s = new StringBuf();
  258. s.add("SELECT * FROM ");
  259. s.add(table_name);
  260. s.add(" WHERE ");
  261. addKeys(s, i);
  262. // will force sync
  263. if( unsafeObject(s.toString(),true) != i )
  264. throw "Could not lock object (was deleted ?); try restarting transaction";
  265. }
  266. function objectToString( it : T ) : String {
  267. var s = new StringBuf();
  268. s.add(table_name);
  269. if( table_keys.length == 1 ) {
  270. s.add("#");
  271. s.add(Reflect.field(it,table_keys[0]));
  272. } else {
  273. s.add("(");
  274. var first = true;
  275. for( f in table_keys ) {
  276. if( first )
  277. first = false;
  278. else
  279. s.add(",");
  280. s.add(quoteField(f));
  281. s.add(":");
  282. s.add(Reflect.field(it,f));
  283. }
  284. s.add(")");
  285. }
  286. return s.toString();
  287. }
  288. function doSerialize( field : String, v : Dynamic ) : haxe.io.Bytes {
  289. var s = new haxe.Serializer();
  290. s.useEnumIndex = true;
  291. s.serialize(v);
  292. var str = s.toString();
  293. #if neko
  294. return neko.Lib.bytesReference(str);
  295. #else
  296. return haxe.io.Bytes.ofString(str);
  297. #end
  298. }
  299. function doUnserialize( field : String, b : haxe.io.Bytes ) : Dynamic {
  300. if( b == null )
  301. return null;
  302. var str;
  303. #if neko
  304. str = neko.Lib.stringReference(b);
  305. #else
  306. str = b.toString();
  307. #end
  308. if( str == "" )
  309. return null;
  310. return haxe.Unserializer.run(str);
  311. }
  312. /* ---------------------------- INTERNAL API -------------------------- */
  313. function normalizeCache(x:CacheType<T>)
  314. {
  315. for (f in Reflect.fields(x) )
  316. {
  317. var val:Dynamic = Reflect.field(x,f), info = table_infos.hfields.get(f);
  318. if (info != null)
  319. {
  320. if (val != null) switch (info.t) {
  321. case DDate, DDateTime if (!Std.is(val,Date)):
  322. if (Std.is(val,Float))
  323. {
  324. val = Date.fromTime(val);
  325. } else {
  326. var v = val + "";
  327. var index = v.indexOf('.');
  328. if (index >= 0)
  329. v = v.substr(0,index);
  330. val = Date.fromString(v);
  331. }
  332. case DSmallBinary, DLongBinary, DBinary, DBytes(_), DData if (Std.is(val, String)):
  333. val = haxe.io.Bytes.ofString(val);
  334. case DString(_) | DTinyText | DSmallText | DText if(!Std.is(val,String)):
  335. val = val + "";
  336. #if (cs && erase_generics)
  337. // on C#, SQLite Ints are returned as Int64
  338. case DInt if (!Std.is(val,Int)):
  339. val = cast(val,Int);
  340. #end
  341. case DBool if (!Std.is(val,Bool)):
  342. if (Std.is(val,Int))
  343. val = val != 0;
  344. else if (Std.is(val, String)) switch (val.toLowerCase()) {
  345. case "1", "true": val = true;
  346. case "0", "false": val = false;
  347. }
  348. case DFloat if (Std.is(val,String)):
  349. val = Std.parseFloat(val);
  350. case _:
  351. }
  352. Reflect.setField(x, f, val);
  353. }
  354. }
  355. }
  356. function cacheObject( x : T, lock : Bool ) {
  357. #if neko
  358. var o = untyped __dollar__new(x);
  359. untyped __dollar__objsetproto(o, class_proto.prototype);
  360. #else
  361. var o : T = Type.createEmptyInstance(cast class_proto);
  362. untyped o._manager = this;
  363. #end
  364. normalizeCache(x);
  365. for (f in Reflect.fields(x) )
  366. {
  367. var val:Dynamic = Reflect.field(x,f), info = table_infos.hfields.get(f);
  368. if (info != null)
  369. {
  370. var fieldName = getFieldName(info);
  371. Reflect.setField(o, fieldName, val);
  372. }
  373. }
  374. Reflect.setField(o,cache_field,x);
  375. addToCache(o);
  376. untyped o._lock = lock;
  377. return o;
  378. }
  379. function make( x : T ) {
  380. }
  381. function unmake( x : T ) {
  382. }
  383. function quoteField(f : String) {
  384. return KEYWORDS.exists(f.toLowerCase()) ? "`"+f+"`" : f;
  385. }
  386. function addKeys( s : StringBuf, x : {} ) {
  387. var first = true;
  388. for( k in table_keys ) {
  389. if( first )
  390. first = false;
  391. else
  392. s.add(" AND ");
  393. s.add(quoteField(k));
  394. s.add(" = ");
  395. var f = Reflect.field(x,k);
  396. if( f == null )
  397. throw ("Missing key "+k);
  398. getCnx().addValue(s,f);
  399. }
  400. }
  401. function unsafeExecute( sql : String ) {
  402. return getCnx().request(sql);
  403. }
  404. public function unsafeObject( sql : String, lock : Bool ) : T {
  405. if( lock != false ) {
  406. lock = true;
  407. sql += getLockMode();
  408. }
  409. var r = unsafeExecute(sql);
  410. var r = r.hasNext() ? r.next() : null;
  411. if( r == null )
  412. return null;
  413. normalizeCache(r);
  414. var c = getFromCache(r,lock);
  415. if( c != null )
  416. return c;
  417. r = cacheObject(r,lock);
  418. make(r);
  419. return r;
  420. }
  421. public function unsafeObjects( sql : String, lock : Bool ) : List<T> {
  422. if( lock != false ) {
  423. lock = true;
  424. sql += getLockMode();
  425. }
  426. var l = unsafeExecute(sql).results();
  427. var l2 = new List<T>();
  428. for( x in l ) {
  429. normalizeCache(x);
  430. var c = getFromCache(x,lock);
  431. if( c != null )
  432. l2.add(c);
  433. else {
  434. x = cacheObject(x,lock);
  435. make(x);
  436. l2.add(x);
  437. }
  438. }
  439. return l2;
  440. }
  441. public function unsafeCount( sql : String ) {
  442. return unsafeExecute(sql).getIntResult(0);
  443. }
  444. public function unsafeDelete( sql : String ) {
  445. unsafeExecute(sql);
  446. }
  447. public function unsafeGet( id : Dynamic, ?lock : Bool ) : T {
  448. if( lock == null ) lock = true;
  449. if( table_keys.length != 1 )
  450. throw "Invalid number of keys";
  451. if( id == null )
  452. return null;
  453. var x : Dynamic = getFromCacheKey(Std.string(id) + table_name);
  454. if( x != null && (!lock || x._lock) )
  455. return x;
  456. var s = new StringBuf();
  457. s.add("SELECT * FROM ");
  458. s.add(table_name);
  459. s.add(" WHERE ");
  460. s.add(quoteField(table_keys[0]));
  461. s.add(" = ");
  462. getCnx().addValue(s,id);
  463. return unsafeObject(s.toString(), lock);
  464. }
  465. public function unsafeGetWithKeys( keys : { }, ?lock : Bool ) : T {
  466. if( lock == null ) lock = true;
  467. var x : Dynamic = getFromCacheKey(makeCacheKey(cast keys));
  468. if( x != null && (!lock || x._lock) )
  469. return x;
  470. var s = new StringBuf();
  471. s.add("SELECT * FROM ");
  472. s.add(table_name);
  473. s.add(" WHERE ");
  474. addKeys(s,keys);
  475. return unsafeObject(s.toString(),lock);
  476. }
  477. public function unsafeGetId( o : T ) : Dynamic {
  478. return o == null ? null : Reflect.field(o, table_keys[0]);
  479. }
  480. public static function nullCompare( a : String, b : String, eq : Bool ) {
  481. if (a == null || a == 'NULL') {
  482. return eq ? '$b IS NULL' : '$b IS NOT NULL';
  483. } else if (b == null || b == 'NULL') {
  484. return eq ? '$a IS NULL' : '$a IS NOT NULL';
  485. }
  486. // we can't use a null-safe operator here
  487. if( cnx.dbName() != "MySQL" )
  488. return a + (eq ? " = " : " != ") + b;
  489. var sql = a+" <=> "+b;
  490. if( !eq ) sql = "NOT("+sql+")";
  491. return sql;
  492. }
  493. function addCondition(s : StringBuf,x) {
  494. var first = true;
  495. if( x != null )
  496. for( f in Reflect.fields(x) ) {
  497. if( first )
  498. first = false;
  499. else
  500. s.add(" AND ");
  501. s.add(quoteField(f));
  502. var d = Reflect.field(x,f);
  503. if( d == null )
  504. s.add(" IS NULL");
  505. else {
  506. s.add(" = ");
  507. getCnx().addValue(s,d);
  508. }
  509. }
  510. if( first )
  511. s.add("TRUE");
  512. }
  513. /* --------------------------- MISC API ------------------------------ */
  514. public function dbClass() : Class<Dynamic> {
  515. return cast class_proto;
  516. }
  517. public function dbInfos() {
  518. return table_infos;
  519. }
  520. function getCnx() {
  521. return cnx;
  522. }
  523. function getLockMode() {
  524. return lockMode;
  525. }
  526. /**
  527. Remove the cached value for the given Object field : this will ensure
  528. that the value is updated when calling .update(). This is necessary if
  529. you are modifying binary data in-place since the cache will be modified
  530. as well.
  531. **/
  532. public function forceUpdate( o : T, field : String ) {
  533. // set a reference that will ensure != and .compare() != 0
  534. Reflect.setField(Reflect.field(o,cache_field),field,null);
  535. }
  536. /* --------------------------- INIT / CLEANUP ------------------------- */
  537. public static function initialize() {
  538. var l = init_list;
  539. init_list = new List();
  540. for( m in l )
  541. for( r in m.table_infos.relations )
  542. m.initRelation(r);
  543. }
  544. public static function cleanup() {
  545. object_cache = new haxe.ds.StringMap();
  546. }
  547. function initRelation( r : RecordInfos.RecordRelation ) {
  548. // setup getter/setter
  549. var spod : Dynamic = Type.resolveClass(r.type);
  550. if( spod == null ) throw "Missing spod type " + r.type;
  551. var manager : Manager<Dynamic> = spod.manager;
  552. var hprop = "__"+r.prop;
  553. var hkey = r.key;
  554. var lock = r.lock;
  555. if( manager == null || manager.table_keys == null ) throw ("Invalid manager for relation "+table_name+":"+r.prop);
  556. if( manager.table_keys.length != 1 ) throw ("Relation " + r.prop + "(" + r.key + ") on a multiple key table");
  557. #if neko
  558. Reflect.setField(class_proto.prototype,"get_"+r.prop,function() {
  559. var othis = untyped __this__;
  560. var f = Reflect.field(othis,hprop);
  561. if( f != null )
  562. return f;
  563. var id = Reflect.field(othis, hkey);
  564. if( id == null )
  565. return null;
  566. f = manager.unsafeGet(id,lock);
  567. // it's highly possible that in that case the object has been inserted
  568. // after we started our transaction : in that case, let's lock it, since
  569. // it's still better than returning 'null' while it exists
  570. if( f == null && id != null && !lock )
  571. f = manager.unsafeGet(id,true);
  572. Reflect.setField(othis,hprop,f);
  573. return f;
  574. });
  575. Reflect.setField(class_proto.prototype,"set_"+r.prop,function(f) {
  576. var othis = untyped __this__;
  577. Reflect.setField(othis,hprop,f);
  578. Reflect.setField(othis,hkey,Reflect.field(f,manager.table_keys[0]));
  579. return f;
  580. });
  581. #end
  582. }
  583. #if !neko
  584. function __get( x : Dynamic, prop : String, key : String, lock ) {
  585. var v = Reflect.field(x,prop);
  586. if( v != null )
  587. return v;
  588. var y = unsafeGet(Reflect.field(x, key), lock);
  589. Reflect.setField(x,prop,v);
  590. return y;
  591. }
  592. function __set( x : Dynamic, prop : String, key : String, v : T ) {
  593. Reflect.setField(x,prop,v);
  594. if( v == null )
  595. Reflect.setField(x,key,null);
  596. else
  597. Reflect.setField(x,key,Reflect.field(v,table_keys[0]));
  598. return v;
  599. }
  600. #end
  601. /* ---------------------------- OBJECT CACHE -------------------------- */
  602. function makeCacheKey( x : T ) : String {
  603. if( table_keys.length == 1 ) {
  604. var k = Reflect.field(x,table_keys[0]);
  605. if( k == null )
  606. throw("Missing key "+table_keys[0]);
  607. return Std.string(k)+table_name;
  608. }
  609. var s = new StringBuf();
  610. for( k in table_keys ) {
  611. var v = Reflect.field(x,k);
  612. if( k == null )
  613. throw("Missing key "+k);
  614. s.add(v);
  615. s.add("#");
  616. }
  617. s.add(table_name);
  618. return s.toString();
  619. }
  620. function addToCache( x : CacheType<T> ) {
  621. object_cache.set(makeCacheKey(x),x);
  622. }
  623. function removeFromCache( x : CacheType<T> ) {
  624. object_cache.remove(makeCacheKey(x));
  625. }
  626. function getFromCacheKey( key : String ) : T {
  627. return cast object_cache.get(key);
  628. }
  629. function getFromCache( x : CacheType<T>, lock : Bool ) : T {
  630. var c : Dynamic = object_cache.get(makeCacheKey(x));
  631. if( c != null && lock && !c._lock ) {
  632. // synchronize the fields since our result is up-to-date !
  633. for( f in Reflect.fields(c) )
  634. Reflect.deleteField(c,f);
  635. for (f in table_infos.fields)
  636. {
  637. var name = f.name,
  638. fieldName = getFieldName(f);
  639. Reflect.setField(c,fieldName,Reflect.field(x,name));
  640. }
  641. // mark as locked
  642. c._lock = true;
  643. // restore our manager
  644. #if !neko
  645. untyped c._manager = this;
  646. #end
  647. // use the new object as our cache of fields
  648. Reflect.setField(c,cache_field,x);
  649. // remake object
  650. make(c);
  651. }
  652. return c;
  653. }
  654. /* ---------------------------- QUOTES -------------------------- */
  655. public static function quoteAny( v : Dynamic ) {
  656. if (v == null) {
  657. return 'NULL';
  658. }
  659. var s = new StringBuf();
  660. cnx.addValue(s, v);
  661. return s.toString();
  662. }
  663. public static function quoteList( v : String, it : Iterable<Dynamic> ) {
  664. var b = new StringBuf();
  665. var first = true;
  666. if( it != null )
  667. for( v in it ) {
  668. if( first ) first = false else b.addChar(','.code);
  669. cnx.addValue(b, v);
  670. }
  671. if( first )
  672. return "FALSE";
  673. return v + " IN (" + b.toString() + ")";
  674. }
  675. // We need Bytes.toString to not be DCE'd. See #1937
  676. @:keep static function __depends() { return haxe.io.Bytes.alloc(0).toString(); }
  677. }
  678. private typedef CacheType<T> = Dynamic;