Manager.hx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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_fields : List<String>;
  58. var table_keys : Array<String>;
  59. var class_proto : { prototype : Dynamic };
  60. public function new( classval : Class<T> ) {
  61. var m : Array<Dynamic> = haxe.rtti.Meta.getType(classval).rtti;
  62. if( m == null ) throw "Missing @rtti for class " + Type.getClassName(classval);
  63. table_infos = haxe.Unserializer.run(m[0]);
  64. table_name = quoteField(table_infos.name);
  65. table_keys = table_infos.key;
  66. table_fields = new List();
  67. for( f in table_infos.fields )
  68. table_fields.add(f.name);
  69. // set the manager and ready for further init
  70. class_proto = cast classval;
  71. #if neko
  72. class_proto.prototype._manager = this;
  73. init_list.add(this);
  74. #end
  75. }
  76. public function all( ?lock: Bool ) : List<T> {
  77. return unsafeObjects("SELECT * FROM " + table_name,lock);
  78. }
  79. @:macro public function get(ethis,id,?lock:haxe.macro.Expr.ExprRequire<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprRequire<T> #end {
  80. return SpodMacros.macroGet(ethis,id,lock);
  81. }
  82. @:macro public function select(ethis, cond, ?options, ?lock:haxe.macro.Expr.ExprRequire<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprRequire<T> #end {
  83. return SpodMacros.macroSearch(ethis, cond, options, lock, true);
  84. }
  85. @:macro public function search(ethis, cond, ?options, ?lock:haxe.macro.Expr.ExprRequire<Bool>) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprRequire<List<T>> #end {
  86. return SpodMacros.macroSearch(ethis, cond, options, lock);
  87. }
  88. @:macro public function count(ethis, cond) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprRequire<Int> #end {
  89. return SpodMacros.macroCount(ethis, cond);
  90. }
  91. @:macro public function delete(ethis, cond) : #if macro haxe.macro.Expr #else haxe.macro.Expr.ExprRequire<Void> #end {
  92. return SpodMacros.macroDelete(ethis, cond);
  93. }
  94. public function dynamicSearch( x : {}, ?lock : Bool ) : List<T> {
  95. var s = new StringBuf();
  96. s.add("SELECT * FROM ");
  97. s.add(table_name);
  98. s.add(" WHERE ");
  99. addCondition(s,x);
  100. return unsafeObjects(s.toString(),lock);
  101. }
  102. function quote( s : String ) : String {
  103. return getCnx().quote( s );
  104. }
  105. /* -------------------------- SPODOBJECT API -------------------------- */
  106. function doInsert( x : T ) {
  107. unmake(x);
  108. var s = new StringBuf();
  109. var fields = new List();
  110. var values = new List();
  111. var pos = 0;
  112. for( f in table_fields ) {
  113. var v = Reflect.field(x,f);
  114. if( v != null ) {
  115. fields.add(quoteField(f));
  116. values.add(v);
  117. } else {
  118. var inf = table_infos.fields[pos];
  119. // if the field is not defined, give it a default value on insert
  120. if( !inf.isNull )
  121. switch( inf.t ) {
  122. case DUInt, DTinyInt, DInt, DSingle, DFloat, DFlags(_), DBigInt:
  123. Reflect.setField(x, f, 0);
  124. case DBool:
  125. Reflect.setField(x, f, false);
  126. case DTinyText, DText, DString(_), DSmallText, DSerialized:
  127. Reflect.setField(x, f, "");
  128. case DSmallBinary, DNekoSerialized, DLongBinary, DBytes(_), DBinary:
  129. Reflect.setField(x, f, haxe.io.Bytes.alloc(0));
  130. case DDate, DDateTime, DTimeStamp:
  131. // default date might depend on database
  132. case DId, DUId, DBigId, DNull, DInterval, DEncoded:
  133. // no default value for these
  134. }
  135. }
  136. pos++;
  137. }
  138. s.add("INSERT INTO ");
  139. s.add(table_name);
  140. s.add(" (");
  141. s.add(fields.join(","));
  142. s.add(") VALUES (");
  143. var first = true;
  144. for( v in values ) {
  145. if( first )
  146. first = false;
  147. else
  148. s.add(", ");
  149. getCnx().addValue(s,v);
  150. }
  151. s.add(")");
  152. unsafeExecute(s.toString());
  153. untyped x._lock = true;
  154. // table with one key not defined : suppose autoincrement
  155. if( table_keys.length == 1 && Reflect.field(x,table_keys[0]) == null )
  156. Reflect.setField(x,table_keys[0],getCnx().lastInsertId());
  157. addToCache(x);
  158. }
  159. function doUpdate( x : T ) {
  160. if( untyped !x._lock )
  161. throw "Cannot update a not locked object";
  162. unmake(x);
  163. var s = new StringBuf();
  164. s.add("UPDATE ");
  165. s.add(table_name);
  166. s.add(" SET ");
  167. var cache = Reflect.field(x,cache_field);
  168. var mod = false;
  169. for( f in table_fields ) {
  170. var v = Reflect.field(x,f);
  171. var vc = Reflect.field(cache,f);
  172. if( v != vc ) {
  173. if( mod )
  174. s.add(", ");
  175. else
  176. mod = true;
  177. s.add(quoteField(f));
  178. s.add(" = ");
  179. getCnx().addValue(s,v);
  180. Reflect.setField(cache,f,v);
  181. }
  182. }
  183. if( !mod )
  184. return;
  185. s.add(" WHERE ");
  186. addKeys(s,x);
  187. unsafeExecute(s.toString());
  188. }
  189. function doDelete( x : T ) {
  190. var s = new StringBuf();
  191. s.add("DELETE FROM ");
  192. s.add(table_name);
  193. s.add(" WHERE ");
  194. addKeys(s,x);
  195. unsafeExecute(s.toString());
  196. removeFromCache(x);
  197. }
  198. function doLock( i : T ) {
  199. if( untyped i._lock )
  200. return;
  201. var s = new StringBuf();
  202. s.add("SELECT * FROM ");
  203. s.add(table_name);
  204. s.add(" WHERE ");
  205. addKeys(s, i);
  206. // will force sync
  207. unsafeObject(s.toString(),true);
  208. }
  209. function objectToString( it : T ) : String {
  210. var s = new StringBuf();
  211. s.add(table_name);
  212. if( table_keys.length == 1 ) {
  213. s.add("#");
  214. s.add(Reflect.field(it,table_keys[0]));
  215. } else {
  216. s.add("(");
  217. var first = true;
  218. for( f in table_keys ) {
  219. if( first )
  220. first = false;
  221. else
  222. s.add(",");
  223. s.add(quoteField(f));
  224. s.add(":");
  225. s.add(Reflect.field(it,f));
  226. }
  227. s.add(")");
  228. }
  229. return s.toString();
  230. }
  231. /* ---------------------------- INTERNAL API -------------------------- */
  232. function cacheObject( x : T, lock : Bool ) {
  233. #if neko
  234. var o = untyped __dollar__new(x);
  235. untyped __dollar__objsetproto(o, class_proto.prototype);
  236. #else
  237. var o : T = Type.createEmptyInstance(cast class_proto);
  238. for( f in Reflect.fields(x) )
  239. Reflect.setField(o, f, Reflect.field(x, f));
  240. untyped o._manager = this;
  241. #end
  242. Reflect.setField(o,cache_field,x);
  243. addToCache(o);
  244. untyped o._lock = lock;
  245. return o;
  246. }
  247. function make( x : T ) {
  248. }
  249. function unmake( x : T ) {
  250. }
  251. function quoteField(f : String) {
  252. return KEYWORDS.exists(f.toLowerCase()) ? "`"+f+"`" : f;
  253. }
  254. function addKeys( s : StringBuf, x : {} ) {
  255. var first = true;
  256. for( k in table_keys ) {
  257. if( first )
  258. first = false;
  259. else
  260. s.add(" AND ");
  261. s.add(quoteField(k));
  262. s.add(" = ");
  263. var f = Reflect.field(x,k);
  264. if( f == null )
  265. throw ("Missing key "+k);
  266. getCnx().addValue(s,f);
  267. }
  268. }
  269. function unsafeExecute( sql : String ) {
  270. return getCnx().request(sql);
  271. }
  272. public function unsafeObject( sql : String, lock : Bool ) : T {
  273. if( lock != false ) {
  274. lock = true;
  275. sql += getLockMode();
  276. }
  277. var r = unsafeExecute(sql).next();
  278. if( r == null )
  279. return null;
  280. var c = getFromCache(r,lock);
  281. if( c != null )
  282. return c;
  283. r = cacheObject(r,lock);
  284. make(r);
  285. return r;
  286. }
  287. public function unsafeObjects( sql : String, lock : Bool ) : List<T> {
  288. if( lock != false ) {
  289. lock = true;
  290. sql += getLockMode();
  291. }
  292. var l = unsafeExecute(sql).results();
  293. var l2 = new List<T>();
  294. for( x in l ) {
  295. var c = getFromCache(x,lock);
  296. if( c != null )
  297. l2.add(c);
  298. else {
  299. x = cacheObject(x,lock);
  300. make(x);
  301. l2.add(x);
  302. }
  303. }
  304. return l2;
  305. }
  306. public function unsafeCount( sql : String ) {
  307. return unsafeExecute(sql).getIntResult(0);
  308. }
  309. public function unsafeDelete( sql : String ) {
  310. unsafeExecute(sql);
  311. }
  312. public function unsafeGet( id : Dynamic, ?lock : Bool ) : T {
  313. if( lock == null ) lock = true;
  314. if( table_keys.length != 1 )
  315. throw "Invalid number of keys";
  316. if( id == null )
  317. return null;
  318. var x : Dynamic = getFromCacheKey(Std.string(id) + table_name);
  319. if( x != null && (!lock || x._lock) )
  320. return x;
  321. var s = new StringBuf();
  322. s.add("SELECT * FROM ");
  323. s.add(table_name);
  324. s.add(" WHERE ");
  325. s.add(quoteField(table_keys[0]));
  326. s.add(" = ");
  327. getCnx().addValue(s,id);
  328. return unsafeObject(s.toString(), lock);
  329. }
  330. public function unsafeGetWithKeys( keys : { }, ?lock : Bool ) : T {
  331. if( lock == null ) lock = true;
  332. var x : Dynamic = getFromCacheKey(makeCacheKey(cast keys));
  333. if( x != null && (!lock || x._lock) )
  334. return x;
  335. var s = new StringBuf();
  336. s.add("SELECT * FROM ");
  337. s.add(table_name);
  338. s.add(" WHERE ");
  339. addKeys(s,keys);
  340. return unsafeObject(s.toString(),lock);
  341. }
  342. public function unsafeGetId( o : T ) : Dynamic {
  343. return o == null ? null : Reflect.field(o, table_keys[0]);
  344. }
  345. function addCondition(s : StringBuf,x) {
  346. var first = true;
  347. if( x != null )
  348. for( f in Reflect.fields(x) ) {
  349. if( first )
  350. first = false;
  351. else
  352. s.add(" AND ");
  353. s.add(quoteField(f));
  354. var d = Reflect.field(x,f);
  355. if( d == null )
  356. s.add(" IS NULL");
  357. else {
  358. s.add(" = ");
  359. getCnx().addValue(s,d);
  360. }
  361. }
  362. if( first )
  363. s.add("1");
  364. }
  365. /* --------------------------- MISC API ------------------------------ */
  366. public function dbClass() : Class<Dynamic> {
  367. return cast class_proto;
  368. }
  369. public function dbInfos() {
  370. return table_infos;
  371. }
  372. function getCnx() {
  373. return cnx;
  374. }
  375. function getLockMode() {
  376. return lockMode;
  377. }
  378. /* --------------------------- INIT / CLEANUP ------------------------- */
  379. public static function initialize() {
  380. var l = init_list;
  381. init_list = new List();
  382. for( m in l )
  383. for( r in m.table_infos.relations )
  384. m.initRelation(r);
  385. }
  386. public static function cleanup() {
  387. object_cache = new Hash();
  388. }
  389. function initRelation( r : SpodInfos.SpodRelation ) {
  390. // setup getter/setter
  391. var spod : Dynamic = Type.resolveClass(r.type);
  392. if( spod == null ) throw "Missing spod type " + r.type;
  393. var manager : Manager<Dynamic> = spod.manager;
  394. var hprop = "__"+r.prop;
  395. var hkey = r.key;
  396. var lock = r.lock;
  397. if( manager == null || manager.table_keys == null ) throw ("Invalid manager for relation "+table_name+":"+r.prop);
  398. if( manager.table_keys.length != 1 ) throw ("Relation " + r.prop + "(" + r.key + ") on a multiple key table");
  399. Reflect.setField(class_proto.prototype,"get_"+r.prop,function() {
  400. var othis = untyped __this__;
  401. var f = Reflect.field(othis,hprop);
  402. if( f != null )
  403. return f;
  404. var id = Reflect.field(othis, hkey);
  405. if( id == null )
  406. return null;
  407. f = manager.unsafeGet(id,lock);
  408. // it's highly possible that in that case the object has been inserted
  409. // after we started our transaction : in that case, let's lock it, since
  410. // it's still better than returning 'null' while it exists
  411. if( f == null && id != null && !lock )
  412. f = manager.unsafeGet(id,true);
  413. Reflect.setField(othis,hprop,f);
  414. return f;
  415. });
  416. Reflect.setField(class_proto.prototype,"set_"+r.prop,function(f) {
  417. var othis = untyped __this__;
  418. Reflect.setField(othis,hprop,f);
  419. Reflect.setField(othis,hkey,Reflect.field(f,manager.table_keys[0]));
  420. return f;
  421. });
  422. }
  423. #if !neko
  424. function __get( x : Dynamic, prop : String, key : String, lock ) {
  425. var v = Reflect.field(x,prop);
  426. if( v != null )
  427. return v.value;
  428. var x = unsafeGet(Reflect.field(x, key), lock);
  429. Reflect.setField(x,prop,{ value : x });
  430. return x;
  431. }
  432. function __set( x : Dynamic, prop : String, key : String, v : T ) {
  433. Reflect.setField(x,prop,{ value : v });
  434. if( v == null )
  435. Reflect.setField(x,key,null);
  436. else
  437. Reflect.setField(x,key,Reflect.field(v,table_keys[0]));
  438. }
  439. #end
  440. /* ---------------------------- OBJECT CACHE -------------------------- */
  441. function makeCacheKey( x : T ) : String {
  442. if( table_keys.length == 1 ) {
  443. var k = Reflect.field(x,table_keys[0]);
  444. if( k == null )
  445. throw("Missing key "+table_keys[0]);
  446. return Std.string(k)+table_name;
  447. }
  448. var s = new StringBuf();
  449. for( k in table_keys ) {
  450. var v = Reflect.field(x,k);
  451. if( k == null )
  452. throw("Missing key "+k);
  453. s.add(v);
  454. s.add("#");
  455. }
  456. s.add(table_name);
  457. return s.toString();
  458. }
  459. function addToCache( x : T ) {
  460. object_cache.set(makeCacheKey(x),x);
  461. }
  462. function removeFromCache( x : T ) {
  463. object_cache.remove(makeCacheKey(x));
  464. }
  465. function getFromCacheKey( key : String ) : T {
  466. return cast object_cache.get(key);
  467. }
  468. function getFromCache( x : T, lock : Bool ) : T {
  469. var c : Dynamic = object_cache.get(makeCacheKey(x));
  470. if( c != null && lock && !c._lock ) {
  471. // synchronize the fields since our result is up-to-date !
  472. for( f in Reflect.fields(c) )
  473. Reflect.deleteField(c,f);
  474. for( f in Reflect.fields(x) )
  475. Reflect.setField(c,f,Reflect.field(x,f));
  476. // mark as locked
  477. c._lock = true;
  478. // use the new object as our cache of fields
  479. Reflect.setField(c,cache_field,x);
  480. // remake object
  481. make(c);
  482. }
  483. return c;
  484. }
  485. /* ---------------------------- QUOTES -------------------------- */
  486. public static function quoteAny( v : Dynamic ) {
  487. var s = new StringBuf();
  488. cnx.addValue(s, v);
  489. return s.toString();
  490. }
  491. public static function quoteList( v : String, it : Iterable<Dynamic> ) {
  492. var b = new StringBuf();
  493. var first = true;
  494. if( it != null )
  495. for( v in it ) {
  496. if( first ) first = false else b.addChar(','.code);
  497. cnx.addValue(b, v);
  498. }
  499. if( first )
  500. return "FALSE";
  501. return v + " IN (" + b.toString() + ")";
  502. }
  503. }