SpodMacros.hx 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. /*
  2. * Copyright (c) 2005-2011, 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 sys.db.SpodInfos;
  27. import haxe.macro.Expr;
  28. import haxe.macro.Type.VarAccess;
  29. #if macro
  30. import haxe.macro.Context;
  31. #end
  32. private typedef SqlFunction = {
  33. var name : String;
  34. var params : Array<SpodType>;
  35. var ret : SpodType;
  36. var sql : String;
  37. }
  38. class SpodMacros {
  39. static var GLOBAL = null;
  40. static var simpleString = ~/^[A-Za-z0-9 ]*$/;
  41. var isNull : Bool;
  42. var manager : Expr;
  43. var inf : SpodInfos;
  44. var g : {
  45. var cache : Hash<SpodInfos>;
  46. var types : Hash<SpodType>;
  47. var functions : Hash<SqlFunction>;
  48. };
  49. function new(c) {
  50. if( GLOBAL != null )
  51. g = GLOBAL;
  52. else {
  53. g = initGlobals();
  54. GLOBAL = g;
  55. }
  56. inf = getSpodInfos(c);
  57. }
  58. function initGlobals()
  59. {
  60. var cache = new Hash();
  61. var types = new Hash();
  62. for( c in Type.getEnumConstructs(SpodType) ) {
  63. var e : Dynamic = Reflect.field(SpodType, c);
  64. if( Std.is(e, SpodType) )
  65. types.set("S"+c.substr(1), e);
  66. }
  67. types.remove("SNull");
  68. var functions = new Hash();
  69. for( f in [
  70. { name : "now", params : [], ret : DDateTime, sql : "NOW($)" },
  71. { name : "curDate", params : [], ret : DDate, sql : "CURDATE($)" },
  72. { name : "seconds", params : [DFloat], ret : DInterval, sql : "INTERVAL $ SECOND" },
  73. { name : "minutes", params : [DFloat], ret : DInterval, sql : "INTERVAL $ MINUTE" },
  74. { name : "hours", params : [DFloat], ret : DInterval, sql : "INTERVAL $ HOUR" },
  75. { name : "days", params : [DFloat], ret : DInterval, sql : "INTERVAL $ DAY" },
  76. { name : "months", params : [DFloat], ret : DInterval, sql : "INTERVAL $ MONTH" },
  77. { name : "years", params : [DFloat], ret : DInterval, sql : "INTERVAL $ YEAR" },
  78. { name : "date", params : [DDateTime], ret : DDate, sql : "DATE($)" },
  79. ])
  80. functions.set(f.name, f);
  81. return { cache : cache, types : types, functions : functions };
  82. }
  83. public dynamic function error( msg : String, pos : Position ) : Dynamic {
  84. #if macro
  85. Context.error(msg, pos);
  86. #else
  87. throw msg;
  88. #end
  89. return null;
  90. }
  91. public dynamic function typeof( e : Expr ) : haxe.macro.Type {
  92. #if macro
  93. return Context.typeof(e);
  94. #else
  95. throw "not implemented";
  96. return null;
  97. #end
  98. }
  99. public dynamic function follow( t : haxe.macro.Type ) : haxe.macro.Type {
  100. #if macro
  101. return Context.follow(t);
  102. #else
  103. throw "not implemented";
  104. return null;
  105. #end
  106. }
  107. public dynamic function getManager( t : haxe.macro.Type, p : Position ) : SpodMacros {
  108. #if macro
  109. return getManagerInfos(t, p);
  110. #else
  111. throw "not implemented";
  112. return null;
  113. #end
  114. }
  115. function makeInt( t : haxe.macro.Type ) {
  116. switch( t ) {
  117. case TInst(c, _):
  118. var name = c.toString();
  119. if( name.charAt(0) == "I" )
  120. return Std.parseInt(name.substr(1));
  121. default:
  122. }
  123. throw "Unsupported " + Std.string(t);
  124. }
  125. function makeSpod( t : haxe.macro.Type ) {
  126. switch( t ) {
  127. case TInst(c, p):
  128. var name = c.toString();
  129. var cl = c.get();
  130. var csup = cl.superClass;
  131. while( csup != null ) {
  132. if( csup.t.toString() == "sys.db.Object" )
  133. return name;
  134. csup = csup.t.get().superClass;
  135. }
  136. case TType(t, p):
  137. var name = t.toString();
  138. if( p.length == 1 && (name == "Null" || name == "sys.db.SNull") ) {
  139. isNull = true;
  140. return makeSpod(p[0]);
  141. }
  142. default:
  143. }
  144. return null;
  145. }
  146. function getFlags( t : haxe.macro.Type ) {
  147. switch( t ) {
  148. case TEnum(e,_):
  149. var cl = e.get().names;
  150. if( cl.length > 1 ) {
  151. var prefix = cl[0];
  152. for( c in cl )
  153. while( prefix.length > 0 && c.substr(0, prefix.length) != prefix )
  154. prefix = prefix.substr(0, -1);
  155. for( i in 0...cl.length )
  156. cl[i] = cl[i].substr(prefix.length);
  157. }
  158. if( cl.length > 31 ) throw "Too many flags";
  159. return cl;
  160. default:
  161. throw "Flags parameter should be an enum";
  162. }
  163. }
  164. function makeType( t : haxe.macro.Type ) {
  165. switch( t ) {
  166. case TInst(c, p):
  167. var name = c.toString();
  168. return switch( name ) {
  169. case "Int": DInt;
  170. case "Float": DFloat;
  171. case "String": DText;
  172. case "Date": DDateTime;
  173. case "haxe.io.Bytes": DBinary;
  174. default: throw "Unsupported " + name;
  175. }
  176. case TEnum(e, p):
  177. var name = e.toString();
  178. return switch( name ) {
  179. case "Bool": DBool;
  180. default: throw "Unsupported " + name;
  181. }
  182. case TType(t, p):
  183. var name = t.toString();
  184. if( StringTools.startsWith(name, "sys.db.") )
  185. name = name.substr(7);
  186. var k = g.types.get(name);
  187. if( k != null ) return k;
  188. if( p.length == 1 )
  189. switch( name ) {
  190. case "SString": return DString(makeInt(p[0]));
  191. case "SBytes": return DBytes(makeInt(p[0]));
  192. case "SNull", "Null": isNull = true; return makeType(p[0]);
  193. case "SFlags": return DFlags(getFlags(p[0]),false);
  194. case "SSmallFlags": return DFlags(getFlags(p[0]),true);
  195. default:
  196. }
  197. throw "Unsupported " + name;
  198. default:
  199. }
  200. throw "Unsupported " + Std.string(t);
  201. }
  202. function makeIdent( e : Expr ) {
  203. return switch( e.expr ) {
  204. case EConst(c):
  205. switch( c ) {
  206. #if haxe3
  207. case CIdent(s): s;
  208. #else
  209. case CIdent(s), CType(s): s;
  210. #end
  211. default: error("Identifier expected", e.pos);
  212. }
  213. default: error("Identifier expected", e.pos);
  214. }
  215. }
  216. function getSpodInfos( c : haxe.macro.Type.Ref<haxe.macro.Type.ClassType> ) : SpodInfos {
  217. var cname = c.toString();
  218. var i = g.cache.get(cname);
  219. if( i != null ) return i;
  220. i = {
  221. key : null,
  222. name : cname.split(".").pop(), // remove package name
  223. fields : [],
  224. hfields : new Hash(),
  225. relations : [],
  226. indexes : [],
  227. };
  228. var c = c.get();
  229. var fieldsPos = new Hash();
  230. var fields = c.fields.get();
  231. var csup = c.superClass;
  232. while( csup != null ) {
  233. var c = csup.t.get();
  234. if( !c.meta.has(":skipFields") )
  235. fields = c.fields.get().concat(fields);
  236. csup = c.superClass;
  237. }
  238. for( f in fields ) {
  239. fieldsPos.set(f.name, f.pos);
  240. switch( f.kind ) {
  241. case FMethod(_):
  242. // skip methods
  243. continue;
  244. case FVar(g, s):
  245. // skip not-db fields
  246. if( f.meta.has(":skip") )
  247. continue;
  248. // handle relations
  249. if( f.meta.has(":relation") ) {
  250. if( !Type.enumEq(g,AccCall("get_" + f.name)) || !Type.enumEq(s,AccCall("set_" + f.name)) )
  251. error("Relation should be (dynamic,dynamic)", f.pos);
  252. for( m in f.meta.get() ) {
  253. if( m.name != ":relation" ) continue;
  254. if( m.params.length == 0 ) error("Missing relation key", m.pos);
  255. var params = [];
  256. for( p in m.params )
  257. params.push({ i : makeIdent(p), p : p.pos });
  258. isNull = false;
  259. var t = makeSpod(f.type);
  260. if( t == null ) error("Relation type should be a sys.db.Object", f.pos);
  261. var r = {
  262. prop : f.name,
  263. key : params.shift().i,
  264. type : t,
  265. cascade : false,
  266. lock : false,
  267. isNull : isNull,
  268. };
  269. // setup flags
  270. for( p in params )
  271. switch( p.i ) {
  272. case "lock": r.lock = true;
  273. case "cascade": r.cascade = true;
  274. default: error("Unknown relation flag", p.p);
  275. }
  276. i.relations.push(r);
  277. }
  278. continue;
  279. }
  280. switch( g ) {
  281. case AccCall(_):
  282. error("Relation should be defined with @:relation(key)", f.pos);
  283. default:
  284. }
  285. }
  286. isNull = false;
  287. var fi = {
  288. name : f.name,
  289. t : try makeType(f.type) catch( e : String ) error(e,f.pos),
  290. isNull : isNull,
  291. };
  292. var isId = switch( fi.t ) {
  293. case DId, DUId: true;
  294. default: fi.name == "id";
  295. }
  296. if( isId ) {
  297. if( i.key == null ) i.key = [fi.name] else error("Multiple table id declaration", f.pos);
  298. }
  299. i.fields.push(fi);
  300. i.hfields.set(fi.name, fi);
  301. }
  302. // create fields for undeclared relations keys :
  303. for( r in i.relations ) {
  304. var f = i.hfields.get(r.key);
  305. if( f == null ) {
  306. f = {
  307. name : r.key,
  308. t : DInt,
  309. isNull : r.isNull,
  310. };
  311. i.fields.push(f);
  312. i.hfields.set(f.name, f);
  313. } else {
  314. var pos = fieldsPos.get(f.name);
  315. if( f.t != DInt ) error("Relation key should be SInt", pos);
  316. if( f.isNull != r.isNull ) error("Relation and field should have same nullability", pos);
  317. }
  318. }
  319. // process class metadata
  320. for( m in c.meta.get() )
  321. switch( m.name ) {
  322. case ":id":
  323. i.key = [];
  324. for( p in m.params ) {
  325. var id = makeIdent(p);
  326. if( !i.hfields.exists(id) )
  327. error("This field does not exists", p.pos);
  328. i.key.push(id);
  329. }
  330. if( i.key.length == 0 ) error("Invalid :id", m.pos);
  331. case ":index":
  332. var idx = [];
  333. for( p in m.params ) idx.push(makeIdent(p));
  334. var unique = idx[idx.length - 1] == "unique";
  335. if( unique ) idx.pop();
  336. if( idx.length == 0 ) error("Invalid :index", m.pos);
  337. for( k in 0...idx.length )
  338. if( !i.hfields.exists(idx[k]) )
  339. error("This field does not exists", m.params[k].pos);
  340. i.indexes.push( { keys : idx, unique : unique } );
  341. case ":table":
  342. if( m.params.length != 1 ) error("Invalid :table", m.pos);
  343. i.name = switch( m.params[0].expr ) {
  344. case EConst(c): switch( c ) { case CString(s): s; default: null; }
  345. default: null;
  346. };
  347. if( i.name == null ) error("Invalid :table value", m.params[0].pos);
  348. default:
  349. }
  350. // check primary key defined
  351. if( i.key == null )
  352. error("Table is missing unique id, use either SId or @:id", c.pos);
  353. g.cache.set(cname, i);
  354. return i;
  355. }
  356. function quoteField( f : String ) {
  357. var m : { private var KEYWORDS : Hash<Bool>; } = Manager;
  358. return m.KEYWORDS.exists(f.toLowerCase()) ? "`"+f+"`" : f;
  359. }
  360. function initManager( pos : Position ) {
  361. manager = { expr : #if haxe3 EField #else EType #end({ expr : EField({ expr : EConst(CIdent("sys")), pos : pos },"db"), pos : pos }, "Manager"), pos : pos };
  362. }
  363. inline function makeString( s : String, pos ) {
  364. return { expr : EConst(CString(s)), pos : pos };
  365. }
  366. inline function makeOp( op : String, e1, e2, pos ) {
  367. return sqlAdd(sqlAddString(e1,op),e2,pos);
  368. }
  369. inline function sqlAdd( e1 : Expr, e2 : Expr, pos : Position ) {
  370. return { expr : EBinop(OpAdd, e1, e2), pos : pos };
  371. }
  372. inline function sqlAddString( sql : Expr, s : String ) {
  373. return { expr : EBinop(OpAdd, sql, makeString(s,sql.pos)), pos : sql.pos };
  374. }
  375. function sqlQuoteValue( v : Expr, t : SpodType ) {
  376. switch( v.expr ) {
  377. case EConst(c):
  378. switch( c ) {
  379. case CInt(_), CFloat(_): return v;
  380. case CString(s):
  381. if( simpleString.match(s) ) return { expr : EConst(CString("'"+s+"'")), pos : v.pos };
  382. case CIdent(n):
  383. switch( n ) {
  384. case "null": return { expr : EConst(CString("NULL")), pos : v.pos };
  385. case "true": return { expr : EConst(CInt("1")), pos : v.pos };
  386. case "false": return { expr : EConst(CInt("0")), pos : v.pos };
  387. }
  388. default:
  389. }
  390. default:
  391. }
  392. return { expr : ECall( { expr : EField(manager, "quoteAny"), pos : v.pos }, [ensureType(v,t)]), pos : v.pos }
  393. }
  394. inline function sqlAddValue( sql : Expr, v : Expr, t : SpodType ) {
  395. return { expr : EBinop(OpAdd, sql, sqlQuoteValue(v,t)), pos : sql.pos };
  396. }
  397. function unifyClass( t : SpodType ) {
  398. return switch( t ) {
  399. case DId, DInt, DUId, DUInt, DEncoded, DFlags(_), DTinyInt, DTinyUInt, DSmallInt, DSmallUInt, DMediumInt, DMediumUInt: 0;
  400. case DBigId, DBigInt, DSingle, DFloat: 1;
  401. case DBool: 2;
  402. case DString(_), DTinyText, DSmallText, DText, DSerialized: 3;
  403. case DDate, DDateTime, DTimeStamp: 4;
  404. case DSmallBinary, DLongBinary, DBinary, DBytes(_), DNekoSerialized: 5;
  405. case DInterval: 6;
  406. case DNull: 7;
  407. };
  408. }
  409. function tryUnify( t, rt ) {
  410. if( t == rt ) return true;
  411. var c = unifyClass(t);
  412. var rc = unifyClass(rt);
  413. return c == rc || (c == 0 && rc == 1); // allow Int-to-Float expansion
  414. }
  415. function typeStr( t : SpodType ) {
  416. return Std.string(t).substr(1);
  417. }
  418. function canStringify( t : SpodType ) {
  419. return switch( unifyClass(t) ) {
  420. case 0, 1, 2, 3, 4, 5, 7: true;
  421. default: false;
  422. };
  423. }
  424. function convertType( t : SpodType ) {
  425. var pack = [];
  426. return TPath( {
  427. name : switch( unifyClass(t) ) {
  428. case 0: "Int";
  429. case 1: "Float";
  430. case 2: "Bool";
  431. case 3: "String";
  432. case 4: "Date";
  433. case 5: pack = ["haxe", "io"]; "Bytes";
  434. default: throw "assert";
  435. },
  436. pack : pack,
  437. params : [],
  438. sub : null,
  439. });
  440. }
  441. function unify( t : SpodType, rt : SpodType, pos : Position ) {
  442. if( !tryUnify(t, rt) )
  443. error(typeStr(t) + " should be " + typeStr(rt), pos);
  444. }
  445. function buildCmp( op, e1, e2, pos ) {
  446. var r1 = buildCond(e1);
  447. var r2 = buildCond(e2);
  448. unify(r2.t, r1.t, e2.pos);
  449. if( !tryUnify(r1.t, DInt) && !tryUnify(r1.t, DDate) && !tryUnify(r1.t, DText) )
  450. unify(r1.t, DInt, e1.pos);
  451. return { sql : makeOp(op, r1.sql, r2.sql, pos), t : DBool, n : r1.n || r2.n };
  452. }
  453. function buildNum( op, e1, e2, pos ) {
  454. var r1 = buildCond(e1);
  455. var r2 = buildCond(e2);
  456. var c1 = unifyClass(r1.t);
  457. var c2 = unifyClass(r2.t);
  458. if( c1 > 1 ) {
  459. if( op == "-" && tryUnify(r1.t, DDateTime) && tryUnify(r2.t,DInterval) )
  460. return { sql : makeOp(op, r1.sql, r2.sql, pos), t : DDateTime, n : r1.n };
  461. unify(r1.t, DInt, e1.pos);
  462. }
  463. if( c2 > 1 ) unify(r2.t, DInt, e2.pos);
  464. return { sql : makeOp(op, r1.sql, r2.sql, pos), t : (c1 + c2) == 0 ? DInt : DFloat, n : r1.n || r2.n };
  465. }
  466. function buildInt( op, e1, e2, pos ) {
  467. var r1 = buildCond(e1);
  468. var r2 = buildCond(e2);
  469. unify(r1.t, DInt, e1.pos);
  470. unify(r2.t, DInt, e2.pos);
  471. return { sql : makeOp(op, r1.sql, r2.sql, pos), t : DInt, n : r1.n || r2.n };
  472. }
  473. function buildEq( eq, e1 : Expr, e2, pos ) {
  474. var r1 = null;
  475. switch( e1.expr ) {
  476. case EConst(c):
  477. switch( c ) {
  478. case CIdent(i):
  479. if( i.charCodeAt(0) == "$".code ) {
  480. var tmp = { field : i.substr(1), expr : e2 };
  481. var f = getField(tmp);
  482. r1 = { sql : makeString(quoteField(tmp.field), e1.pos), t : f.t, n : f.isNull };
  483. e2 = tmp.expr;
  484. }
  485. default:
  486. }
  487. default:
  488. }
  489. if( r1 == null )
  490. r1 = buildCond(e1);
  491. var r2 = buildCond(e2);
  492. if( r2.t == DNull ) {
  493. if( !r1.n )
  494. error("Expression can't be null", e1.pos);
  495. return { sql : sqlAddString(r1.sql, eq ? " IS NULL" : " IS NOT NULL"), t : DBool, n : false };
  496. } else {
  497. unify(r2.t, r1.t, e2.pos);
  498. unify(r1.t, r2.t, e1.pos);
  499. }
  500. var sql;
  501. // use some different operators if there is a possibility for comparing two NULLs
  502. if( r1.n || r2.n )
  503. sql = { expr : ECall({ expr : EField(manager,"nullCompare"), pos : pos },[r1.sql,r2.sql,{ expr : EConst(CIdent(eq?"true":"false")), pos : pos }]), pos : pos };
  504. else
  505. sql = makeOp(eq?" = ":" != ", r1.sql, r2.sql, pos);
  506. return { sql : sql, t : DBool, n : r1.n || r2.n };
  507. }
  508. function buildDefault( cond : Expr ) {
  509. var t = typeof(cond);
  510. isNull = false;
  511. var d = try makeType(t) catch( e : String ) try makeType(follow(t)) catch( e : String ) error("Unsupported type " + Std.string(t), cond.pos);
  512. return { sql : sqlQuoteValue(cond, d), t : d, n : isNull };
  513. }
  514. function getField( f : { field : String, expr : Expr } ) {
  515. var fi = inf.hfields.get(f.field);
  516. if( fi == null ) {
  517. for( r in inf.relations )
  518. if( r.prop == f.field ) {
  519. var path = r.type.split(".");
  520. var p = f.expr.pos;
  521. path.push("manager");
  522. var first = path.shift();
  523. #if haxe3
  524. var mpath = { expr : EConst(CIdent(first)), pos : p };
  525. #else
  526. var mpath = { expr : EConst(first.charCodeAt(0) <= 'Z'.code ? CType(first) : CIdent(first)), pos : p };
  527. #end
  528. for ( e in path )
  529. #if haxe3
  530. mpath = { expr : EField(mpath, e), pos : p };
  531. #else
  532. mpath = { expr : e.charCodeAt(0) <= 'Z'.code ? EType(mpath, e) : EField(mpath, e), pos : p };
  533. #end
  534. var m = getManager(typeof(mpath),p);
  535. var getid = { expr : ECall( { expr : EField(mpath, "unsafeGetId"), pos : p }, [f.expr]), pos : p };
  536. f.field = r.key;
  537. f.expr = ensureType(getid, m.inf.hfields.get(m.inf.key[0]).t);
  538. return inf.hfields.get(r.key);
  539. }
  540. error("No database field '" + f.field+"'", f.expr.pos);
  541. }
  542. return fi;
  543. }
  544. function buildCond( cond : Expr ) {
  545. var sql = null;
  546. var p = cond.pos;
  547. switch( cond.expr ) {
  548. case EObjectDecl(fl):
  549. var first = true;
  550. var sql = makeString("(", p);
  551. var fields = new Hash();
  552. for( f in fl ) {
  553. var fi = getField(f);
  554. if( first )
  555. first = false;
  556. else
  557. sql = sqlAddString(sql, " AND ");
  558. sql = sqlAddString(sql, quoteField(fi.name) + (fi.isNull ? " <=> " : " = "));
  559. sql = sqlAddValue(sql, f.expr, fi.t);
  560. if( fields.exists(fi.name) )
  561. error("Duplicate field " + fi.name, p);
  562. else
  563. fields.set(fi.name, true);
  564. }
  565. if( first ) sqlAddString(sql, "TRUE");
  566. sql = sqlAddString(sql, ")");
  567. return { sql : sql, t : DBool, n : false };
  568. case EParenthesis(e):
  569. var r = buildCond(e);
  570. r.sql = sqlAdd(makeString("(", p), r.sql, p);
  571. r.sql = sqlAddString(r.sql, ")");
  572. return r;
  573. case EBinop(op, e1, e2):
  574. switch( op ) {
  575. case OpAdd:
  576. var r1 = buildCond(e1);
  577. var r2 = buildCond(e2);
  578. var rt = if( tryUnify(r1.t, DFloat) && tryUnify(r2.t, DFloat) )
  579. tryUnify(r1.t, DInt) ? tryUnify(r2.t, DInt) ? DInt : DFloat : DFloat;
  580. else if( (tryUnify(r1.t, DText) && canStringify(r2.t)) || (tryUnify(r2.t, DText) && canStringify(r1.t)) )
  581. return { sql : sqlAddString(sqlAdd(sqlAddString(sqlAdd(makeString("CONCAT(",p),r1.sql,p),","),r2.sql,p),")"), t : DText, n : r1.n || r2.n }
  582. else
  583. error("Can't add " + typeStr(r1.t) + " and " + typeStr(r2.t), p);
  584. return { sql : makeOp("+", r1.sql, r2.sql, p), t : rt, n : r1.n || r2.n };
  585. case OpBoolAnd, OpBoolOr:
  586. var r1 = buildCond(e1);
  587. var r2 = buildCond(e2);
  588. unify(r1.t, DBool, e1.pos);
  589. unify(r2.t, DBool, e2.pos);
  590. return { sql : makeOp(op == OpBoolAnd ? " AND " : " OR ", r1.sql, r2.sql, p), t : DBool, n : false };
  591. case OpGte:
  592. return buildCmp(">=", e1, e2, p);
  593. case OpLte:
  594. return buildCmp("<=", e1, e2, p);
  595. case OpGt:
  596. return buildCmp(">", e1, e2, p);
  597. case OpLt:
  598. return buildCmp("<", e1, e2, p);
  599. case OpSub:
  600. return buildNum("-", e1, e2, p);
  601. case OpDiv:
  602. var r = buildNum("/", e1, e2, p);
  603. r.t = DFloat;
  604. return r;
  605. case OpMult:
  606. return buildNum("*", e1, e2, p);
  607. case OpEq, OpNotEq:
  608. return buildEq(op == OpEq, e1, e2, p);
  609. case OpXor:
  610. return buildInt("^", e1, e2, p);
  611. case OpOr:
  612. return buildInt("|", e1, e2, p);
  613. case OpAnd:
  614. return buildInt("&", e1, e2, p);
  615. case OpShr:
  616. return buildInt(">>", e1, e2, p);
  617. case OpShl:
  618. return buildInt("<<", e1, e2, p);
  619. case OpMod:
  620. return buildNum("%", e1, e2, p);
  621. case OpUShr, OpInterval, OpAssignOp(_), OpAssign:
  622. error("Unsupported operation", p);
  623. }
  624. case EUnop(op, post, e):
  625. var r = buildCond(e);
  626. switch( op ) {
  627. case OpNot:
  628. var sql = makeString("!", p);
  629. unify(r.t, DBool, e.pos);
  630. switch( r.sql.expr ) {
  631. case EConst(_):
  632. default:
  633. r.sql = sqlAddString(r.sql, ")");
  634. sql = sqlAddString(sql, "(");
  635. }
  636. return { sql : sqlAdd(sql, r.sql, p), t : DBool, n : r.n };
  637. case OpNegBits:
  638. var sql = makeString("~", p);
  639. unify(r.t, DInt, e.pos);
  640. return { sql : sqlAdd(sql, r.sql, p), t : DInt, n : r.n };
  641. case OpNeg:
  642. var sql = makeString("-", p);
  643. unify(r.t, DFloat, e.pos);
  644. return { sql : sqlAdd(sql, r.sql, p), t : r.t, n : r.n };
  645. case OpIncrement, OpDecrement:
  646. error("Unsupported operation", p);
  647. }
  648. case EConst(c):
  649. switch( c ) {
  650. case CInt(s): return { sql : makeString(s, p), t : DInt, n : false };
  651. case CFloat(s): return { sql : makeString(s, p), t : DFloat, n : false };
  652. case CString(s): return { sql : sqlQuoteValue(cond, DText), t : DString(s.length), n : false };
  653. case CRegexp(_): error("Unsupported", p);
  654. #if haxe3
  655. case CIdent(n):
  656. #else
  657. case CIdent(n), CType(n):
  658. #end
  659. if( n.charCodeAt(0) == "$".code ) {
  660. n = n.substr(1);
  661. var f = inf.hfields.get(n);
  662. if( f == null ) error("Unknown database field '" + n + "'", p);
  663. return { sql : makeString(quoteField(f.name), p), t : f.t, n : f.isNull };
  664. }
  665. switch( n ) {
  666. case "null":
  667. return { sql : makeString("NULL", p), t : DNull, n : true };
  668. case "true":
  669. return { sql : makeString("1", p), t : DBool, n : false };
  670. case "false":
  671. return { sql : makeString("0", p), t : DBool, n : false };
  672. }
  673. return buildDefault(cond);
  674. }
  675. case ECall(c, pl):
  676. switch( c.expr ) {
  677. case EConst(co):
  678. switch(co) {
  679. #if haxe3
  680. case CIdent(t):
  681. #else
  682. case CIdent(t), CType(t):
  683. #end
  684. if( t.charCodeAt(0) == '$'.code ) {
  685. var f = g.functions.get(t.substr(1));
  686. if( f == null ) error("Unknown method " + t, c.pos);
  687. if( f.params.length != pl.length ) error("Function " + f.name + " requires " + f.params.length + " parameters", p);
  688. var parts = f.sql.split("$");
  689. var sql = makeString(parts[0], p);
  690. var first = true;
  691. var isNull = false;
  692. for( i in 0...f.params.length ) {
  693. var r = buildCond(pl[i]);
  694. if( r.n ) isNull = true;
  695. unify(r.t, f.params[i], pl[i].pos);
  696. if( first )
  697. first = false;
  698. else
  699. sql = sqlAddString(sql, ",");
  700. sql = sqlAdd(sql, r.sql, p);
  701. }
  702. sql = sqlAddString(sql, parts[1]);
  703. // assume that for all SQL functions, a NULL parameter will make a NULL result
  704. return { sql : sql, t : f.ret, n : isNull };
  705. }
  706. default:
  707. }
  708. #if haxe3
  709. case EField(e, f):
  710. #else
  711. case EField(e, f), EType(e, f):
  712. #end
  713. switch( f ) {
  714. case "like":
  715. if( pl.length == 1 ) {
  716. var r = buildCond(e);
  717. var v = buildCond(pl[0]);
  718. if( !tryUnify(r.t, DText) ) {
  719. if( tryUnify(r.t, DBinary) )
  720. unify(v.t, DBinary, pl[0].pos);
  721. else
  722. unify(r.t, DText, e.pos);
  723. } else
  724. unify(v.t, DText, pl[0].pos);
  725. return { sql : makeOp(" LIKE ", r.sql, v.sql, p), t : DBool, n : r.n || v.n };
  726. }
  727. case "has":
  728. if( pl.length == 1 ) {
  729. var r = buildCond(e);
  730. switch( r.t ) {
  731. case DFlags(vals,_):
  732. var id = makeIdent(pl[0]);
  733. var idx = Lambda.indexOf(vals,id);
  734. if( idx < 0 ) error("Flag should be "+vals.join(","), pl[0].pos);
  735. return { sql : sqlAddString(r.sql, " & " + (1 << idx) + " != 0"), t : DBool, n : r.n };
  736. default:
  737. }
  738. }
  739. }
  740. default:
  741. }
  742. return buildDefault(cond);
  743. #if haxe3
  744. case EField(_, _), EDisplay(_):
  745. #else
  746. case EField(_, _), EType(_, _), EDisplay(_):
  747. #end
  748. return buildDefault(cond);
  749. case EIf(e, e1, e2), ETernary(e, e1, e2):
  750. if( e2 == null ) error("If must have an else statement", p);
  751. var r1 = buildCond(e1);
  752. var r2 = buildCond(e2);
  753. unify(r2.t, r1.t, e2.pos);
  754. unify(r1.t, r2.t, e1.pos);
  755. return { sql : { expr : EIf(e, r1.sql, r2.sql), pos : p }, t : r1.t, n : r1.n || r2.n };
  756. case EIn(e, v):
  757. var e = buildCond(e);
  758. var t = TPath({
  759. pack : [],
  760. name : "Iterable",
  761. params : [TPType(convertType(e.t))],
  762. sub : null,
  763. });
  764. return { sql : { expr : ECall( { expr : EField(manager, "quoteList"), pos : p }, [e.sql, { expr : ECheckType(v,t), pos : p } ]), pos : p }, t : DBool, n : e.n };
  765. default:
  766. return buildDefault(cond);
  767. }
  768. error("Unsupported expression", p);
  769. return null;
  770. }
  771. function ensureType( e : Expr, rt : SpodType ) {
  772. return { expr : ECheckType(e, convertType(rt)), pos : e.pos };
  773. }
  774. function checkKeys( econd : Expr ) {
  775. var p = econd.pos;
  776. switch( econd.expr ) {
  777. case EObjectDecl(fl):
  778. var key = inf.key.copy();
  779. for( f in fl ) {
  780. var fi = getField(f);
  781. if( !key.remove(fi.name) ) {
  782. if( Lambda.has(inf.key, fi.name) )
  783. error("Duplicate field " + fi.name, p);
  784. else
  785. error("Field " + f.field + " is not part of table key (" + inf.key.join(",") + ")", p);
  786. }
  787. f.expr = ensureType(f.expr, fi.t);
  788. }
  789. return econd;
  790. default:
  791. if( inf.key.length > 1 )
  792. error("You can't use a single value on a table with multiple keys (" + inf.key.join(",") + ")", p);
  793. var fi = inf.hfields.get(inf.key[0]);
  794. return ensureType(econd, fi.t);
  795. }
  796. }
  797. function orderField(e) {
  798. switch( e.expr ) {
  799. case EConst(c):
  800. switch( c ) {
  801. #if haxe3
  802. case CIdent(t):
  803. #else
  804. case CIdent(t), CType(t):
  805. #end
  806. if( !inf.hfields.exists(t) )
  807. error("Unknown database field", e.pos);
  808. return quoteField(t);
  809. default:
  810. }
  811. case EUnop(op, _, e):
  812. if( op == OpNeg )
  813. return orderField(e) + " DESC";
  814. default:
  815. }
  816. error("Invalid order field", e.pos);
  817. return null;
  818. }
  819. function concatStrings( e : Expr ) {
  820. var inf = { e : null, str : null };
  821. browseStrings(inf, e);
  822. if( inf.str != null ) {
  823. var es = { expr : EConst(CString(inf.str)), pos : e.pos };
  824. if( inf.e == null )
  825. inf.e = es;
  826. else
  827. inf.e = { expr : EBinop(OpAdd, inf.e, es), pos : e.pos };
  828. }
  829. return inf.e;
  830. }
  831. function browseStrings( inf : { e : Expr, str : String }, e : Expr ) {
  832. switch( e.expr ) {
  833. case EConst(c):
  834. switch( c ) {
  835. case CString(s):
  836. if( inf.str == null )
  837. inf.str = s;
  838. else
  839. inf.str += s;
  840. return;
  841. case CInt(s), CFloat(s):
  842. if( inf.str != null ) {
  843. inf.str += s;
  844. return;
  845. }
  846. default:
  847. }
  848. case EBinop(op, e1, e2):
  849. if( op == OpAdd ) {
  850. browseStrings(inf,e1);
  851. browseStrings(inf,e2);
  852. return;
  853. }
  854. case EIf(cond, e1, e2):
  855. e = { expr : EIf(cond, concatStrings(e1), concatStrings(e2)), pos : e.pos };
  856. default:
  857. }
  858. if( inf.str != null ) {
  859. e = { expr : EBinop(OpAdd, { expr : EConst(CString(inf.str)), pos : e.pos }, e), pos : e.pos };
  860. inf.str = null;
  861. }
  862. if( inf.e == null )
  863. inf.e = e;
  864. else
  865. inf.e = { expr : EBinop(OpAdd, inf.e, e), pos : e.pos };
  866. }
  867. function buildOptions( eopt : Expr ) {
  868. var p = eopt.pos;
  869. var opts = new Hash();
  870. var opt = { limit : null, orderBy : null, forceIndex : null };
  871. switch( eopt.expr ) {
  872. case EObjectDecl(fields):
  873. var limit = null;
  874. for( o in fields ) {
  875. if( opts.exists(o.field) ) error("Duplicate option " + o.field, p);
  876. opts.set(o.field, true);
  877. switch( o.field ) {
  878. case "orderBy":
  879. var fields = switch( o.expr.expr ) {
  880. case EArrayDecl(vl): Lambda.array(Lambda.map(vl, orderField));
  881. case ECall(v, pl):
  882. if( pl.length != 0 || !Type.enumEq(v.expr, EConst(CIdent("rand"))) )
  883. [orderField(o.expr)]
  884. else
  885. ["RAND()"];
  886. default: [orderField(o.expr)];
  887. };
  888. opt.orderBy = fields.join(",");
  889. case "limit":
  890. var limits = switch( o.expr.expr ) {
  891. case EArrayDecl(vl): Lambda.array(Lambda.map(vl, buildDefault));
  892. default: [buildDefault(o.expr)];
  893. }
  894. if( limits.length == 0 || limits.length > 2 ) error("Invalid limits", o.expr.pos);
  895. var l0 = limits[0], l1 = limits[1];
  896. unify(l0.t, DInt, l0.sql.pos);
  897. if( l1 != null )
  898. unify(l1.t, DInt, l1.sql.pos);
  899. opt.limit = { pos : l0.sql, len : l1 == null ? null : l1.sql };
  900. case "forceIndex":
  901. var fields = switch( o.expr.expr ) {
  902. case EArrayDecl(vl): Lambda.array(Lambda.map(vl, makeIdent));
  903. default: [makeIdent(o.expr)];
  904. }
  905. for( f in fields )
  906. if( !inf.hfields.exists(f) )
  907. error("Unknown field " + f, o.expr.pos);
  908. var idx = fields.join(",");
  909. if( !Lambda.exists(inf.indexes, function(i) return i.keys.join(",") == idx) && !Lambda.exists(inf.relations,function(r) return r.key == idx) )
  910. error("These fields are not indexed", o.expr.pos);
  911. opt.forceIndex = idx;
  912. default:
  913. error("Unknown option '" + o.field + "'", p);
  914. }
  915. }
  916. default:
  917. error("Options should be { orderBy : field, limit : [a,b] }", p);
  918. }
  919. return opt;
  920. }
  921. public static function getInfos( t : haxe.macro.Type ) {
  922. var c = switch( t ) {
  923. case TInst(c, _): if( c.toString() == "sys.db.Object" ) return null; c;
  924. default: return null;
  925. };
  926. return new SpodMacros(c);
  927. }
  928. #if macro
  929. static var RTTI = false;
  930. public static function addRtti() : Array<Field> {
  931. if( RTTI ) return null;
  932. RTTI = true;
  933. Context.getType("sys.db.SpodInfos");
  934. Context.onGenerate(function(types) {
  935. for( t in types )
  936. switch( t ) {
  937. case TInst(c, _):
  938. var c = c.get();
  939. var cur = c.superClass;
  940. while( cur != null ) {
  941. if( cur.t.toString() == "sys.db.Object" )
  942. break;
  943. cur = cur.t.get().superClass;
  944. }
  945. if( cur == null || c.meta.has(":skip") || c.meta.has("rtti") )
  946. continue;
  947. var inst = getInfos(t);
  948. var s = new haxe.Serializer();
  949. s.useEnumIndex = true;
  950. s.useCache = true;
  951. s.serialize(inst.inf);
  952. c.meta.add("rtti", [ { expr : EConst(CString(s.toString())), pos : c.pos } ], c.pos);
  953. default:
  954. }
  955. });
  956. Context.registerModuleReuseCall("sys.db.Manager", "sys.db.SpodMacros.addRtti()");
  957. return null;
  958. }
  959. static function getManagerInfos( t : haxe.macro.Type, pos ) {
  960. var param = null;
  961. switch( t ) {
  962. case TInst(c, p):
  963. while( true ) {
  964. if( c.toString() == "sys.db.Manager" ) {
  965. param = p[0];
  966. break;
  967. }
  968. var csup = c.get().superClass;
  969. if( csup == null ) break;
  970. c = csup.t;
  971. p = csup.params;
  972. }
  973. case TType(t, p):
  974. if( p.length == 1 && t.toString() == "sys.db.Manager" )
  975. param = p[0];
  976. default:
  977. }
  978. var inst = if( param == null ) null else getInfos(param);
  979. if( inst == null )
  980. Context.error("This method must be called from a specific Manager", Context.currentPos());
  981. inst.initManager(pos);
  982. return inst;
  983. }
  984. static function buildSQL( em : Expr, econd : Expr, prefix : String, ?eopt : Expr ) {
  985. var pos = Context.currentPos();
  986. var inst = getManagerInfos(Context.typeof(em),pos);
  987. var sql = { expr : EConst(CString(prefix + " " + inst.quoteField(inst.inf.name))), pos : econd.pos };
  988. var r = inst.buildCond(econd);
  989. if( r.t != DBool ) Context.error("Expression should be a condition", econd.pos);
  990. if( eopt != null && !Type.enumEq(eopt.expr, EConst(CIdent("null"))) ) {
  991. var opt = inst.buildOptions(eopt);
  992. if( opt.orderBy != null )
  993. r.sql = inst.sqlAddString(r.sql, " ORDER BY " + opt.orderBy);
  994. if( opt.limit != null ) {
  995. r.sql = inst.sqlAddString(r.sql, " LIMIT ");
  996. r.sql = inst.sqlAdd(r.sql, opt.limit.pos, pos);
  997. if( opt.limit.len != null ) {
  998. r.sql = inst.sqlAddString(r.sql, ",");
  999. r.sql = inst.sqlAdd(r.sql, opt.limit.len, pos);
  1000. }
  1001. }
  1002. if( opt.forceIndex != null )
  1003. sql = inst.sqlAddString(sql, " FORCE INDEX (" + inst.inf.name+"_"+opt.forceIndex+")");
  1004. }
  1005. sql = inst.sqlAddString(sql, " WHERE ");
  1006. var sql = inst.sqlAdd(sql, r.sql, sql.pos);
  1007. #if !display
  1008. sql = inst.concatStrings(sql);
  1009. #end
  1010. return sql;
  1011. }
  1012. public static function macroGet( em : Expr, econd : Expr, elock : Expr ) {
  1013. var pos = Context.currentPos();
  1014. var inst = getManagerInfos(Context.typeof(em),pos);
  1015. econd = inst.checkKeys(econd);
  1016. switch( econd.expr ) {
  1017. case EObjectDecl(_):
  1018. return { expr : ECall({ expr : EField(em,"unsafeGetWithKeys"), pos : pos },[econd,elock]), pos : pos };
  1019. default:
  1020. return { expr : ECall({ expr : EField(em,"unsafeGet"), pos : pos },[econd,elock]), pos : pos };
  1021. }
  1022. }
  1023. public static function macroSearch( em : Expr, econd : Expr, eopt : Expr, elock : Expr, ?single ) {
  1024. // allow both search(e,opts) and search(e,lock)
  1025. if( eopt != null && (elock == null || Type.enumEq(elock.expr, EConst(CIdent("null")))) ) {
  1026. switch( eopt.expr ) {
  1027. case EObjectDecl(_):
  1028. default:
  1029. var tmp = eopt;
  1030. eopt = elock;
  1031. elock = tmp;
  1032. }
  1033. }
  1034. var sql = buildSQL(em, econd, "SELECT * FROM", eopt);
  1035. var pos = Context.currentPos();
  1036. var e = { expr : ECall( { expr : EField(em, "unsafeObjects"), pos : pos }, [sql,elock]), pos : pos };
  1037. if( single )
  1038. e = { expr : ECall( { expr : EField(e, "first"), pos : pos }, []), pos : pos };
  1039. return e;
  1040. }
  1041. public static function macroCount( em : Expr, econd : Expr ) {
  1042. var sql = buildSQL(em, econd, "SELECT COUNT(*) FROM");
  1043. var pos = Context.currentPos();
  1044. return { expr : ECall({ expr : EField(em,"unsafeCount"), pos : pos },[sql]), pos : pos };
  1045. }
  1046. public static function macroDelete( em : Expr, econd : Expr, eopt : Expr ) {
  1047. var sql = buildSQL(em, econd, "DELETE FROM", eopt);
  1048. var pos = Context.currentPos();
  1049. return { expr : ECall({ expr : EField(em,"unsafeDelete"), pos : pos },[sql]), pos : pos };
  1050. }
  1051. static var isNeko = Context.defined("neko");
  1052. public static function macroBuild() {
  1053. var fields = Context.getBuildFields();
  1054. var hasManager = false;
  1055. for( f in fields ) {
  1056. if( f.name == "manager") hasManager = true;
  1057. for( m in f.meta )
  1058. if( m.name == ":relation" ) {
  1059. switch( f.kind ) {
  1060. case FVar(t, _):
  1061. f.kind = FProp("dynamic", "dynamic", t);
  1062. if( isNeko )
  1063. continue;
  1064. var relKey = null;
  1065. var relParams = [];
  1066. var lock = false;
  1067. for( p in m.params )
  1068. switch( p.expr ) {
  1069. case EConst(c):
  1070. switch( c ) {
  1071. #if haxe3
  1072. case CIdent(i):
  1073. #else
  1074. case CIdent(i), CType(i):
  1075. #end
  1076. relParams.push(i);
  1077. default:
  1078. }
  1079. default:
  1080. }
  1081. relKey = relParams.shift();
  1082. for( p in relParams )
  1083. if( p == "lock" )
  1084. lock = true;
  1085. // we will get an error later
  1086. if( relKey == null )
  1087. continue;
  1088. // generate get/set methods stubs
  1089. var pos = f.pos;
  1090. var ttype = t, tname;
  1091. while( true )
  1092. switch(ttype) {
  1093. case TPath(t):
  1094. if( t.params.length == 1 && (t.name == "Null" || t.name == "SNull") ) {
  1095. ttype = switch( t.params[0] ) {
  1096. case TPType(t): t;
  1097. default: throw "assert";
  1098. };
  1099. continue;
  1100. }
  1101. var p = t.pack.copy();
  1102. p.push(t.name);
  1103. if( t.sub != null ) p.push(t.sub);
  1104. tname = p.join(".");
  1105. break;
  1106. default:
  1107. Context.error("Relation type should be a type path", f.pos);
  1108. }
  1109. function e(expr) return { expr : expr, pos : pos };
  1110. var get = {
  1111. args : [],
  1112. params : [],
  1113. ret : t,
  1114. expr : Context.parse("return untyped "+tname+".manager.__get(this,'"+f.name+"','"+relKey+"',"+lock+")",pos),
  1115. };
  1116. var set = {
  1117. args : [{ name : "_v", opt : false, type : t, value : null }],
  1118. params : [],
  1119. ret : t,
  1120. expr : Context.parse("return untyped "+tname+".manager.__set(this,'"+f.name+"','"+relKey+"',_v)",pos),
  1121. };
  1122. var meta = [{ name : ":hide", params : [], pos : pos }];
  1123. fields.push({ name : "get_"+f.name, pos : pos, meta : meta, access : [APrivate], doc : null, kind : FFun(get) });
  1124. fields.push({ name : "set_"+f.name, pos : pos, meta : meta, access : [APrivate], doc : null, kind : FFun(set) });
  1125. default:
  1126. Context.error("Invalid relation field type", f.pos);
  1127. }
  1128. break;
  1129. }
  1130. }
  1131. if( !hasManager ) {
  1132. var inst = Context.getLocalClass().get();
  1133. if( inst.meta.has(":skip") )
  1134. return fields;
  1135. var p = inst.pos;
  1136. var tinst = TPath( { pack : inst.pack, name : inst.name, sub : null, params : [] } );
  1137. var path = inst.pack.copy().concat([inst.name]).join(".");
  1138. var enew = { expr : ENew( { pack : ["sys", "db"], name : "Manager", sub : null, params : [TPType(tinst)] }, [Context.parse(path, p)]), pos : p }
  1139. fields.push({ name : "manager", meta : [], kind : FVar(null,enew), doc : null, access : [AStatic,APublic], pos : p });
  1140. }
  1141. return fields;
  1142. }
  1143. #end
  1144. }