Manager.hx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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 php.db;
  26. import Reflect;
  27. import php.db.Connection;
  28. /**
  29. SPOD Manager : the persistent object database manager. See the tutorial on
  30. haXe website to learn how to use SPOD.
  31. **/
  32. class Manager<T : Object> {
  33. /* ----------------------------- STATICS ------------------------------ */
  34. public static var cnx(default,setConnection) : Connection;
  35. private static var object_cache : Hash<Object> = new Hash();
  36. private static var cache_field = "__cache__";
  37. private static var FOR_UPDATE = "";
  38. public static var managers = new Hash<Manager<Dynamic>>();
  39. private static dynamic function setConnection( c : Connection ) {
  40. Reflect.setField(Manager,"cnx",c);
  41. if( c != null )
  42. FOR_UPDATE = if( c.dbName() == "MySQL" ) " FOR UPDATE" else "";
  43. return c;
  44. }
  45. /* ---------------------------- BASIC API ----------------------------- */
  46. var table_name : String;
  47. var table_fields : List<String>;
  48. var table_keys : Array<String>;
  49. var cls : Dynamic; //Class<php.db.Object>;
  50. public function new( classval : Class<php.db.Object> ) {
  51. cls = classval;
  52. var clname = Type.getClassName(cls);
  53. // get basic infos
  54. table_name = quoteField((cls.TABLE_NAME != null ) ? cls.TABLE_NAME : clname.split('.').pop());
  55. table_keys = if( cls.TABLE_IDS != null ) cls.TABLE_IDS else ["id"];
  56. // get the list of private fields
  57. var apriv : Array<String> = cls.PRIVATE_FIELDS;
  58. apriv = if( apriv == null ) new Array() else apriv.copy();
  59. apriv.push("__cache__");
  60. apriv.push("__noupdate__");
  61. apriv.push("__manager__");
  62. apriv.push("update");
  63. // get the proto fields not marked private (excluding methods)
  64. table_fields = new List();
  65. var stub = Type.createEmptyInstance(cls);
  66. var instance_fields = Type.getInstanceFields(cls);
  67. var scls = Type.getSuperClass(cls);
  68. while(scls != null) {
  69. for(remove in Type.getInstanceFields(scls))
  70. instance_fields.remove(remove);
  71. scls = Type.getSuperClass(scls);
  72. }
  73. for( f in instance_fields ) {
  74. var isfield = !Reflect.isFunction(Reflect.field(stub,f));
  75. if( isfield )
  76. for( f2 in apriv ) {
  77. if(f == f2 ) {
  78. isfield = false;
  79. break;
  80. }
  81. }
  82. if( isfield ) {
  83. table_fields.add(f);
  84. }
  85. }
  86. // set the manager and ready for further init
  87. managers.set(clname, this);
  88. var rl : Array<Dynamic>;
  89. try {
  90. rl = untyped cls.RELATIONS();
  91. } catch(e : Dynamic) { return; }
  92. for(r in rl) {
  93. // remove prop from precomputed table_fields
  94. // always add key to table fields (even if not declared)
  95. table_fields.remove(r.prop);
  96. table_fields.remove("get_" + r.prop);
  97. table_fields.remove("set_" + r.prop);
  98. table_fields.remove(r.key);
  99. table_fields.add(r.key);
  100. }
  101. }
  102. public function get( id : Int, ?lock : Bool ) : T {
  103. if( lock == null )
  104. lock = true;
  105. if( table_keys.length != 1 )
  106. throw "Invalid number of keys";
  107. if( id == null )
  108. return null;
  109. var x : Dynamic = untyped object_cache.get(id + table_name);
  110. if( x != null && (!lock || !x.__noupdate__) )
  111. return x;
  112. var s = new StringBuf();
  113. s.add("SELECT * FROM ");
  114. s.add(table_name);
  115. s.add(" WHERE ");
  116. s.add(quoteField(table_keys[0]));
  117. s.add(" = ");
  118. cnx.addValue(s,id);
  119. if( lock )
  120. s.add(FOR_UPDATE);
  121. return object(s.toString(),lock);
  122. }
  123. public function getWithKeys( keys : {}, ?lock : Bool ) : T {
  124. if( lock == null )
  125. lock = true;
  126. var x : Dynamic = getFromCache(untyped keys,false);
  127. if( x != null && (!lock || !x.__noupdate__) )
  128. return x;
  129. var s = new StringBuf();
  130. s.add("SELECT * FROM ");
  131. s.add(table_name);
  132. s.add(" WHERE ");
  133. addKeys(s,keys);
  134. if( lock )
  135. s.add(FOR_UPDATE);
  136. return object(s.toString(),lock);
  137. }
  138. public function delete( x : {} ) {
  139. var s = new StringBuf();
  140. s.add("DELETE FROM ");
  141. s.add(table_name);
  142. s.add(" WHERE ");
  143. addCondition(s,x);
  144. execute(s.toString());
  145. }
  146. public function search( x : {}, ?lock : Bool ) : List<T> {
  147. if( lock == null )
  148. lock = true;
  149. var s = new StringBuf();
  150. s.add("SELECT * FROM ");
  151. s.add(table_name);
  152. s.add(" WHERE ");
  153. addCondition(s,x);
  154. if( lock )
  155. s.add(FOR_UPDATE);
  156. return objects(s.toString(),lock);
  157. }
  158. function addCondition(s : StringBuf,x) {
  159. var first = true;
  160. if( x != null )
  161. for( f in Reflect.fields(x) ) {
  162. if( first )
  163. first = false;
  164. else
  165. s.add(" AND ");
  166. s.add(quoteField(f));
  167. var d = Reflect.field(x,f);
  168. if( d == null )
  169. s.add(" IS NULL");
  170. else {
  171. s.add(" = ");
  172. cnx.addValue(s,d);
  173. }
  174. }
  175. if( first )
  176. s.add("1");
  177. }
  178. public function all( ?lock: Bool ) : List<T> {
  179. if( lock == null )
  180. lock = true;
  181. return objects("SELECT * FROM " + table_name + if( lock ) FOR_UPDATE else "",lock);
  182. }
  183. public function count( ?x : {} ) : Int {
  184. var s = new StringBuf();
  185. s.add("SELECT COUNT(*) FROM ");
  186. s.add(table_name);
  187. s.add(" WHERE ");
  188. addCondition(s,x);
  189. return execute(s.toString()).getIntResult(0);
  190. }
  191. public function quote( s : String ) : String {
  192. return cnx.quote( s );
  193. }
  194. public function result( sql : String ) : Dynamic {
  195. return cnx.request(sql).next();
  196. }
  197. public function results<T>( sql : String ) : List<T> {
  198. return cast cnx.request(sql).results();
  199. }
  200. /* -------------------------- SPODOBJECT API -------------------------- */
  201. function doInsert( x : T ) {
  202. unmake(x);
  203. var s = new StringBuf();
  204. var fields = new List();
  205. var values = new List();
  206. for( f in table_fields ) {
  207. var v = Reflect.field(x,f);
  208. if( v != null ) {
  209. fields.add(quoteField(f));
  210. values.add(v);
  211. }
  212. }
  213. s.add("INSERT INTO ");
  214. s.add(table_name);
  215. s.add(" (");
  216. s.add(fields.join(","));
  217. s.add(") VALUES (");
  218. var first = true;
  219. for( v in values ) {
  220. if( first )
  221. first = false;
  222. else
  223. s.add(", ");
  224. cnx.addValue(s,v);
  225. }
  226. s.add(")");
  227. execute(s.toString());
  228. // table with one key not defined : suppose autoincrement
  229. if( table_keys.length == 1 && Reflect.field(x,table_keys[0]) == null )
  230. Reflect.setField(x,table_keys[0],cnx.lastInsertId());
  231. addToCache(x);
  232. }
  233. function doUpdate( x : T ) {
  234. unmake(x);
  235. var s = new StringBuf();
  236. s.add("UPDATE ");
  237. s.add(table_name);
  238. s.add(" SET ");
  239. var cache = Reflect.field(x, cache_field);
  240. if (null == cache)
  241. {
  242. cache = cacheObject(x, false);
  243. Reflect.setField(x, cache_field, cache);
  244. }
  245. var mod = false;
  246. for( f in table_fields ) {
  247. var v = Reflect.field(x,f);
  248. var vc = Reflect.field(cache,f);
  249. if( v != vc ) {
  250. if( mod )
  251. s.add(", ");
  252. else
  253. mod = true;
  254. s.add(quoteField(f));
  255. s.add(" = ");
  256. cnx.addValue(s,v);
  257. Reflect.setField(cache,f,v);
  258. }
  259. }
  260. if( !mod )
  261. return;
  262. s.add(" WHERE ");
  263. addKeys(s,x);
  264. execute(s.toString());
  265. }
  266. function doDelete( x : T ) {
  267. var s = new StringBuf();
  268. s.add("DELETE FROM ");
  269. s.add(table_name);
  270. s.add(" WHERE ");
  271. addKeys(s,x);
  272. execute(s.toString());
  273. }
  274. function doSync( i : T ) {
  275. object_cache.remove(makeCacheKey(i));
  276. var i2 = getWithKeys(i, untyped !i.__noupdate__);
  277. // delete all fields
  278. for( f in Reflect.fields(i) )
  279. Reflect.deleteField(i,f);
  280. // copy fields from new object
  281. for( f in Reflect.fields(i2) )
  282. Reflect.setField(i,f,Reflect.field(i2,f));
  283. // set same field-cache
  284. Reflect.setField(i,cache_field,Reflect.field(i2,cache_field));
  285. addToCache(i);
  286. }
  287. function objectToString( it : T ) : String {
  288. var s = new StringBuf();
  289. s.add(table_name);
  290. if( table_keys.length == 1 ) {
  291. s.add("#");
  292. s.add(Reflect.field(it,table_keys[0]));
  293. } else {
  294. s.add("(");
  295. var first = true;
  296. for( f in table_keys ) {
  297. if( first )
  298. first = false;
  299. else
  300. s.add(",");
  301. s.add(quoteField(f));
  302. s.add(":");
  303. s.add(Reflect.field(it,f));
  304. }
  305. s.add(")");
  306. }
  307. return s.toString();
  308. }
  309. /* ---------------------------- INTERNAL API -------------------------- */
  310. function cacheObject( x : T, lock : Bool ) {
  311. var o : T = Type.createEmptyInstance(cls);
  312. for(field in Reflect.fields(x)) {
  313. Reflect.setField(o, field, Reflect.field(x, field));
  314. }
  315. untyped o.__init_object();
  316. addToCache(o);
  317. Reflect.setField(o, cache_field, Type.createEmptyInstance(cls));
  318. if( !lock )
  319. untyped o.__noupdate__ = true;
  320. return o;
  321. }
  322. function make( x : T ) {
  323. }
  324. function unmake( x : T ) {
  325. }
  326. function quoteField(f : String) {
  327. var fsmall = f.toLowerCase();
  328. if( fsmall == "read" || fsmall == "desc" || fsmall == "out" || fsmall == "group" || fsmall == "version" || fsmall == "option" )
  329. return "`"+f+"`";
  330. return f;
  331. }
  332. function addKeys( s : StringBuf, x : {} ) {
  333. var first = true;
  334. for( k in table_keys ) {
  335. if( first )
  336. first = false;
  337. else
  338. s.add(" AND ");
  339. s.add(quoteField(k));
  340. s.add(" = ");
  341. var f = Reflect.field(x,k);
  342. if( f == null )
  343. throw ("Missing key "+k);
  344. cnx.addValue(s,f);
  345. }
  346. }
  347. function execute( sql : String ) {
  348. return cnx.request(sql);
  349. }
  350. function select( cond : String ) {
  351. var s = new StringBuf();
  352. s.add("SELECT * FROM ");
  353. s.add(table_name);
  354. s.add(" WHERE ");
  355. s.add(cond);
  356. s.add(FOR_UPDATE);
  357. return s.toString();
  358. }
  359. function selectReadOnly( cond : String ) {
  360. var s = new StringBuf();
  361. s.add("SELECT * FROM ");
  362. s.add(table_name);
  363. s.add(" WHERE ");
  364. s.add(cond);
  365. return s.toString();
  366. }
  367. public function object( sql : String, lock : Bool ) : T {
  368. var r = cnx.request(sql).next();
  369. if( r == null )
  370. return null;
  371. var c = getFromCache(r,lock);
  372. if( c != null )
  373. return c;
  374. var o = cacheObject(r,lock);
  375. make(o);
  376. return o;
  377. }
  378. public function objects( sql : String, lock : Bool ) : List<T> {
  379. var me = this;
  380. var l = cnx.request(sql).results();
  381. var l2 = new List<T>();
  382. for( x in l ) {
  383. var c = getFromCache(x,lock);
  384. if( c != null ) {
  385. l2.add(c);
  386. } else {
  387. var o = cacheObject(x,lock);
  388. make(o);
  389. l2.add(o);
  390. }
  391. }
  392. return l2;
  393. }
  394. public function dbClass() : Class<Dynamic> {
  395. return cls;
  396. }
  397. /* --------------------------- INIT / CLEANUP ------------------------- */
  398. /**
  399. * Left for compability with neko SPOD
  400. */
  401. public static function initialize() {
  402. }
  403. public static function cleanup() {
  404. object_cache = new Hash();
  405. }
  406. function initRelation(o : Dynamic, r : { prop : String, key : String, manager : Manager<Object>, lock : Bool } ) {
  407. // setup getter/setter
  408. var manager = r.manager;
  409. var hkey = r.key;
  410. var lock = r.lock;
  411. if( lock == null ) lock = true;
  412. if( manager == null || manager.table_keys == null ) throw ("Invalid manager for relation "+table_name+":"+r.prop);
  413. if( manager.table_keys.length != 1 ) throw ("Relation "+r.prop+"("+r.key+") on a multiple key table");
  414. Reflect.setField(o,"get_"+r.prop,function() {
  415. return manager.get(Reflect.field(o,hkey), lock);
  416. });
  417. Reflect.setField(o,"set_"+r.prop,function(f) {
  418. Reflect.setField(o, hkey, Reflect.field(f, manager.table_keys[0]));
  419. return f;
  420. });
  421. }
  422. /* ---------------------------- OBJECT CACHE -------------------------- */
  423. function makeCacheKey( x : T ) : String {
  424. if( table_keys.length == 1 ) {
  425. var k = Reflect.field(x,table_keys[0]);
  426. if( k == null )
  427. throw("Missing key "+table_keys[0]);
  428. return Std.string(k)+table_name;
  429. }
  430. var s = new StringBuf();
  431. for( k in table_keys ) {
  432. var v = Reflect.field(x,k);
  433. if( k == null )
  434. throw("Missing key "+k);
  435. s.add(v);
  436. s.add("#");
  437. }
  438. s.add(table_name);
  439. return s.toString();
  440. }
  441. function addToCache( x : T ) {
  442. object_cache.set(makeCacheKey(x),x);
  443. }
  444. function getFromCache( x : T, lock : Bool ) : T {
  445. var c : Dynamic = object_cache.get(makeCacheKey(x));
  446. // restore update method since now the object is locked
  447. if( c != null && lock && c.__noupdate__)
  448. c.__noupdate__ = false;
  449. return c;
  450. }
  451. }