Manager.hx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*
  2. * Copyright (c) 2005, The haXe Project Contributors
  3. * All rights reserved.
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * - Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * - Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
  17. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  20. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  23. * DAMAGE.
  24. */
  25. package sys.db;
  26. import Reflect;
  27. import sys.db.Connection;
  28. #if (!spod_macro && !doc_gen && !macro)
  29. #error "Please use -D spod_macro when using new SPOD version"
  30. #end
  31. /**
  32. SPOD Manager : the persistent object database manager. See the tutorial on
  33. haXe website to learn how to use SPOD.
  34. **/
  35. #if !macro @:build(sys.db.SpodMacros.addRtti()) #end
  36. class Manager<T : Object> {
  37. /* ----------------------------- STATICS ------------------------------ */
  38. public static var cnx(default, setConnection) : Connection;
  39. public static var lockMode : String;
  40. private static inline var cache_field = "__cache__";
  41. private static var object_cache : Hash<Object> = new Hash();
  42. private static var init_list : List<Manager<Dynamic>> = new List();
  43. private static var KEYWORDS = {
  44. var h = new Hash();
  45. 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("|") )
  46. h.set(k.toLowerCase(),true);
  47. h;
  48. }
  49. private static function setConnection( c : Connection ) {
  50. cnx = c;
  51. lockMode = (c != null && c.dbName() == "MySQL") ? " FOR UPDATE" : "";
  52. return c;
  53. }
  54. /* ---------------------------- BASIC API ----------------------------- */
  55. var table_infos : SpodInfos;
  56. var table_name : String;
  57. var table_keys : Array<String>;
  58. var class_proto : { prototype : Dynamic };
  59. public function new( classval : Class<T> ) {
  60. var m : Array<Dynamic> = haxe.rtti.Meta.getType(classval).rtti;
  61. if( m == null ) throw "Missing @rtti for class " + Type.getClassName(classval);
  62. table_infos = haxe.Unserializer.run(m[0]);
  63. table_name = quoteField(table_infos.name);
  64. table_keys = table_infos.key;
  65. // set the manager and ready for further init
  66. class_proto = cast classval;
  67. #if neko
  68. class_proto.prototype._manager = this;
  69. init_list.add(this);
  70. #end
  71. }
  72. public function all( ?lock: Bool ) : List<T> {
  73. return unsafeObjects("SELECT * FROM " + table_name,lock);
  74. }
  75. @:macro public function get(ethis,id,?lock:haxe.macro.Expr.ExprOf<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<T> #end {
  76. return SpodMacros.macroGet(ethis,id,lock);
  77. }
  78. @:macro public function select(ethis, cond, ?options, ?lock:haxe.macro.Expr.ExprOf<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<T> #end {
  79. return SpodMacros.macroSearch(ethis, cond, options, lock, true);
  80. }
  81. @:macro public function search(ethis, cond, ?options, ?lock:haxe.macro.Expr.ExprOf<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<List<T>> #end {
  82. return SpodMacros.macroSearch(ethis, cond, options, lock);
  83. }
  84. @:macro public function count(ethis, cond) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<Int> #end {
  85. return SpodMacros.macroCount(ethis, cond);
  86. }
  87. @:macro public function delete(ethis, cond, ?options) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprOf<Void> #end {
  88. return SpodMacros.macroDelete(ethis, cond, options);
  89. }
  90. public function dynamicSearch( x : {}, ?lock : Bool ) : List<T> {
  91. var s = new StringBuf();
  92. s.add("SELECT * FROM ");
  93. s.add(table_name);
  94. s.add(" WHERE ");
  95. addCondition(s,x);
  96. return unsafeObjects(s.toString(),lock);
  97. }
  98. function quote( s : String ) : String {
  99. return getCnx().quote( s );
  100. }
  101. /* -------------------------- SPODOBJECT API -------------------------- */
  102. function doInsert( x : T ) {
  103. unmake(x);
  104. var s = new StringBuf();
  105. var fields = new List();
  106. var values = new List();
  107. for( f in table_infos.fields ) {
  108. var name = f.name;
  109. var v = Reflect.field(x,name);
  110. if( v != null ) {
  111. fields.add(quoteField(name));
  112. values.add(v);
  113. } else if( !f.isNull ) {
  114. // if the field is not defined, give it a default value on insert
  115. switch( f.t ) {
  116. case DUInt, DTinyInt, DInt, DSingle, DFloat, DFlags(_), DBigInt, DTinyUInt, DSmallInt, DSmallUInt, DMediumInt, DMediumUInt:
  117. Reflect.setField(x, name, 0);
  118. case DBool:
  119. Reflect.setField(x, name, false);
  120. case DTinyText, DText, DString(_), DSmallText, DSerialized:
  121. Reflect.setField(x, name, "");
  122. case DSmallBinary, DNekoSerialized, DLongBinary, DBytes(_), DBinary:
  123. Reflect.setField(x, name, haxe.io.Bytes.alloc(0));
  124. case DDate, DDateTime, DTimeStamp:
  125. // default date might depend on database
  126. case DId, DUId, DBigId, DNull, DInterval, DEncoded:
  127. // no default value for these
  128. }
  129. }
  130. }
  131. s.add("INSERT INTO ");
  132. s.add(table_name);
  133. s.add(" (");
  134. s.add(fields.join(","));
  135. s.add(") VALUES (");
  136. var first = true;
  137. for( v in values ) {
  138. if( first )
  139. first = false;
  140. else
  141. s.add(", ");
  142. getCnx().addValue(s,v);
  143. }
  144. s.add(")");
  145. unsafeExecute(s.toString());
  146. untyped x._lock = true;
  147. // table with one key not defined : suppose autoincrement
  148. if( table_keys.length == 1 && Reflect.field(x,table_keys[0]) == null )
  149. Reflect.setField(x,table_keys[0],getCnx().lastInsertId());
  150. addToCache(x);
  151. }
  152. inline function isBinary( t : SpodInfos.SpodType ) {
  153. return switch( t ) {
  154. case DSmallBinary, DNekoSerialized, DLongBinary, DBytes(_), DBinary: true;
  155. default: false;
  156. };
  157. }
  158. inline function hasBinaryChanged( a : haxe.io.Bytes, b : haxe.io.Bytes ) {
  159. return a != b && (a == null || b == null || a.compare(b) != 0);
  160. }
  161. function doUpdate( x : T ) {
  162. if( untyped !x._lock )
  163. throw "Cannot update a not locked object";
  164. unmake(x);
  165. var s = new StringBuf();
  166. s.add("UPDATE ");
  167. s.add(table_name);
  168. s.add(" SET ");
  169. var cache = Reflect.field(x,cache_field);
  170. var mod = false;
  171. for( f in table_infos.fields ) {
  172. var name = f.name;
  173. var v : Dynamic = Reflect.field(x,name);
  174. var vc : Dynamic = Reflect.field(cache,name);
  175. if( v != vc && (!isBinary(f.t) || hasBinaryChanged(v,vc)) ) {
  176. if( mod )
  177. s.add(", ");
  178. else
  179. mod = true;
  180. s.add(quoteField(name));
  181. s.add(" = ");
  182. getCnx().addValue(s,v);
  183. Reflect.setField(cache,name,v);
  184. }
  185. }
  186. if( !mod )
  187. return;
  188. s.add(" WHERE ");
  189. addKeys(s,x);
  190. unsafeExecute(s.toString());
  191. }
  192. function doDelete( x : T ) {
  193. var s = new StringBuf();
  194. s.add("DELETE FROM ");
  195. s.add(table_name);
  196. s.add(" WHERE ");
  197. addKeys(s,x);
  198. unsafeExecute(s.toString());
  199. removeFromCache(x);
  200. }
  201. function doLock( i : T ) {
  202. if( untyped i._lock )
  203. return;
  204. var s = new StringBuf();
  205. s.add("SELECT * FROM ");
  206. s.add(table_name);
  207. s.add(" WHERE ");
  208. addKeys(s, i);
  209. // will force sync
  210. if( unsafeObject(s.toString(),true) != i )
  211. throw "Could not lock object (was deleted ?); try restarting transaction";
  212. }
  213. function objectToString( it : T ) : String {
  214. var s = new StringBuf();
  215. s.add(table_name);
  216. if( table_keys.length == 1 ) {
  217. s.add("#");
  218. s.add(Reflect.field(it,table_keys[0]));
  219. } else {
  220. s.add("(");
  221. var first = true;
  222. for( f in table_keys ) {
  223. if( first )
  224. first = false;
  225. else
  226. s.add(",");
  227. s.add(quoteField(f));
  228. s.add(":");
  229. s.add(Reflect.field(it,f));
  230. }
  231. s.add(")");
  232. }
  233. return s.toString();
  234. }
  235. /* ---------------------------- INTERNAL API -------------------------- */
  236. function cacheObject( x : T, lock : Bool ) {
  237. #if neko
  238. var o = untyped __dollar__new(x);
  239. untyped __dollar__objsetproto(o, class_proto.prototype);
  240. #else
  241. var o : T = Type.createEmptyInstance(cast class_proto);
  242. for( f in Reflect.fields(x) )
  243. Reflect.setField(o, f, Reflect.field(x, f));
  244. untyped o._manager = this;
  245. #end
  246. Reflect.setField(o,cache_field,x);
  247. addToCache(o);
  248. untyped o._lock = lock;
  249. return o;
  250. }
  251. function make( x : T ) {
  252. }
  253. function unmake( x : T ) {
  254. }
  255. function quoteField(f : String) {
  256. return KEYWORDS.exists(f.toLowerCase()) ? "`"+f+"`" : f;
  257. }
  258. function addKeys( s : StringBuf, x : {} ) {
  259. var first = true;
  260. for( k in table_keys ) {
  261. if( first )
  262. first = false;
  263. else
  264. s.add(" AND ");
  265. s.add(quoteField(k));
  266. s.add(" = ");
  267. var f = Reflect.field(x,k);
  268. if( f == null )
  269. throw ("Missing key "+k);
  270. getCnx().addValue(s,f);
  271. }
  272. }
  273. function unsafeExecute( sql : String ) {
  274. return getCnx().request(sql);
  275. }
  276. public function unsafeObject( sql : String, lock : Bool ) : T {
  277. if( lock != false ) {
  278. lock = true;
  279. sql += getLockMode();
  280. }
  281. var r = unsafeExecute(sql).next();
  282. if( r == null )
  283. return null;
  284. var c = getFromCache(r,lock);
  285. if( c != null )
  286. return c;
  287. r = cacheObject(r,lock);
  288. make(r);
  289. return r;
  290. }
  291. public function unsafeObjects( sql : String, lock : Bool ) : List<T> {
  292. if( lock != false ) {
  293. lock = true;
  294. sql += getLockMode();
  295. }
  296. var l = unsafeExecute(sql).results();
  297. var l2 = new List<T>();
  298. for( x in l ) {
  299. var c = getFromCache(x,lock);
  300. if( c != null )
  301. l2.add(c);
  302. else {
  303. x = cacheObject(x,lock);
  304. make(x);
  305. l2.add(x);
  306. }
  307. }
  308. return l2;
  309. }
  310. public function unsafeCount( sql : String ) {
  311. return unsafeExecute(sql).getIntResult(0);
  312. }
  313. public function unsafeDelete( sql : String ) {
  314. unsafeExecute(sql);
  315. }
  316. public function unsafeGet( id : Dynamic, ?lock : Bool ) : T {
  317. if( lock == null ) lock = true;
  318. if( table_keys.length != 1 )
  319. throw "Invalid number of keys";
  320. if( id == null )
  321. return null;
  322. var x : Dynamic = getFromCacheKey(Std.string(id) + table_name);
  323. if( x != null && (!lock || x._lock) )
  324. return x;
  325. var s = new StringBuf();
  326. s.add("SELECT * FROM ");
  327. s.add(table_name);
  328. s.add(" WHERE ");
  329. s.add(quoteField(table_keys[0]));
  330. s.add(" = ");
  331. getCnx().addValue(s,id);
  332. return unsafeObject(s.toString(), lock);
  333. }
  334. public function unsafeGetWithKeys( keys : { }, ?lock : Bool ) : T {
  335. if( lock == null ) lock = true;
  336. var x : Dynamic = getFromCacheKey(makeCacheKey(cast keys));
  337. if( x != null && (!lock || x._lock) )
  338. return x;
  339. var s = new StringBuf();
  340. s.add("SELECT * FROM ");
  341. s.add(table_name);
  342. s.add(" WHERE ");
  343. addKeys(s,keys);
  344. return unsafeObject(s.toString(),lock);
  345. }
  346. public function unsafeGetId( o : T ) : Dynamic {
  347. return o == null ? null : Reflect.field(o, table_keys[0]);
  348. }
  349. public static function nullCompare( a : String, b : String, eq : Bool ) {
  350. // we can't use a null-safe operator here
  351. if( cnx.dbName() != "MySQL" )
  352. return a + (eq ? " = " : " != ") + b;
  353. var sql = a+" <=> "+b;
  354. if( !eq ) sql = "NOT("+sql+")";
  355. return sql;
  356. }
  357. function addCondition(s : StringBuf,x) {
  358. var first = true;
  359. if( x != null )
  360. for( f in Reflect.fields(x) ) {
  361. if( first )
  362. first = false;
  363. else
  364. s.add(" AND ");
  365. s.add(quoteField(f));
  366. var d = Reflect.field(x,f);
  367. if( d == null )
  368. s.add(" IS NULL");
  369. else {
  370. s.add(" = ");
  371. getCnx().addValue(s,d);
  372. }
  373. }
  374. if( first )
  375. s.add("1");
  376. }
  377. /* --------------------------- MISC API ------------------------------ */
  378. public function dbClass() : Class<Dynamic> {
  379. return cast class_proto;
  380. }
  381. public function dbInfos() {
  382. return table_infos;
  383. }
  384. function getCnx() {
  385. return cnx;
  386. }
  387. function getLockMode() {
  388. return lockMode;
  389. }
  390. /**
  391. Remove the cached value for the given Object field : this will ensure
  392. that the value is updated when calling .update(). This is necessary if
  393. you are modifying binary data in-place since the cache will be modified
  394. as well.
  395. **/
  396. public function forceUpdate( o : T, field : String ) {
  397. // set a reference that will ensure != and .compare() != 0
  398. Reflect.setField(Reflect.field(o,cache_field),field,null);
  399. }
  400. /* --------------------------- INIT / CLEANUP ------------------------- */
  401. public static function initialize() {
  402. var l = init_list;
  403. init_list = new List();
  404. for( m in l )
  405. for( r in m.table_infos.relations )
  406. m.initRelation(r);
  407. }
  408. public static function cleanup() {
  409. object_cache = new Hash();
  410. }
  411. function initRelation( r : SpodInfos.SpodRelation ) {
  412. // setup getter/setter
  413. var spod : Dynamic = Type.resolveClass(r.type);
  414. if( spod == null ) throw "Missing spod type " + r.type;
  415. var manager : Manager<Dynamic> = spod.manager;
  416. var hprop = "__"+r.prop;
  417. var hkey = r.key;
  418. var lock = r.lock;
  419. if( manager == null || manager.table_keys == null ) throw ("Invalid manager for relation "+table_name+":"+r.prop);
  420. if( manager.table_keys.length != 1 ) throw ("Relation " + r.prop + "(" + r.key + ") on a multiple key table");
  421. Reflect.setField(class_proto.prototype,"get_"+r.prop,function() {
  422. var othis = untyped __this__;
  423. var f = Reflect.field(othis,hprop);
  424. if( f != null )
  425. return f;
  426. var id = Reflect.field(othis, hkey);
  427. if( id == null )
  428. return null;
  429. f = manager.unsafeGet(id,lock);
  430. // it's highly possible that in that case the object has been inserted
  431. // after we started our transaction : in that case, let's lock it, since
  432. // it's still better than returning 'null' while it exists
  433. if( f == null && id != null && !lock )
  434. f = manager.unsafeGet(id,true);
  435. Reflect.setField(othis,hprop,f);
  436. return f;
  437. });
  438. Reflect.setField(class_proto.prototype,"set_"+r.prop,function(f) {
  439. var othis = untyped __this__;
  440. Reflect.setField(othis,hprop,f);
  441. Reflect.setField(othis,hkey,Reflect.field(f,manager.table_keys[0]));
  442. return f;
  443. });
  444. }
  445. #if !neko
  446. function __get( x : Dynamic, prop : String, key : String, lock ) {
  447. var v = Reflect.field(x,prop);
  448. if( v != null )
  449. return v.value;
  450. var y = unsafeGet(Reflect.field(x, key), lock);
  451. Reflect.setField(x,prop,{ value : y });
  452. return y;
  453. }
  454. function __set( x : Dynamic, prop : String, key : String, v : T ) {
  455. Reflect.setField(x,prop,{ value : v });
  456. if( v == null )
  457. Reflect.setField(x,key,null);
  458. else
  459. Reflect.setField(x,key,Reflect.field(v,table_keys[0]));
  460. }
  461. #end
  462. /* ---------------------------- OBJECT CACHE -------------------------- */
  463. function makeCacheKey( x : T ) : String {
  464. if( table_keys.length == 1 ) {
  465. var k = Reflect.field(x,table_keys[0]);
  466. if( k == null )
  467. throw("Missing key "+table_keys[0]);
  468. return Std.string(k)+table_name;
  469. }
  470. var s = new StringBuf();
  471. for( k in table_keys ) {
  472. var v = Reflect.field(x,k);
  473. if( k == null )
  474. throw("Missing key "+k);
  475. s.add(v);
  476. s.add("#");
  477. }
  478. s.add(table_name);
  479. return s.toString();
  480. }
  481. function addToCache( x : T ) {
  482. object_cache.set(makeCacheKey(x),x);
  483. }
  484. function removeFromCache( x : T ) {
  485. object_cache.remove(makeCacheKey(x));
  486. }
  487. function getFromCacheKey( key : String ) : T {
  488. return cast object_cache.get(key);
  489. }
  490. function getFromCache( x : T, lock : Bool ) : T {
  491. var c : Dynamic = object_cache.get(makeCacheKey(x));
  492. if( c != null && lock && !c._lock ) {
  493. // synchronize the fields since our result is up-to-date !
  494. for( f in Reflect.fields(c) )
  495. Reflect.deleteField(c,f);
  496. for( f in Reflect.fields(x) )
  497. Reflect.setField(c,f,Reflect.field(x,f));
  498. // mark as locked
  499. c._lock = true;
  500. // restore our manager
  501. #if !neko
  502. untyped c._manager = this;
  503. #end
  504. // use the new object as our cache of fields
  505. Reflect.setField(c,cache_field,x);
  506. // remake object
  507. make(c);
  508. }
  509. return c;
  510. }
  511. /* ---------------------------- QUOTES -------------------------- */
  512. public static function quoteAny( v : Dynamic ) {
  513. var s = new StringBuf();
  514. cnx.addValue(s, v);
  515. return s.toString();
  516. }
  517. public static function quoteList( v : String, it : Iterable<Dynamic> ) {
  518. var b = new StringBuf();
  519. var first = true;
  520. if( it != null )
  521. for( v in it ) {
  522. if( first ) first = false else b.addChar(','.code);
  523. cnx.addValue(b, v);
  524. }
  525. if( first )
  526. return "FALSE";
  527. return v + " IN (" + b.toString() + ")";
  528. }
  529. }