hqlinline.cpp 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jliball.hpp"
  14. #include "hql.hpp"
  15. #include "platform.h"
  16. #include "jlib.hpp"
  17. #include "jmisc.hpp"
  18. #include "jstream.ipp"
  19. #include "jdebug.hpp"
  20. #include "hql.hpp"
  21. #include "hqlcpp.ipp"
  22. #include "hqlhtcpp.ipp"
  23. #include "hqlutil.hpp"
  24. #include "hqlcpputil.hpp"
  25. #include "hqltcppc.ipp"
  26. #include "hqlwcpp.hpp"
  27. #include "hqlcatom.hpp"
  28. #include "hqlccommon.hpp"
  29. #include "hqlcerrors.hpp"
  30. #include "hqlpmap.hpp"
  31. #include "hqlinline.hpp"
  32. //I don't think the following is needed yet, but when it is, just enable following...
  33. //#define ONSTART_IN_NESTED_TOPLEVEL
  34. /*
  35. Allow the following operations to be evaluated in line:
  36. a) project of an dataset that can be iterated
  37. */
  38. enum
  39. {
  40. HEFprocessinline = 0x0001,
  41. HEFassigninline = 0x0002,
  42. HEFiterateinline = 0x0004,
  43. HEFevaluateinline = 0x0008, // can evaluate without any temporary dataset being created (temporary row is ok)
  44. HEFspillinline = 0x0010, // I'm not sure I can do this - because whether it spills depends on how it is being used.
  45. RETassign = HEFassigninline|HEFprocessinline,
  46. RETiterate = HEFiterateinline|HEFassigninline|HEFprocessinline,
  47. RETevaluate = HEFevaluateinline|HEFiterateinline|HEFassigninline|HEFprocessinline,
  48. };
  49. #define canAssignNoSpill(childFlags) ((childFlags & (HEFspillinline|HEFassigninline)) == HEFassigninline)
  50. #define canIterateNoSpill(childFlags) ((childFlags & (HEFspillinline|HEFiterateinline)) == HEFiterateinline)
  51. #define canEvaluateNoSpill(childFlags) ((childFlags & (HEFspillinline|HEFevaluateinline)) == HEFevaluateinline)
  52. // assign is superset of iterate, iterate is a superset of evaluate
  53. static unsigned getInlineFlags(BuildCtx * ctx, IHqlExpression * expr);
  54. static unsigned calcInlineFlags(BuildCtx * ctx, IHqlExpression * expr)
  55. {
  56. //The following improves a few graphs, but sometimes significantly (e.g., bc10.xhql, seep11.xhql)
  57. //But it would be really good if the code could be made context independent - then it could go in hqlattr and be cached.
  58. if (ctx)
  59. {
  60. if (expr->isDataset() || expr->isDictionary())
  61. {
  62. if (ctx->queryMatchExpr(expr))
  63. return RETevaluate;
  64. }
  65. else
  66. {
  67. if (ctx->queryAssociation(expr, AssocRow, NULL))
  68. return RETevaluate;
  69. }
  70. }
  71. node_operator op = expr->getOperator();
  72. switch (op)
  73. {
  74. case no_workunit_dataset:
  75. if (expr->hasAttribute(wuidAtom))
  76. return 0;
  77. return RETassign;
  78. case no_alias:
  79. {
  80. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  81. if (childFlags == 0)
  82. return 0;
  83. if (isGrouped(expr))
  84. return RETevaluate;
  85. return RETevaluate|RETiterate;
  86. }
  87. case no_dataset_alias:
  88. return getInlineFlags(ctx, expr->queryChild(0));
  89. }
  90. if (isGrouped(expr))
  91. return 0;
  92. switch (op)
  93. {
  94. case no_select:
  95. //MORE: What about child datasets in nested records?
  96. if (isNewSelector(expr))
  97. {
  98. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  99. if ((childFlags & HEFevaluateinline) && isMultiLevelDatasetSelector(expr, false))
  100. childFlags &= ~HEFevaluateinline;
  101. return childFlags;
  102. }
  103. return RETevaluate;
  104. case no_newaggregate:
  105. {
  106. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  107. if ((childFlags == 0) || queryRealChild(expr, 3))
  108. return 0;
  109. if (canIterateNoSpill(childFlags))
  110. return RETevaluate;
  111. return RETevaluate|HEFspillinline;
  112. }
  113. case no_hqlproject:
  114. //can't do a skip inside an inline project - since the generated code doesn't allow "continue" to be used.
  115. if (transformContainsSkip(expr->queryChild(1)))
  116. return 0;
  117. case no_newusertable:
  118. {
  119. if (expr->hasAttribute(prefetchAtom))
  120. return 0;
  121. IHqlExpression * ds = expr->queryChild(0);
  122. unsigned childFlags = getInlineFlags(ctx, ds);
  123. if (childFlags == 0)
  124. return 0;
  125. if (hasSingleRow(ds))
  126. {
  127. if (canEvaluateNoSpill(childFlags))
  128. return RETevaluate;
  129. return RETevaluate|HEFspillinline;
  130. }
  131. if (canIterateNoSpill(childFlags))
  132. return RETiterate;
  133. return RETiterate|HEFspillinline;
  134. }
  135. case no_selectmap:
  136. case no_selectnth:
  137. {
  138. IHqlExpression * ds = expr->queryChild(0);
  139. unsigned childFlags = getInlineFlags(ctx, ds);
  140. if (childFlags == 0)
  141. {
  142. if (isSelectSortedTop(expr))
  143. {
  144. childFlags = getInlineFlags(ctx, ds->queryChild(0));
  145. if (childFlags == 0)
  146. return 0;
  147. if (canIterateNoSpill(childFlags))
  148. return RETevaluate;
  149. return RETevaluate|HEFspillinline;
  150. }
  151. return 0;
  152. }
  153. if (isTrivialSelectN(expr) && canEvaluateNoSpill(childFlags))
  154. return RETevaluate;
  155. if (canIterateNoSpill(childFlags))
  156. return RETevaluate;
  157. return RETevaluate|HEFspillinline;
  158. }
  159. case no_filter:
  160. {
  161. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  162. if (childFlags == 0)
  163. return 0;
  164. if (canIterateNoSpill(childFlags))
  165. return RETiterate;
  166. if (filterIsTableInvariant(expr) && canAssignNoSpill(childFlags))
  167. return RETassign;
  168. return RETiterate|HEFspillinline;
  169. }
  170. case no_choosen:
  171. case no_index:
  172. {
  173. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  174. if (childFlags == 0)
  175. return 0;
  176. if (canIterateNoSpill(childFlags))
  177. return RETiterate;
  178. return RETiterate|HEFspillinline;
  179. }
  180. case no_limit:
  181. {
  182. if (expr->hasAttribute(skipAtom) || expr->hasAttribute(onFailAtom))
  183. return 0;
  184. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  185. if (childFlags == 0)
  186. return 0;
  187. unsigned flags = 0;
  188. if (canEvaluateNoSpill(childFlags))
  189. flags |= RETevaluate;
  190. if (canIterateNoSpill(childFlags))
  191. flags |= RETiterate;
  192. if (flags)
  193. return flags;
  194. return RETevaluate|HEFspillinline;
  195. }
  196. case no_fail:
  197. return RETevaluate;
  198. case no_catchds:
  199. return 0; // for the moment always do this out of line
  200. case no_table:
  201. return 0;
  202. case no_createdictionary:
  203. return RETassign;
  204. case no_datasetfromdictionary:
  205. return RETiterate;
  206. case no_owned_ds:
  207. {
  208. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  209. if (childFlags == 0)
  210. return 0;
  211. return RETevaluate|HEFspillinline;
  212. }
  213. case no_addfiles:
  214. {
  215. unsigned ret = RETassign;
  216. for (unsigned i=0; i < 2; i++)
  217. {
  218. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(i));
  219. if (childFlags == 0)
  220. return 0;
  221. if (childFlags & HEFspillinline)
  222. ret |= HEFspillinline;
  223. }
  224. return ret;
  225. }
  226. case no_if:
  227. case no_choose:
  228. case no_chooseds:
  229. {
  230. unsigned ret = expr->isDatarow() ? RETevaluate : RETassign;
  231. ForEachChildFrom(i, expr, 1)
  232. {
  233. IHqlExpression * cur = expr->queryChild(i);
  234. if (cur->isAttribute())
  235. continue;
  236. unsigned childFlags = getInlineFlags(ctx, cur);
  237. if (childFlags == 0)
  238. return 0;
  239. if (childFlags & HEFspillinline)
  240. ret |= HEFspillinline;
  241. }
  242. return ret;
  243. }
  244. case no_compound_childread:
  245. case no_compound_childnormalize:
  246. case no_compound_childaggregate:
  247. case no_compound_selectnew:
  248. case no_compound_inline:
  249. case no_datasetfromrow:
  250. case no_sorted:
  251. case no_distributed:
  252. case no_preservemeta:
  253. case no_nofold:
  254. case no_nohoist:
  255. case no_alias_scope:
  256. case no_serialize:
  257. case no_deserialize:
  258. case no_dataset_alias:
  259. return getInlineFlags(ctx, expr->queryChild(0));
  260. case no_forcegraph:
  261. return 0;
  262. case no_section:
  263. if (expr->hasAttribute(graphAtom)) // force it to appear in the graph
  264. return 0;
  265. return getInlineFlags(ctx, expr->queryChild(0));
  266. case no_call:
  267. case no_externalcall: // no so sure about this - should possibly be assignable only. (also no_call above)
  268. case no_getresult:
  269. return expr->isDatarow() ? RETevaluate : RETassign;
  270. case no_getgraphresult:
  271. if (expr->hasAttribute(_distributed_Atom))
  272. return 0;
  273. return expr->isDatarow() ? RETevaluate : RETassign;
  274. case no_temptable:
  275. return RETassign;
  276. case no_xmlproject:
  277. return RETiterate;
  278. case no_dataset_from_transform:
  279. {
  280. if (expr->hasAttribute(distributedAtom))
  281. return 0;
  282. if (transformContainsSkip(expr->queryChild(1)))
  283. return 0;
  284. return RETassign;
  285. }
  286. case no_inlinetable:
  287. {
  288. IHqlExpression * transforms = expr->queryChild(0);
  289. if (transformListContainsSkip(transforms))
  290. return 0;
  291. if (isConstantDataset(expr))
  292. return RETevaluate;
  293. return RETassign;
  294. }
  295. case no_createrow:
  296. //MORE: We should probably be able to handle this...
  297. if (containsSkip(expr->queryChild(0)))
  298. return 0;
  299. return RETevaluate;
  300. case no_translated:
  301. return RETassign|RETiterate|RETevaluate;
  302. case no_null:
  303. case no_temprow:
  304. case no_projectrow:
  305. case no_left:
  306. case no_right:
  307. case no_top:
  308. case no_id2blob:
  309. case no_activerow:
  310. case no_typetransfer:
  311. case no_rows:
  312. case no_skip:
  313. case no_matchattr:
  314. case no_matchrow:
  315. case no_libraryinput:
  316. case no_fromxml:
  317. return RETevaluate;
  318. case no_apply:
  319. {
  320. unsigned childFlags = getInlineFlags(ctx, expr->queryChild(0));
  321. if (childFlags == 0)
  322. return 0;
  323. return RETevaluate|(childFlags & HEFspillinline);
  324. }
  325. case no_activetable:
  326. return RETassign;
  327. case no_join:
  328. {
  329. if (!expr->hasAttribute(allAtom) || isKeyedJoin(expr))
  330. return 0;
  331. //conservatively check we support the attributes.
  332. unsigned max = expr->numChildren();
  333. for (unsigned i=4; i < max; i++)
  334. {
  335. IHqlExpression * cur = expr->queryChild(i);
  336. if (!cur->isAttribute())
  337. return 0;
  338. IAtom * name = cur->queryName();
  339. //possibly implement keep as well. (Local on a child join does nothing.)
  340. if ((name != leftouterAtom) && (name != leftonlyAtom) && (name != innerAtom) && (name != allAtom) && (name != localAtom) && !isInternalAttributeName(name))
  341. return 0;
  342. }
  343. unsigned childLFlags = getInlineFlags(ctx, expr->queryChild(0));
  344. unsigned childRFlags = getInlineFlags(ctx, expr->queryChild(1));
  345. if ((childLFlags == 0) || (childRFlags == 0))
  346. return 0;
  347. return RETassign|((childLFlags|childRFlags) & HEFspillinline);
  348. }
  349. case no_compound:
  350. return getInlineFlags(ctx, expr->queryChild(1));
  351. default:
  352. return 0;
  353. }
  354. }
  355. unsigned getInlineFlags(BuildCtx * ctx, IHqlExpression * expr)
  356. {
  357. //This could one day use flags stored in CHqlExpression if it was considered necessary.
  358. return calcInlineFlags(ctx, expr);
  359. }
  360. bool canProcessInline(BuildCtx * ctx, IHqlExpression * expr)
  361. {
  362. return getInlineFlags(ctx, expr) != 0;
  363. }
  364. bool canIterateInline(BuildCtx * ctx, IHqlExpression * expr)
  365. {
  366. return canIterateNoSpill(getInlineFlags(ctx, expr));
  367. }
  368. bool canEvaluateInline(BuildCtx * ctx, IHqlExpression * expr)
  369. {
  370. return canEvaluateNoSpill(getInlineFlags(ctx, expr));
  371. }
  372. bool canAssignInline(BuildCtx * ctx, IHqlExpression * expr)
  373. {
  374. return canAssignNoSpill(getInlineFlags(ctx, expr));
  375. }
  376. bool canAssignNotEvaluateInline(BuildCtx * ctx, IHqlExpression * expr)
  377. {
  378. unsigned flags = getInlineFlags(ctx, expr);
  379. return canAssignNoSpill(flags) && !canEvaluateNoSpill(flags);
  380. }
  381. bool alwaysEvaluatesToBound(IHqlExpression * expr)
  382. {
  383. switch (expr->getOperator())
  384. {
  385. case no_select:
  386. return !isMultiLevelDatasetSelector(expr, false);
  387. case no_owned_ds:
  388. case no_translated:
  389. case no_alias:
  390. case no_call:
  391. case no_externalcall: // no so sure about this - should possibly be assignable only. (also no_call above)
  392. case no_getresult:
  393. case no_getgraphresult:
  394. case no_workunit_dataset:
  395. case no_activetable:
  396. return !hasStreamedModifier(expr->queryType());
  397. case no_temptable:
  398. //MORE! when we have constant datasets
  399. default:
  400. return canEvaluateInline(NULL, expr);
  401. }
  402. }
  403. //============================================================================
  404. //============================================================================
  405. /*
  406. Notes on Graph distribution/localisation etc.
  407. There are several different things to take into account when noting the localisation of an activity, including the problem
  408. that meaning of "local" is overloaded. Currently we have something like the following, for each logical instance of an activity:
  409. Roxie
  410. SingleNode - only evaluated on a single node
  411. [Split, filter, project, rollup, sort, simpleresult]
  412. SingleNodeLocal - evaluated on a single node, and only access local files.
  413. [local keyed join, local index read]
  414. PrimarySlave - Evaluated on many nodes, only using local file parts [if any], interaction between nodes may occur on the primary
  415. [keyed join, index read, remote]
  416. Thor
  417. Master - only evalauted on the master
  418. [simpleresult]
  419. Distributed - Evaluated on many nodes
  420. [sort, distribute, group]
  421. DistributedIndependent - Evaluated on many nodes, no interaction between nodes
  422. [local sort, local group]
  423. DistributedLocal- Evaluated on many nodes using local file parts
  424. [local index read, local keyed join]
  425. HThor only has singlenode as an option
  426. Child Queries can be essentially be any of the roxie options. In addition there is the question of where the child will be
  427. evaluated in relation to its parent. The options are
  428. NoAccess - no context dependent information in the helper
  429. CoLocal - Will be evaluated on the same slave node as the parent
  430. NonLocal - May be evaluated on a different slave node from the parent
  431. We'll use the following logical flags to keep track of the different options:
  432. _singlenode_|_independentnodes_|_
  433. independent - no interaction between records on the different nodes.
  434. correlated - interaction between records on the different nodes.
  435. _localparts_|_allparts_
  436. - access local file parts or all file parts.
  437. allfile - access all file parts.
  438. local - either access local file parts, or no interaction between nodes.
  439. Ideally we want the expression tree to be independent of the current engine type, and so that derived attributes and optimizations
  440. can work independently of the engine if possible.
  441. => spill needs to indicate whether it is a purely memory spill
  442. => sort order and local interactions need resolving.
  443. */
  444. inline GraphLocalisation mergeLocalisation(GraphLocalisation l, GraphLocalisation r)
  445. {
  446. //options are GraphCoLocal, GraphNonLocal, GraphCoNonLocal, GraphNoAccess
  447. if (l == GraphNeverAccess)
  448. l = GraphNoAccess;
  449. if (r == GraphNeverAccess)
  450. r = GraphNoAccess;
  451. if (l == GraphNoAccess)
  452. return r;
  453. if (r == GraphNoAccess)
  454. return l;
  455. if ((l == GraphCoNonLocal) || (r == GraphCoNonLocal))
  456. return GraphCoNonLocal;
  457. if (l != r)
  458. return GraphCoNonLocal;
  459. return l;
  460. }
  461. bool exprExcludingInputNeedsParent(IHqlExpression * expr)
  462. {
  463. unsigned first = getFirstActivityArgument(expr);
  464. unsigned last = first + getNumActivityArguments(expr);
  465. ForEachChild(i, expr)
  466. {
  467. if (i >= first && i < last)
  468. continue;
  469. IHqlExpression * cur = expr->queryChild(i);
  470. if (isGraphDependent(cur) || usesRuntimeContext(cur))
  471. return true;
  472. }
  473. if (!expr->isIndependentOfScopeIgnoringInputs())
  474. return true;
  475. return false;
  476. }
  477. bool exprIncludingInputNeedsParent(IHqlExpression * expr)
  478. {
  479. if (isGraphDependent(expr) || usesRuntimeContext(expr))
  480. return true;
  481. if (!expr->isIndependentOfScope())
  482. return true;
  483. return false;
  484. }
  485. bool activityNeedsParent(IHqlExpression * expr)
  486. {
  487. //This should always err on the side of yes...
  488. switch (expr->getOperator())
  489. {
  490. case no_select:
  491. case no_compound_diskread:
  492. case no_compound_disknormalize:
  493. case no_compound_diskaggregate:
  494. case no_compound_diskcount:
  495. case no_compound_diskgroupaggregate:
  496. case no_compound_indexread:
  497. case no_compound_indexnormalize:
  498. case no_compound_indexaggregate:
  499. case no_compound_indexcount:
  500. case no_compound_indexgroupaggregate:
  501. if (exprIncludingInputNeedsParent(expr))
  502. return true;
  503. break;
  504. case no_libraryinput:
  505. return true;
  506. case no_hqlproject:
  507. case no_newusertable:
  508. //Filters might be merged into projects, so need to walk the inputs.
  509. loop
  510. {
  511. if (activityNeedsParent(expr))
  512. return true;
  513. expr = expr->queryChild(0);
  514. switch (expr->getOperator())
  515. {
  516. case no_sorted:
  517. case no_filter:
  518. break;
  519. default:
  520. return false;
  521. }
  522. }
  523. case no_libraryscopeinstance:
  524. {
  525. //Obscure way to get at the name of the library being called.
  526. IHqlExpression * moduleFunction = expr->queryBody()->queryDefinition();
  527. IHqlExpression * module = moduleFunction->queryChild(0);
  528. assertex(module->getOperator() == no_libraryscope);
  529. IHqlExpression * nameAttr = module->queryAttribute(nameAtom);
  530. if (activityNeedsParent(nameAttr))
  531. return true;
  532. return exprExcludingInputNeedsParent(expr);
  533. }
  534. default:
  535. if (expr->isDatarow())
  536. {
  537. switch (expr->getOperator())
  538. {
  539. case no_split:
  540. case no_spill:
  541. case no_selectnth:
  542. if (exprExcludingInputNeedsParent(expr))
  543. return true;
  544. break;
  545. default:
  546. if (exprIncludingInputNeedsParent(expr))
  547. return true;
  548. break;
  549. }
  550. }
  551. else
  552. {
  553. if (exprExcludingInputNeedsParent(expr))
  554. return true;
  555. }
  556. break;
  557. }
  558. //Assume the worse for queries based on ds.childdataset
  559. if (getChildDatasetType(expr) & childdataset_hasdataset)
  560. {
  561. IHqlExpression * ds = queryRoot(expr);
  562. if (ds && ds->getOperator() == no_select)
  563. return true;
  564. }
  565. return false;
  566. }
  567. // A minimal function to catch the obviously context invariant items.
  568. static bool accessesData(IHqlExpression * expr)
  569. {
  570. switch (expr->getOperator())
  571. {
  572. case no_attr:
  573. case no_constant:
  574. case no_record:
  575. return false;
  576. case no_assign:
  577. return accessesData(expr->queryChild(1));
  578. case no_assignall:
  579. case no_transform:
  580. case no_newtransform:
  581. case no_transformlist:
  582. case no_attr_expr:
  583. case no_fail:
  584. {
  585. ForEachChild(i, expr)
  586. {
  587. if (accessesData(expr->queryChild(i)))
  588. return true;
  589. }
  590. return false;
  591. }
  592. default:
  593. return true;
  594. }
  595. }
  596. GraphLocalisation queryActivityLocalisation(IHqlExpression * expr, bool optimizeParentAccess)
  597. {
  598. node_operator op = expr->getOperator();
  599. switch (op)
  600. {
  601. case no_compound_diskread:
  602. {
  603. if (isLocalActivity(expr) || expr->hasAttribute(_colocal_Atom))
  604. return GraphCoLocal;
  605. //If a compound operation has been added, but with no other effect then don't allow that to change the localisation
  606. IHqlExpression * ds = expr->queryChild(0);
  607. if (ds->getOperator() == no_table)
  608. return queryActivityLocalisation(ds, optimizeParentAccess);
  609. return GraphNonLocal;
  610. }
  611. case no_table:
  612. if (expr->hasAttribute(_noAccess_Atom))
  613. return GraphNeverAccess;
  614. //fallthrough
  615. case no_keyindex:
  616. case no_newkeyindex:
  617. case no_compound_disknormalize:
  618. case no_compound_diskaggregate:
  619. case no_compound_diskcount:
  620. case no_compound_diskgroupaggregate:
  621. case no_compound_indexread:
  622. case no_compound_indexnormalize:
  623. case no_compound_indexaggregate:
  624. case no_compound_indexcount:
  625. case no_compound_indexgroupaggregate:
  626. if (!isLocalActivity(expr) && !expr->hasAttribute(_colocal_Atom))
  627. return GraphNonLocal;
  628. break;
  629. case no_compound_fetch:
  630. case no_fetch:
  631. //Maybe there should be a version that only fetches locally???
  632. return GraphNonLocal; // simplest if we do it this way
  633. case no_allnodes:
  634. return GraphNonLocal; // simplest if we do it this way
  635. case no_join:
  636. case no_denormalize:
  637. case no_denormalizegroup:
  638. if (isKeyedJoin(expr) && !expr->hasAttribute(localAtom))
  639. return GraphNonLocal;
  640. break;
  641. case no_output:
  642. if (expr->hasAttribute(_spill_Atom))
  643. return GraphNeverAccess;
  644. break;
  645. case no_setgraphresult:
  646. case no_spillgraphresult:
  647. case no_setgraphloopresult:
  648. case no_null:
  649. case no_split:
  650. case no_spill:
  651. case no_readspill:
  652. case no_writespill:
  653. case no_commonspill:
  654. case no_addfiles:
  655. case no_subgraph:
  656. case no_nofold:
  657. case no_nohoist:
  658. case no_regroup:
  659. case no_sorted:
  660. case no_distributed:
  661. case no_preservemeta:
  662. case no_grouped:
  663. case no_alias_scope:
  664. case no_sequential:
  665. case no_parallel:
  666. case no_deserialize:
  667. case no_serialize:
  668. case no_actionlist:
  669. case no_definesideeffect:
  670. case no_dataset_alias:
  671. return GraphNeverAccess; // Will never access any data values from anywhere
  672. case no_hqlproject:
  673. case no_newusertable:
  674. //Many more of these could return GraphNoAccess if I determined that only constants and the input
  675. //row were accessed. Examples are project, sort, group etc. etc.
  676. //if (isSimpleProject(expr))
  677. /// return GraphNoAccess;
  678. return GraphCoLocal;
  679. case no_datasetfromrow:
  680. {
  681. if (getNumActivityArguments(expr) != 0)
  682. return GraphNeverAccess;
  683. IHqlExpression * row = expr->queryChild(0);
  684. switch (row->getOperator())
  685. {
  686. case no_createrow:
  687. case no_null:
  688. return queryActivityLocalisation(row, optimizeParentAccess);
  689. }
  690. break;
  691. }
  692. case no_workunit_dataset:
  693. return GraphCoLocal; // weird exception in roxie
  694. case no_getgraphresult:
  695. case no_datasetfromdictionary:
  696. return GraphCoLocal;
  697. case no_createrow:
  698. case no_inlinetable:
  699. {
  700. unsigned max = expr->numChildren();
  701. for (unsigned i=0; i<max; i++)
  702. {
  703. if (accessesData(expr->queryChild(i)))
  704. return GraphCoLocal;
  705. }
  706. return GraphNeverAccess;
  707. }
  708. case no_group:
  709. case no_choosen:
  710. case no_limit:
  711. case no_catchds:
  712. case no_selectnth:
  713. {
  714. unsigned max = expr->numChildren();
  715. for (unsigned i=1; i<max; i++)
  716. {
  717. if (accessesData(expr->queryChild(i)))
  718. return GraphCoLocal;
  719. }
  720. return GraphNeverAccess;
  721. }
  722. case no_newaggregate:
  723. if (!queryRealChild(expr, 3))
  724. {
  725. node_operator op = querySimpleAggregate(expr, false, false);
  726. if (op == no_existsgroup || op == no_countgroup)
  727. return GraphNeverAccess;
  728. //Need to check if it accesses anything in the context!
  729. }
  730. break;
  731. }
  732. if (optimizeParentAccess && !activityNeedsParent(expr))
  733. {
  734. switch (op)
  735. {
  736. case no_if:
  737. case no_case:
  738. case no_map:
  739. //May be combined into a switch.
  740. break;
  741. default:
  742. return GraphNoAccess;
  743. }
  744. }
  745. return GraphCoLocal;
  746. }
  747. bool isNonLocal(IHqlExpression * expr, bool optimizeParentAccess)
  748. {
  749. return (queryActivityLocalisation(expr, optimizeParentAccess) == GraphNonLocal);
  750. }
  751. static GraphLocalisation doGetGraphLocalisation(IHqlExpression * expr, bool optimizeParentAccess);
  752. static GraphLocalisation queryGraphLocalisation(IHqlExpression * expr, bool optimizeParentAccess)
  753. {
  754. GraphLocalisation localisation = queryActivityLocalisation(expr, optimizeParentAccess);
  755. if (isSourceActivity(expr))
  756. return localisation;
  757. unsigned firstChild = 0;
  758. unsigned numChildren;
  759. switch (expr->getOperator())
  760. {
  761. case no_if:
  762. case no_chooseds:
  763. firstChild = 1;
  764. numChildren = expr->numChildren();
  765. break;
  766. case no_compound:
  767. case no_subgraph:
  768. numChildren = expr->numChildren();
  769. break;
  770. case no_attr:
  771. case no_attr_expr:
  772. return GraphNeverAccess;
  773. default:
  774. numChildren = getNumActivityArguments(expr);
  775. break;
  776. }
  777. for (unsigned i = firstChild; i < numChildren; i++)
  778. {
  779. localisation = mergeLocalisation(localisation, doGetGraphLocalisation(expr->queryChild(i), optimizeParentAccess));
  780. if (localisation == GraphCoNonLocal)
  781. return localisation;
  782. }
  783. return localisation;
  784. }
  785. static GraphLocalisation doGetGraphLocalisation(IHqlExpression * expr, bool optimizeParentAccess)
  786. {
  787. IHqlExpression * cached = (IHqlExpression *)expr->queryTransformExtra();
  788. if (cached)
  789. return (GraphLocalisation)cached->queryValue()->getIntValue();
  790. GraphLocalisation ret = queryGraphLocalisation(expr, optimizeParentAccess);
  791. if (ret == GraphNeverAccess)
  792. ret = GraphNoAccess;
  793. expr->setTransformExtraOwned(getSizetConstant((unsigned)ret));
  794. return ret;
  795. }
  796. bool HqlCppTranslator::isAlwaysCoLocal()
  797. {
  798. return targetHThor();
  799. }
  800. GraphLocalisation HqlCppTranslator::getGraphLocalisation(IHqlExpression * expr, bool isInsideChildQuery)
  801. {
  802. if (isAlwaysCoLocal()) return GraphCoLocal;
  803. if (targetThor() && !isInsideChildQuery)
  804. return GraphNonLocal;
  805. TransformMutexBlock lock;
  806. return doGetGraphLocalisation(expr, options.optimizeParentAccess);
  807. }
  808. bool HqlCppTranslator::isNeverDistributed(IHqlExpression * expr)
  809. {
  810. if (targetHThor())
  811. return true;
  812. switch (expr->getOperator())
  813. {
  814. case no_if:
  815. return expr->isAction();
  816. case no_setresult:
  817. return true;
  818. }
  819. return false;
  820. }
  821. //============================================================================
  822. ParentExtract::ParentExtract(HqlCppTranslator & _translator, PEtype _type, IHqlExpression * _graphId, GraphLocalisation _localisation, EvalContext * _container)
  823. : HqlExprAssociation(parentExtractMarkerExpr), translator(_translator), type(_type), graphId(_graphId)
  824. {
  825. localisation = _localisation;
  826. container = _container;
  827. if (!container)
  828. throwError(HQLERR_ExpectedParentContext);
  829. Owned<ITypeInfo> nullRow = makeRowType(queryNullRecord()->getType());
  830. Owned<ITypeInfo> declType = makeModifier(makeWrapperModifier(LINK(nullRow)), typemod_builder);
  831. StringBuffer extractName;
  832. translator.getUniqueId(extractName.append("ex"));
  833. boundBuilder.expr.setown(createVariable(extractName.str(), LINK(declType)));
  834. boundExtract.expr.setown(createVariable(extractName.str(), makeReferenceModifier(LINK(nullRow))));
  835. serialization = NULL;
  836. childSerialization = NULL;
  837. canDestroyExtract = false;
  838. }
  839. ParentExtract::~ParentExtract()
  840. {
  841. ::Release(serialization);
  842. }
  843. void ParentExtract::associateCursors(BuildCtx & declarectx, BuildCtx & evalctx, GraphLocalisation childLocalisation)
  844. {
  845. const CursorArray * boundCursors = NULL;
  846. switch (childLocalisation)
  847. {
  848. case GraphCoLocal:
  849. if (localisation == GraphRemote)
  850. boundCursors = &nonlocalBoundCursors;
  851. else if ((localisation == GraphCoLocal) || (localisation == GraphCoNonLocal))
  852. boundCursors = &colocalBoundCursors;
  853. else
  854. throwError2(HQLERR_InconsisentLocalisation, (int)childLocalisation, (int)localisation);
  855. break;
  856. case GraphNonLocal:
  857. case GraphRemote:
  858. if ((localisation == GraphCoLocal) || (localisation == GraphNoAccess))
  859. throwError2(HQLERR_InconsisentLocalisation, (int)childLocalisation, (int)localisation);
  860. boundCursors = &nonlocalBoundCursors;
  861. break;
  862. case GraphCoNonLocal:
  863. throwUnexpected();
  864. case GraphNoAccess:
  865. if (localisation == GraphRemote)
  866. boundCursors = &nonlocalBoundCursors;
  867. else
  868. {
  869. //Make sure we get an error if we do try and access something from the parent.
  870. return;
  871. }
  872. break;
  873. default:
  874. throwUnexpected();
  875. }
  876. ForEachItemIn(i, *boundCursors)
  877. {
  878. BoundRow & cur = boundCursors->item(i);
  879. IHqlExpression * aliasExpansion = cur.queryAliasExpansion();
  880. if (aliasExpansion)
  881. {
  882. //NB: If this ever generates any code then we will need to process it later...
  883. CHqlBoundExpr bound;
  884. translator.buildExpr(evalctx, aliasExpansion, bound);
  885. declarectx.associateOwn(*cur.clone(bound.expr));
  886. }
  887. else
  888. declarectx.associateOwn(OLINK(cur));
  889. }
  890. }
  891. void ParentExtract::beginCreateExtract(BuildCtx & ctx, bool doDeclare)
  892. {
  893. buildctx.setown(new BuildCtx(ctx));
  894. // Don't leak the serialization row into other sub calls - may want to do this a different way so
  895. // cses get commoned up by tagging the serialization somehow.
  896. //Probably do this later and allow it to be null. Will need a mechanism for calling some finalisation code
  897. //after all code is generated in order to do it.
  898. if (doDeclare)
  899. {
  900. BuildCtx * declarectx = buildctx;
  901. translator.getInvariantMemberContext(ctx, &declarectx, NULL, false, false);
  902. declarectx->addDeclare(boundBuilder.expr);
  903. }
  904. serialization = SerializationRow::create(translator, boundBuilder.expr, container ? container->queryActivity() : NULL);
  905. buildctx->associateOwn(*LINK(serialization));
  906. //Ensure the row is large enough to cope with any fixed fields - will only get relocated if variable fields are serialised
  907. HqlExprArray args;
  908. args.append(*LINK(serialization->queryBound()));
  909. args.append(*serialization->getFinalFixedSizeExpr());
  910. translator.callProcedure(*buildctx, ensureRowAvailableId, args);
  911. //Collect a list of cursors together... NB these are in reverse order..
  912. gatherActiveRows(*buildctx);
  913. childSerialization->setBuilder(this);
  914. }
  915. void ParentExtract::beginNestedExtract(BuildCtx & clonectx)
  916. {
  917. //Collect a list of cursors together... NB these are in reverse order..
  918. gatherActiveRows(clonectx);
  919. }
  920. void ParentExtract::beginReuseExtract()
  921. {
  922. //MORE: Should really check that the same rows are active...
  923. //Need to check if any additional rows, and if so bind them.
  924. childSerialization->setBuilder(this);
  925. }
  926. void ParentExtract::beginChildActivity(BuildCtx & declareCtx, BuildCtx & startCtx, GraphLocalisation childLocalisation, IHqlExpression * colocal, bool nested, bool ignoreSelf, ActivityInstance * activityRequiringCast)
  927. {
  928. //MORE: If we ever generate grand children - nested classes then the following isn't going to work
  929. //because accessing colocal->exNNN isn't going to get at the extract defined in the child class+passed to the grandchild.
  930. //Simplest would be to define the builders in the activity, and access them as activity->exXXX in the (grand)child.
  931. bool isColocal = (colocal != NULL);
  932. const CursorArray & boundCursors = isColocal ? colocalBoundCursors : nonlocalBoundCursors;
  933. ForEachItemIn(i, boundCursors)
  934. {
  935. BoundRow & cur = boundCursors.item(i);
  936. if (cur.isSerialization())
  937. {
  938. IHqlExpression * bound = cur.queryBound();
  939. if (!colocal && bound != boundExtract.expr)
  940. break;
  941. declareCtx.addDeclare(bound);
  942. OwnedHqlExpr src;
  943. if (bound == boundExtract.expr)
  944. {
  945. if (ignoreSelf)
  946. continue;
  947. //MORE: This cast is a hack. We should process const correctly in helper functions etc.
  948. if (!nested)
  949. src.setown(createVariable("(byte *)pe", bound->getType()));
  950. }
  951. if (!src)
  952. {
  953. src.setown(addMemberSelector(bound, colocal));
  954. //yuk yuk yuk.... A nasty solution to a nasty problem.
  955. //If the parent is a nested class with an extract (used by the compound group aggregate)
  956. //then any other extracts are going to be builders in the parent activity, but pointers in the nested
  957. //class. This means we need to add a .getBytes() call to extract the pointer
  958. //Also if we ever have grandchild nested classes they will need to change where the builder is defined.
  959. if (activityRequiringCast && (cur.queryActivity() == activityRequiringCast))
  960. {
  961. HqlExprArray args;
  962. args.append(*LINK(src));
  963. src.setown(translator.bindTranslatedFunctionCall(getBytesFromBuilderId, args));
  964. }
  965. }
  966. startCtx.addAssign(bound, src);
  967. }
  968. }
  969. associateCursors(declareCtx, startCtx, childLocalisation);
  970. }
  971. bool ParentExtract::canEvaluate(IHqlExpression * expr)
  972. {
  973. if (!buildctx)
  974. return false;
  975. return translator.canEvaluateInContext(*buildctx, expr);
  976. }
  977. void ParentExtract::endChildActivity()
  978. {
  979. }
  980. bool ParentExtract::requiresOnStart() const
  981. {
  982. switch (type)
  983. {
  984. case PETchild:
  985. case PETremote:
  986. case PETloop:
  987. case PETlibrary:
  988. return true;
  989. case PETnested:
  990. case PETcallback:
  991. return container->requiresOnStart();
  992. default:
  993. throwUnexpected();
  994. }
  995. }
  996. bool ParentExtract::insideChildQuery() const
  997. {
  998. switch (type)
  999. {
  1000. case PETchild:
  1001. return true;
  1002. }
  1003. return container->insideChildQuery();
  1004. }
  1005. bool ParentExtract::areGraphResultsAccessible(IHqlExpression * searchGraphId) const
  1006. {
  1007. if (graphId == searchGraphId)
  1008. return true;
  1009. switch (type)
  1010. {
  1011. case PETloop:
  1012. return container->areGraphResultsAccessible(searchGraphId);
  1013. }
  1014. return false;
  1015. }
  1016. void ParentExtract::endCreateExtract(CHqlBoundExpr & boundExtract)
  1017. {
  1018. //NB: This can be called more than once - so need to be careful about any processing that
  1019. //is done in here.
  1020. childSerialization->setBuilder(NULL);
  1021. CHqlBoundExpr boundSize;
  1022. translator.getRecordSize(*buildctx, serialization->queryDataset(), boundSize);
  1023. boundExtract.length.set(boundSize.expr);
  1024. boundExtract.expr.set(boundBuilder.expr);
  1025. }
  1026. void ParentExtract::endUseExtract(BuildCtx & ctx)
  1027. {
  1028. childSerialization->finalize();
  1029. //Not so sure about the lifetime of this. If the extract was saved for a later occasion (e.g., prefetch project) then may be destroyed too soon.
  1030. if (canDestroyExtract)
  1031. translator.doGenerateMetaDestruct(ctx, serialization->queryDataset(), childSerialization->queryRecord());
  1032. }
  1033. void ParentExtract::buildAssign(IHqlExpression * serializedTarget, IHqlExpression * originalValue)
  1034. {
  1035. translator.buildAssign(*buildctx, serializedTarget, originalValue);
  1036. }
  1037. //This function converts an expression evaluated in the parent to an expression that can be evaluated in the child.
  1038. //Note, all extracts from all parent colocal activities are cloned into the child activity, so same expression is used to access them in the child
  1039. void ParentExtract::ensureAccessible(BuildCtx & ctx, IHqlExpression * expr, const CHqlBoundExpr & bound, CHqlBoundExpr & tgt, IHqlExpression * colocal)
  1040. {
  1041. //MORE: Need to know whether it is
  1042. //a) a member variable b) a serialization member or c) local and needs seerializing.
  1043. //so when then do
  1044. //a) add colcocal-> or create local references to the variables.
  1045. //b) don't need to do anything
  1046. //c) need to serialize and return value from that.
  1047. //Probably need to tag global declares with an attribute - e.g., typemod_member
  1048. //If activity is not colocal then always need to serialize
  1049. if (colocal)
  1050. {
  1051. //I wish I didn't have to do this. wrapper classes have a cast added when they are converted to
  1052. //expressions. I should probably look at removing it, but until that happens they need to be
  1053. //stripped so the member variables get matched correctly.
  1054. IHqlExpression * boundExpr = bound.expr;
  1055. if (boundExpr->getOperator() == no_implicitcast)
  1056. {
  1057. IHqlExpression * uncast = boundExpr->queryChild(0);
  1058. if (hasModifier(uncast->queryType(), typemod_wrapper) &&
  1059. (queryUnqualifiedType(boundExpr->queryType()) == queryUnqualifiedType(uncast->queryType())))
  1060. boundExpr = uncast;
  1061. }
  1062. //If already bound into a serialization then no need to do anything.
  1063. ITypeInfo * serializedModifier = queryModifier(boundExpr->queryType(), typemod_serialized);
  1064. if (serializedModifier)
  1065. {
  1066. //If serialized rebind the expression, rather than using the bound, otherwise temporary variables used for sizes
  1067. //of fields can get lost (see extractbug.hql) for an example
  1068. IHqlExpression * originalMapped = static_cast<IHqlExpression *>(serializedModifier->queryModifierExtra());
  1069. translator.buildAnyExpr(ctx, originalMapped, tgt);
  1070. tgt.expr.setown(addExpressionModifier(tgt.expr, typemod_serialized, originalMapped));
  1071. return;
  1072. }
  1073. //Special case where this extract is used for member functions within the same class
  1074. if (hasModifier(boundExpr->queryType(), typemod_member))
  1075. {
  1076. if (colocal == colocalSameClassPreserveExpr)
  1077. {
  1078. tgt.set(bound);
  1079. return;
  1080. }
  1081. //Need to create new (reference) members in the new class, and code to assign them in
  1082. //the setParent() call.
  1083. tgt.isAll.setown(addMemberSelector(bound.isAll, colocal));
  1084. tgt.count.setown(addMemberSelector(bound.count, colocal));
  1085. tgt.length.setown(addMemberSelector(bound.length, colocal));
  1086. tgt.expr.setown(addMemberSelector(boundExpr, colocal));
  1087. return;
  1088. }
  1089. }
  1090. //nonLocal child and local variables to a colocal child.
  1091. assertex(childSerialization);
  1092. OwnedHqlExpr value = bound.getTranslatedExpr();
  1093. OwnedHqlExpr mapped = childSerialization->ensureSerialized(value, canDestroyExtract ? colocal : NULL, false);
  1094. translator.buildAnyExpr(ctx, mapped, tgt);
  1095. //add a serialized modifier to the type so we can track expressions already serialized, the extra saves the original mappiing
  1096. tgt.expr.setown(addExpressionModifier(tgt.expr, typemod_serialized, mapped));
  1097. ctx.associateExpr(expr, tgt);
  1098. }
  1099. void ParentExtract::addSerializedExpression(IHqlExpression * value, ITypeInfo * type)
  1100. {
  1101. OwnedHqlExpr mapped = childSerialization->addSerializedValue(value, type, NULL, false);
  1102. }
  1103. AliasKind ParentExtract::evaluateExpression(BuildCtx & ctx, IHqlExpression * value, CHqlBoundExpr & tgt, IHqlExpression * colocal, bool evaluateLocally)
  1104. {
  1105. CHqlBoundExpr bound;
  1106. AliasKind kind;
  1107. if (buildctx)
  1108. kind = translator.doBuildAliasValue(*buildctx, value, bound);
  1109. else
  1110. kind = container->evaluateExpression(ctx, value, bound, evaluateLocally);
  1111. if (kind != NotFoundAlias)
  1112. ensureAccessible(ctx, value, bound, tgt, colocal);
  1113. if (!colocal)
  1114. return RuntimeAlias;
  1115. return kind;
  1116. }
  1117. void ParentExtract::gatherActiveRows(BuildCtx & ctx)
  1118. {
  1119. //Collect a list of cursors together... NB these are in reverse order..
  1120. CursorArray activeRows;
  1121. RowAssociationIterator iter(ctx);
  1122. ForEach(iter)
  1123. {
  1124. BoundRow & cur = iter.get();
  1125. if ((cur.querySide() != no_self) && !cur.isBuilder())
  1126. {
  1127. bool ok = true;
  1128. IHqlExpression * represents = cur.queryDataset();
  1129. switch(represents->getOperator())
  1130. {
  1131. case no_null:
  1132. ok = !represents->hasAttribute(clearAtom); // Don't serialize rows used as default clear rows
  1133. break;
  1134. case no_anon:
  1135. ok = !represents->hasAttribute(selfAtom);
  1136. break;
  1137. default:
  1138. if (cur.isResultAlias())
  1139. ok = false;
  1140. //MORE: This should only be done if the child query etc. actually references the datarow.
  1141. //ideally from a colocal activity.
  1142. #if 0
  1143. //Theoretically the following is true. However it can mean that for the cost of serializing an extra 4 bytes here and
  1144. //there you end up serializing several hundred in some other situations.
  1145. //So on balance, worth including them even if strictly unnecessary.
  1146. if (represents->isDatarow() && !isAlwaysActiveRow(represents))
  1147. ok = false;
  1148. #endif
  1149. break;
  1150. }
  1151. if (ok)
  1152. activeRows.append(OLINK(cur));
  1153. }
  1154. }
  1155. //NB: Serialization needs to be added processed first in the child scope
  1156. if (serialization)
  1157. {
  1158. childSerialization = (SerializationRow *)serialization->clone(boundExtract.expr);
  1159. colocalBoundCursors.append(*childSerialization);
  1160. nonlocalBoundCursors.append(*LINK(childSerialization));
  1161. }
  1162. //MORE: Should possibly create two sets of cursors one if children are colocal, other if children aren't
  1163. //so colocal cursors can be used wherever possible. Would change following to localisation != GraphNonLocal
  1164. //and remove else
  1165. if (localisation == GraphCoLocal || localisation == GraphCoNonLocal)
  1166. {
  1167. OwnedHqlExpr colocal = createQuoted("colocal", makeVoidType());
  1168. ForEachItemInRev(i, activeRows)
  1169. {
  1170. BoundRow & cur = activeRows.item(i);
  1171. IHqlExpression * bound = cur.queryBound();
  1172. OwnedHqlExpr colocalBound = addMemberSelector(bound, colocal);
  1173. BoundRow * newRow = NULL;
  1174. if (cur.isSerialization() || cur.queryDataset()->getOperator() == no_pseudods)
  1175. {
  1176. //an extract/serialization. Same cursor is added to all colocal children.
  1177. newRow = LINK(&cur);
  1178. }
  1179. else if (cur.queryAliasExpansion() || cur.isNonLocal())
  1180. {
  1181. //A cursor who's pointer has already been serialized into an extract
  1182. //NB: Alias expansions get rebound when they are bound into the context.
  1183. newRow = LINK(&cur);
  1184. }
  1185. else if (!cur.isBinary())
  1186. {
  1187. //CSV and xml datasets need their elements serialized into the parent extract
  1188. newRow = new NonLocalIndirectRow(cur, NULL, childSerialization);
  1189. }
  1190. else if (serialization)
  1191. {
  1192. //A cursor active in the current scope => add it to the extract, and create an alias in the
  1193. //child contexts.
  1194. //Need to add these fields to the extract record, and do the assignments at the point of call
  1195. Owned<ITypeInfo> fieldType = makeRowReferenceType(cur.queryDataset());
  1196. if (hasOutOfLineModifier(bound->queryType()))
  1197. fieldType.setown(makeAttributeModifier(LINK(fieldType), getLinkCountedAttr()));
  1198. OwnedHqlExpr expandedAlias = serialization->createField(NULL, fieldType);
  1199. OwnedHqlExpr castSource = createValue(no_implicitcast, LINK(fieldType), LINK(bound));
  1200. OwnedHqlExpr translated = createTranslated(castSource);
  1201. translator.buildAssign(ctx, expandedAlias, translated);
  1202. newRow = new BoundAliasRow(cur, NULL, expandedAlias);
  1203. newRow->setInherited(true);
  1204. }
  1205. else
  1206. {
  1207. //A row defined in a activity class instance, being used from a nested class.
  1208. //E.g., row used to clear a value. Should be possible to recreate in the nested class
  1209. if (cur.getKind() != AssocRow)
  1210. throwUnexpected();
  1211. }
  1212. if (newRow)
  1213. colocalBoundCursors.append(* newRow);
  1214. }
  1215. //May also need to associate any counters in scope. May be more than one!
  1216. //Possibly other things like filepositions, ..... How do I recognise them all?
  1217. //Use a dynamic offset map to represent whatever is serialized.
  1218. //MORE: This may actually mean we want to bind these non-row contexts on demand - but then it is harder to share.
  1219. }
  1220. if (localisation != GraphCoLocal)
  1221. {
  1222. ForEachItemInRev(i, activeRows)
  1223. {
  1224. BoundRow & cur = activeRows.item(i);
  1225. nonlocalBoundCursors.append(*new NonLocalIndirectRow(cur, NULL, childSerialization));
  1226. }
  1227. }
  1228. }
  1229. //----------------------------------------------------------------------------
  1230. ParentExtract * HqlCppTranslator::createExtractBuilder(BuildCtx & ctx, PEtype type, IHqlExpression * graphId, GraphLocalisation localisation, bool doDeclare)
  1231. {
  1232. ParentExtract * extractor = NULL;
  1233. // if (localisation == GraphCoLocal)
  1234. // extract = checkForPreexistingExtract - find a bound association before a row association is found;
  1235. if (!extractor)
  1236. {
  1237. extractor = new ParentExtract(*this, type, graphId, localisation, queryEvalContext(ctx));
  1238. extractor->beginCreateExtract(ctx, doDeclare);
  1239. }
  1240. else
  1241. extractor->beginReuseExtract();
  1242. return extractor;
  1243. }
  1244. ParentExtract * HqlCppTranslator::createExtractBuilder(BuildCtx & ctx, PEtype type, IHqlExpression * graphId, IHqlExpression * expr, bool doDeclare)
  1245. {
  1246. if (isAlwaysCoLocal())
  1247. return createExtractBuilder(ctx, type, graphId, GraphCoLocal, true);
  1248. bool isInsideChildQuery = (type == PETchild) || insideChildQuery(ctx);
  1249. return createExtractBuilder(ctx, type, graphId, getGraphLocalisation(expr, isInsideChildQuery), true);
  1250. }
  1251. //----------------------------------------------------------------------------
  1252. AliasKind HqlCppTranslator::buildExprInCorrectContext(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, bool evaluateLocally)
  1253. {
  1254. EvalContext * instance = queryEvalContext(ctx);
  1255. if (instance)
  1256. return instance->evaluateExpression(ctx, expr, tgt, evaluateLocally);
  1257. if (ctx.getMatchExpr(expr, tgt))
  1258. return RuntimeAlias;
  1259. return NotFoundAlias;
  1260. }
  1261. //---------------------------------------------------------------------------
  1262. void CtxCollection::createFunctionStructure(HqlCppTranslator & translator, BuildCtx & ctx, bool canEvaluate, const char * serializeFunc)
  1263. {
  1264. clonectx.set(ctx);
  1265. clonectx.addGroup();
  1266. if (serializeFunc)
  1267. {
  1268. BuildCtx condctx(ctx);
  1269. OwnedHqlExpr cond = createVariable("in", makeBoolType());
  1270. IHqlStmt * stmt = condctx.addFilter(cond);
  1271. deserializectx.setown(new BuildCtx(condctx));
  1272. condctx.selectElse(stmt);
  1273. evalctx.setown(new BuildCtx(condctx));
  1274. //virtual void serializeCreateContext(MemoryBuffer & out)
  1275. serializectx.setown(new BuildCtx(declarectx));
  1276. StringBuffer s;
  1277. s.append("virtual void ").append(serializeFunc).append("(MemoryBuffer & out)");
  1278. serializectx->addQuotedCompoundOpt(s.str());
  1279. } else if (canEvaluate)
  1280. {
  1281. evalctx.setown(new BuildCtx(ctx));
  1282. evalctx->addGroup();
  1283. }
  1284. childctx.set(ctx);
  1285. }
  1286. //---------------------------------------------------------------------------
  1287. EvalContext::EvalContext(HqlCppTranslator & _translator, ParentExtract * _parentExtract, EvalContext * _parent)
  1288. : HqlExprAssociation(classMarkerExpr), translator(_translator)
  1289. {
  1290. parent = _parent;
  1291. parentExtract.set(_parentExtract);
  1292. }
  1293. IHqlExpression * EvalContext::createGraphLookup(unique_id_t id, bool isChild)
  1294. {
  1295. assertex(parent);
  1296. return parent->createGraphLookup(id, isChild);
  1297. }
  1298. bool EvalContext::needToEvaluateLocally(BuildCtx & ctx, IHqlExpression * expr)
  1299. {
  1300. return mustEvaluateInContext(ctx, expr);
  1301. }
  1302. bool EvalContext::evaluateInParent(BuildCtx & ctx, IHqlExpression * expr, bool hasOnStart)
  1303. {
  1304. if (!parent)
  1305. return false;
  1306. if (parent->isLibraryContext())
  1307. return (expr->getOperator() == no_libraryinput);
  1308. switch (expr->getOperator())
  1309. {
  1310. case no_libraryinput:
  1311. case no_rows:
  1312. case no_filepos:
  1313. case no_file_logicalname:
  1314. case no_counter:
  1315. case no_variable: // this really should happen
  1316. return true; // would have been bound if found
  1317. case no_id2blob:
  1318. return !queryBlobHelper(ctx, expr->queryChild(0));
  1319. case no_loopcounter:
  1320. return !translator.isCurrentActiveGraph(ctx, expr->queryChild(0));
  1321. case no_getgraphresult:
  1322. return !translator.isCurrentActiveGraph(ctx, expr->queryChild(1));
  1323. case no_getresult:
  1324. case no_workunit_dataset:
  1325. return translator.needToSerializeToSlave(expr);
  1326. case no_failcode:
  1327. case no_failmessage:
  1328. case no_fail:
  1329. return !ctx.queryMatchExpr(activeFailureMarkerExpr);
  1330. case no_xmltext:
  1331. case no_xmlunicode:
  1332. case no_xmlproject:
  1333. return !ctx.queryMatchExpr(xmlColumnProviderMarkerExpr);
  1334. case no_matched:
  1335. case no_matchtext:
  1336. case no_matchunicode:
  1337. case no_matchlength:
  1338. case no_matchposition:
  1339. case no_matchrow:
  1340. case no_matchutf8:
  1341. return !ctx.queryMatchExpr(activeNlpMarkerExpr);
  1342. case no_matchattr:
  1343. return !ctx.queryMatchExpr(activeNlpMarkerExpr) && !ctx.queryMatchExpr(activeProductionMarkerExpr);
  1344. }
  1345. //Some function of the above => eveluate where we are...
  1346. if (isContextDependent(expr))
  1347. return false;
  1348. if (isIndependentOfScope(expr))
  1349. return true;//isColocal();
  1350. //If can evaluate in parent's start context then always worth doing there.
  1351. if (parent->isRowInvariant(expr))
  1352. return true;
  1353. if (parentExtract && parentExtract->canEvaluate(expr))
  1354. {
  1355. if (!isColocal() || !hasOnStart)
  1356. return true;
  1357. if (translator.queryEvaluateCoLocalRowInvariantInExtract())
  1358. return true;
  1359. return false;
  1360. }
  1361. return false;
  1362. }
  1363. bool EvalContext::insideChildQuery() const
  1364. {
  1365. if (parentExtract)
  1366. return parentExtract->insideChildQuery();
  1367. if (parent)
  1368. return parent->insideChildQuery();
  1369. return false;
  1370. }
  1371. bool EvalContext::requiresOnStart() const
  1372. {
  1373. if (parentExtract)
  1374. return parentExtract->requiresOnStart();
  1375. if (parent)
  1376. return parent->requiresOnStart();
  1377. return false;
  1378. }
  1379. ActivityInstance * EvalContext::queryActivity()
  1380. {
  1381. if (parent)
  1382. return parent->queryActivity();
  1383. return NULL;
  1384. }
  1385. bool EvalContext::areGraphResultsAccessible(IHqlExpression * searchGraphId) const
  1386. {
  1387. if (parentExtract)
  1388. return parentExtract->areGraphResultsAccessible(searchGraphId);
  1389. if (parent)
  1390. return parent->areGraphResultsAccessible(searchGraphId);
  1391. return false;
  1392. }
  1393. IHqlExpression * HqlCppTranslator::doCreateGraphLookup(BuildCtx & declarectx, BuildCtx & resolvectx, unique_id_t id, const char * activity, bool isChild)
  1394. {
  1395. StringBuffer s, var;
  1396. if (isChild)
  1397. {
  1398. appendUniqueId(var.append("child"), id);
  1399. s.clear().append("Owned<IThorChildGraph> ").append(var).append(";");
  1400. declarectx.addQuoted(s);
  1401. s.clear().append(var).append(".setown(ctx->resolveChildQuery(").append(id).append(",").append(activity).append("));");
  1402. resolvectx.addQuoted(s);
  1403. }
  1404. else
  1405. {
  1406. //NB: resolveLocalQuery (unlike children) can't link otherwise you get a circular dependency.
  1407. appendUniqueId(var.append("graph"), id);
  1408. OwnedHqlExpr memberExpr = createQuoted(var, makeBoolType());
  1409. if (declarectx.queryMatchExpr(memberExpr))
  1410. return memberExpr.getClear();
  1411. s.clear().append("IEclGraphResults * ").append(var).append(";");
  1412. declarectx.addQuoted(s);
  1413. declarectx.associateExpr(memberExpr, memberExpr);
  1414. s.clear().append(var).append(" = ctx->resolveLocalQuery(").append(id).append(");");
  1415. resolvectx.addQuoted(s);
  1416. }
  1417. return createQuoted(var, makeBoolType());
  1418. }
  1419. //---------------------------------------------------------------------------
  1420. ClassEvalContext::ClassEvalContext(HqlCppTranslator & _translator, ParentExtract * _parentExtract, EvalContext * _parent, BuildCtx & createctx, BuildCtx & startctx)
  1421. : EvalContext(_translator, _parentExtract, _parent), onCreate(createctx), onStart(startctx)
  1422. {
  1423. }
  1424. IHqlExpression * ClassEvalContext::cloneExprInClass(CtxCollection & ctxs, IHqlExpression * expr)
  1425. {
  1426. if (!expr)
  1427. return NULL;
  1428. if (expr->getOperator() != no_pselect)
  1429. return LINK(expr);
  1430. LinkedHqlExpr value = expr;
  1431. Linked<ITypeInfo> type = expr->queryType();
  1432. assertex(hasModifier(type, typemod_member));
  1433. if (isTypePassedByAddress(type))
  1434. {
  1435. value.setown(getPointer(value));
  1436. type.setown(value->getType());
  1437. }
  1438. else
  1439. {
  1440. if (hasModifier(type, typemod_wrapper))
  1441. {
  1442. type.setown(removeModifier(type, typemod_wrapper));
  1443. value.setown(createValue(no_implicitcast, LINK(type), LINK(value)));
  1444. }
  1445. }
  1446. if (!hasModifier(type, typemod_member))
  1447. type.setown(makeModifier(value->getType(), typemod_member));
  1448. OwnedHqlExpr decl = ctxs.declarectx.getTempDeclare(type, NULL);
  1449. ctxs.clonectx.addAssign(decl, value);
  1450. return LINK(decl);
  1451. }
  1452. void ClassEvalContext::cloneAliasInClass(CtxCollection & ctxs, const CHqlBoundExpr & bound, CHqlBoundExpr & tgt)
  1453. {
  1454. // assertex(!bound.count);
  1455. ensureHelpersExist();
  1456. tgt.isAll.setown(cloneExprInClass(ctxs, bound.isAll));
  1457. tgt.count.setown(cloneExprInClass(ctxs, bound.count));
  1458. tgt.length.setown(cloneExprInClass(ctxs, bound.length));
  1459. tgt.expr.setown(cloneExprInClass(ctxs, bound.expr));
  1460. }
  1461. void ClassEvalContext::createMemberAlias(CtxCollection & ctxs, BuildCtx & ctx, IHqlExpression * value, CHqlBoundExpr & tgt)
  1462. {
  1463. if (ctxs.declarectx.getMatchExpr(value, tgt))
  1464. return;
  1465. //Should never be called for a nested class - that should be done in the context.
  1466. assertex(ctxs.evalctx != NULL);
  1467. translator.expandAliases(*ctxs.evalctx, value);
  1468. IAtom * serializeForm = internalAtom; // The format of serialized expressions in memory must match the internal serialization format
  1469. CHqlBoundTarget tempTarget;
  1470. if (translator.needToSerializeToSlave(value))
  1471. {
  1472. translator.buildTempExpr(*ctxs.evalctx, ctxs.declarectx, tempTarget, value, FormatNatural, false);
  1473. ensureSerialized(ctxs, tempTarget, serializeForm);
  1474. }
  1475. else
  1476. {
  1477. translator.buildTempExpr(ctxs.clonectx, ctxs.declarectx, tempTarget, value, FormatNatural, false);
  1478. }
  1479. tgt.setFromTarget(tempTarget);
  1480. ctxs.declarectx.associateExpr(value, tgt);
  1481. }
  1482. void ClassEvalContext::doCallNestedHelpers(const char * member, const char * activity)
  1483. {
  1484. StringBuffer s;
  1485. onCreate.childctx.addQuoted(s.clear().append(member).append(".onCreate(ctx, ").append(activity).append(");"));
  1486. if (requiresOnStart())
  1487. onStart.childctx.addQuoted(s.clear().append(member).append(".onStart();"));
  1488. }
  1489. void ClassEvalContext::ensureSerialized(CtxCollection & ctxs, const CHqlBoundTarget & target, IAtom * serializeForm)
  1490. {
  1491. if (ctxs.serializectx)
  1492. translator.ensureSerialized(target, *ctxs.serializectx, *ctxs.deserializectx, "*in", "out", serializeForm);
  1493. }
  1494. AliasKind ClassEvalContext::evaluateExpression(BuildCtx & ctx, IHqlExpression * value, CHqlBoundExpr & tgt, bool evaluateLocally)
  1495. {
  1496. if (onStart.declarectx.getMatchExpr(value, tgt))
  1497. {
  1498. if (onCreate.declarectx.queryMatchExpr(value))
  1499. return CreateTimeAlias;
  1500. return StartTimeAlias;
  1501. }
  1502. if (ctx.getMatchExpr(value, tgt))
  1503. return RuntimeAlias;
  1504. ///If contains a nasty like counter/matchtext then evaluate here for the moment...
  1505. if (!needToEvaluateLocally(ctx, value))
  1506. {
  1507. if (evaluateInParent(ctx, value, (onStart.evalctx != NULL)))
  1508. {
  1509. if (!parentExtract)
  1510. throwError(HQLERR_NoParentExtract);
  1511. CHqlBoundExpr bound;
  1512. AliasKind kind = parentExtract->evaluateExpression(ctx, value, bound, colocalMember, evaluateLocally);
  1513. switch (kind)
  1514. {
  1515. case RuntimeAlias:
  1516. tgt.set(bound);
  1517. break;
  1518. case CreateTimeAlias:
  1519. cloneAliasInClass(onCreate, bound, tgt);
  1520. onCreate.declarectx.associateExpr(value, tgt);
  1521. break;
  1522. case StartTimeAlias:
  1523. cloneAliasInClass(onStart, bound, tgt);
  1524. onStart.declarectx.associateExpr(value, tgt);
  1525. break;
  1526. }
  1527. return kind;
  1528. }
  1529. if (!evaluateLocally)
  1530. return NotFoundAlias;
  1531. if (!isContextDependentExceptGraph(value))
  1532. {
  1533. if (!isContextDependent(value) && !containsActiveDataset(value) && value->isIndependentOfScope())
  1534. {
  1535. createMemberAlias(onCreate, ctx, value, tgt);
  1536. return CreateTimeAlias;
  1537. }
  1538. bool evaluateInOnStart = false;
  1539. if (isRowInvariant(value) && onStart.evalctx)
  1540. {
  1541. evaluateInOnStart = true;
  1542. if (isGraphDependent(value))
  1543. {
  1544. //Need to find out which graph it is dependent on, and check that isn't defined locally
  1545. HqlExprCopyArray graphs;
  1546. gatherGraphReferences(graphs, value, true);
  1547. if (graphs.ordinality() != 0)
  1548. evaluateInOnStart = false;
  1549. }
  1550. }
  1551. if (evaluateInOnStart)
  1552. {
  1553. translator.traceExpression("alias", value);
  1554. createMemberAlias(onStart, ctx, value, tgt);
  1555. return StartTimeAlias;
  1556. }
  1557. }
  1558. }
  1559. if (!evaluateLocally)
  1560. return NotFoundAlias;
  1561. translator.expandAliases(ctx, value);
  1562. translator.buildTempExpr(ctx, value, tgt);
  1563. return RuntimeAlias;
  1564. }
  1565. void ClassEvalContext::tempCompatiablityEnsureSerialized(const CHqlBoundTarget & tgt)
  1566. {
  1567. IAtom * serializeForm = internalAtom; // The format of serialized expressions in memory must match the internal serialization format
  1568. ensureSerialized(onCreate, tgt, serializeForm);
  1569. }
  1570. bool ClassEvalContext::isRowInvariant(IHqlExpression * expr)
  1571. {
  1572. return translator.canEvaluateInContext(onStart.declarectx, expr);
  1573. }
  1574. //If this is called on something that is shared, then need to ensure it will be valid for all subsequent calls
  1575. //therefore, we can't test whether it is inside an onCreate function since that may not be true the first time, but may be subsequent times.
  1576. //and we need to generate the code where we guarantee it will have been executed before (almost) everything else.
  1577. //if not independent, then it can't be shared - so generate it after the code to extract temporary values
  1578. bool ClassEvalContext::getInvariantMemberContext(BuildCtx * ctx, BuildCtx * * declarectx, BuildCtx * * initctx, bool isIndependentMaybeShared, bool invariantEachStart)
  1579. {
  1580. if (invariantEachStart)
  1581. {
  1582. if (declarectx)
  1583. *declarectx = &onStart.declarectx;
  1584. if (initctx && (!ctx || isIndependentMaybeShared || !ctx->queryMatchExpr(insideOnStartMarker)))
  1585. {
  1586. ensureHelpersExist();
  1587. if (isIndependentMaybeShared)
  1588. *initctx = &onStart.clonectx; // before anything else happens.
  1589. else
  1590. *initctx = &onStart.childctx; // after global variables have been read or de-serialized.
  1591. }
  1592. }
  1593. else
  1594. {
  1595. if (declarectx)
  1596. *declarectx = &onCreate.declarectx;
  1597. if (initctx && (!ctx || isIndependentMaybeShared || !ctx->queryMatchExpr(insideOnCreateMarker)))
  1598. {
  1599. ensureHelpersExist();
  1600. if (isIndependentMaybeShared)
  1601. *initctx = &onCreate.clonectx; // before anything else happens.
  1602. else
  1603. *initctx = &onCreate.childctx; // after global variables have been read or de-serialized.
  1604. }
  1605. }
  1606. return true;
  1607. }
  1608. //---------------------------------------------------------------------------
  1609. GlobalClassEvalContext::GlobalClassEvalContext(HqlCppTranslator & _translator, ParentExtract * _parentExtract, EvalContext * _parent, BuildCtx & createctx, BuildCtx & startctx)
  1610. : ClassEvalContext(_translator, _parentExtract, _parent, createctx, startctx)
  1611. {
  1612. }
  1613. void GlobalClassEvalContext::ensureHelpersExist()
  1614. {
  1615. }
  1616. void GlobalClassEvalContext::callNestedHelpers(const char * member)
  1617. {
  1618. doCallNestedHelpers(member, "this");
  1619. }
  1620. //---------------------------------------------------------------------------
  1621. ActivityEvalContext::ActivityEvalContext(HqlCppTranslator & _translator, ActivityInstance * _activity, ParentExtract * _parentExtract, EvalContext * _parent, IHqlExpression * _colocal, BuildCtx & createctx, BuildCtx & startctx)
  1622. : ClassEvalContext(_translator, _parentExtract, _parent, createctx, startctx)
  1623. {
  1624. activity = _activity;
  1625. colocalMember.set(_colocal);
  1626. }
  1627. IHqlExpression * ActivityEvalContext::createGraphLookup(unique_id_t id, bool isChild)
  1628. {
  1629. return translator.doCreateGraphLookup(onCreate.declarectx, onCreate.childctx, id, "this", isChild);
  1630. }
  1631. void ActivityEvalContext::ensureHelpersExist()
  1632. {
  1633. }
  1634. void ActivityEvalContext::callNestedHelpers(const char * member)
  1635. {
  1636. doCallNestedHelpers(member, "this");
  1637. }
  1638. ActivityInstance * ActivityEvalContext::queryActivity()
  1639. {
  1640. return activity;
  1641. }
  1642. //---------------------------------------------------------------------------
  1643. NestedEvalContext::NestedEvalContext(HqlCppTranslator & _translator, const char * _memberName, ParentExtract * _parentExtract, EvalContext * _parent, IHqlExpression * _colocal, BuildCtx & createctx, BuildCtx & startctx)
  1644. : ClassEvalContext(_translator, _parentExtract, _parent, createctx, startctx)
  1645. {
  1646. colocalMember.set(_colocal);
  1647. helpersExist = false;
  1648. memberName.set(_memberName);
  1649. }
  1650. IHqlExpression * NestedEvalContext::createGraphLookup(unique_id_t id, bool isChild)
  1651. {
  1652. ensureHelpersExist();
  1653. return translator.doCreateGraphLookup(onCreate.declarectx, onCreate.childctx, id, "activity", isChild);
  1654. }
  1655. void NestedEvalContext::ensureHelpersExist()
  1656. {
  1657. if (!helpersExist)
  1658. {
  1659. if (parent)
  1660. parent->ensureHelpersExist();
  1661. StringBuffer s;
  1662. ActivityInstance * rootActivity = queryActivity();
  1663. //void onStart(ICodeContext * _ctx, <ActivityClass> * _activity)
  1664. BuildCtx oncreatectx(onCreate.declarectx);
  1665. oncreatectx.addQuotedCompound(s.clear().append("inline void onCreate(ICodeContext * _ctx, ").append(rootActivity->className).append(" * _activity)"));
  1666. oncreatectx.addQuoted(s.clear().append("activity = _activity;"));
  1667. oncreatectx.addQuoted("ctx = _ctx;");
  1668. onCreate.declarectx.addQuoted("ICodeContext * ctx;");
  1669. onCreate.declarectx.addQuoted(s.clear().append(rootActivity->className).append(" * activity;"));
  1670. onCreate.declarectx.associateExpr(codeContextMarkerExpr, codeContextMarkerExpr);
  1671. onCreate.createFunctionStructure(translator, oncreatectx, false, NULL);
  1672. //void onStart(const byte * parentExtract)
  1673. BuildCtx onstartctx(onStart.declarectx);
  1674. if (requiresOnStart())
  1675. {
  1676. onstartctx.addQuotedCompound("inline void onStart()");
  1677. if (parentExtract)
  1678. parentExtract->beginChildActivity(onStart.declarectx, onstartctx, GraphCoLocal, colocalMember, true, parentExtract->canSerializeFields(), NULL);
  1679. onstartctx.associateExpr(insideOnStartMarker, NULL);
  1680. onStart.createFunctionStructure(translator, onstartctx, false, NULL);
  1681. }
  1682. parent->callNestedHelpers(memberName);
  1683. helpersExist = true;
  1684. }
  1685. }
  1686. void NestedEvalContext::initContext()
  1687. {
  1688. if (requiresOnStart())
  1689. ensureHelpersExist();
  1690. else if (parentExtract && parentExtract->canSerializeFields())
  1691. {
  1692. ensureHelpersExist();
  1693. parentExtract->associateCursors(onStart.declarectx, onStart.declarectx, GraphCoLocal);
  1694. }
  1695. }
  1696. bool NestedEvalContext::evaluateInParent(BuildCtx & ctx, IHqlExpression * expr, bool hasOnStart)
  1697. {
  1698. //This is a bit ugly. Latter condition is to cope with group aggregate callbacks
  1699. return parent->isRowInvariant(expr) || parentExtract->canEvaluate(expr);
  1700. }
  1701. void NestedEvalContext::callNestedHelpers(const char * member)
  1702. {
  1703. doCallNestedHelpers(member, "activity");
  1704. }
  1705. //---------------------------------------------------------------------------
  1706. MemberEvalContext::MemberEvalContext(HqlCppTranslator & _translator, ParentExtract * _parentExtract, EvalContext * _parent, BuildCtx & _ctx)
  1707. : EvalContext(_translator, _parentExtract, _parent), ctx(_ctx)
  1708. {
  1709. assertex(parent);
  1710. colocalMember.set(colocalSameClassPreserveExpr);
  1711. }
  1712. void MemberEvalContext::callNestedHelpers(const char * member)
  1713. {
  1714. parent->callNestedHelpers(member);
  1715. }
  1716. IHqlExpression * MemberEvalContext::createGraphLookup(unique_id_t id, bool isChild)
  1717. {
  1718. return parent->createGraphLookup(id, isChild);
  1719. }
  1720. AliasKind MemberEvalContext::evaluateExpression(BuildCtx & ctx, IHqlExpression * value, CHqlBoundExpr & tgt, bool evaluateLocally)
  1721. {
  1722. return parentExtract->evaluateExpression(ctx, value, tgt, colocalMember, evaluateLocally);
  1723. }
  1724. bool MemberEvalContext::isRowInvariant(IHqlExpression * expr)
  1725. {
  1726. return translator.canEvaluateInContext(ctx, expr);
  1727. }
  1728. void MemberEvalContext::ensureHelpersExist()
  1729. {
  1730. parent->ensureHelpersExist();
  1731. }
  1732. void MemberEvalContext::initContext()
  1733. {
  1734. parentExtract->associateCursors(ctx, ctx, GraphCoLocal);
  1735. }
  1736. bool MemberEvalContext::getInvariantMemberContext(BuildCtx * ctx, BuildCtx * * declarectx, BuildCtx * * initctx, bool isIndependentMaybeShared, bool invariantEachStart)
  1737. {
  1738. return parent->getInvariantMemberContext(ctx, declarectx, initctx, isIndependentMaybeShared, invariantEachStart);
  1739. }
  1740. void MemberEvalContext::tempCompatiablityEnsureSerialized(const CHqlBoundTarget & tgt)
  1741. {
  1742. parent->tempCompatiablityEnsureSerialized(tgt);
  1743. }
  1744. //---------------------------------------------------------------------------
  1745. void HqlCppTranslator::ensureContextAvailable(BuildCtx & ctx)
  1746. {
  1747. EvalContext * instance = queryEvalContext(ctx);
  1748. if (instance)
  1749. instance->ensureContextAvailable();
  1750. }
  1751. /*
  1752. The following is the structure that we are going to generate in the C++ for an activity
  1753. <<xx>> marks a context that is preserved
  1754. virtual void onCreate(ICodeContext * _ctx, IHThorArg * _colocal, MemoryBuffer * in) {
  1755. colocal = (c2*)_colocal;
  1756. ctx = _ctx;
  1757. <<onCreate.clonectx>>
  1758. // clone values from colocal-> into local variables.
  1759. if (in) {
  1760. //deserialize any values from
  1761. <<onCreate.deserializectx>>
  1762. }
  1763. else {
  1764. //evaluate any query-invariant values.
  1765. <<onCreate.evalctx>>
  1766. }
  1767. <<onCreate.childctx>>
  1768. // create graph lookups.
  1769. nestedObject.onCreate(ctx, this);
  1770. }
  1771. virtual void serializeCreateContext(MemoryBuffer & out) {
  1772. //serialize any query-invariant values
  1773. }
  1774. virtual void onStart(const byte * pe) {
  1775. extractVar = (byte *)pe;
  1776. <<onStart.clonectx>>
  1777. <<onStart.evalctx>>
  1778. <<onStart.childctx>>
  1779. nestedObject.onStart(pe);
  1780. }
  1781. NestedClass:
  1782. void onCreate(ICodeContext * _ctx, cH * _activity) {
  1783. activity = _activity;
  1784. ctx = _ctx;
  1785. <<onCreate.clonectx>>
  1786. <<onCreate.childctx>>
  1787. //clone values from parent class into this class
  1788. }
  1789. virtual void onStart(const byte * pe) {
  1790. extractVar = (byte *)pe; //This means that all values retrieved from the extract have the same format in the nested class
  1791. <<onStart.clonectx>>
  1792. <<onStart.childctx>>
  1793. }
  1794. virtual void onCreate(ICodeContext * _ctx, IHThorArg * _colocal) {
  1795. colocal = (c2*)_colocal;
  1796. ctx = _ctx;
  1797. <<onCreate.clonectx>>
  1798. // clone values from colocal-> into local variables.
  1799. <<onCreate.evalctx>>
  1800. // evaluate graph invariant expressions
  1801. //local graph lookups
  1802. commonCreate();
  1803. }
  1804. inline void commonCreate()
  1805. <<onCreate.childctx>>
  1806. // create child graph lookups.
  1807. // nestedObject.onCreate(ctx, this);
  1808. }
  1809. virtual void serializeContext(MemoryBuffer & out) {
  1810. //serialize any query-invariant values
  1811. }
  1812. virtual void onStart(const byte * pe) {
  1813. extractVar = (byte *)pe;
  1814. <<onStart.clonectx>>
  1815. <<onStart.evalctx>>
  1816. <<onStart.childctx>>
  1817. }
  1818. inline void commonOnStart()
  1819. {
  1820. nestedObject.onStart(pe);
  1821. }
  1822. virtual void onCreateStart(ICodeContext * _ctx, MemoryBuffer * in, const byte * pe)
  1823. {
  1824. ctx = _ctx;
  1825. colocal = NULL;
  1826. <<onCreate.deserialize>>
  1827. commonCreate();
  1828. extractVar = (byte *)pe;
  1829. commonOnStart();
  1830. }
  1831. xxx.clonectx - The point where members are cloned from colocal parent activities and parent classes.
  1832. xxx.evalctx - The point where anything that needs to be evaluated is placed.
  1833. Notes on onCreate/onStart
  1834. a) onStart needs to be created/called for child activities (and their nested classes) in order to bind the variables correctly.
  1835. b) onCreate needs to be called for nested classes in order to access grand parent colocal extracts.
  1836. c) If a function is created it needs to be called from parent (which stops us using compoundOpt unless the base class contains a default implementation)
  1837. d) We don't want to call onCReate() + onStart all the time because it generates lots of extra code.
  1838. =>
  1839. 1) For any child class (activity or nested) we always generate + call them even if they don't have any extra code.
  1840. 2) For nested classes of top level activities the functions are generated and called when something is added that needs it.
  1841. Notes
  1842. * Serialization + evaluation of expressions
  1843. a) if an activity is not executed remotely there is no need to serialize on Create
  1844. => serialize remote roxie activites
  1845. => serialize remote thor child activities.
  1846. => optionally serialize all root thor activities depending on whether Jake makes use of it.
  1847. => add a debug flag to force serialization for the purpose of testing.
  1848. b) table-invariant values.
  1849. => evaluated once in the onCreate() of the activity. Not retrieved from a parent activity.
  1850. c) row invariant values
  1851. => if a value can be evaluated in a parent extract context then it is evaluated there, and
  1852. then cloned/serialized into the current context.
  1853. d) since row invariant values are calulated in the parent context there is nothing to be gained from serializing the
  1854. start context. => Remove the code + methods from the eclagent helper definition.
  1855. e) Nested class values.
  1856. These are always evaluated in the parent context, and then cloned into the current class.
  1857. - The extracts from all parent colocal activities are cloned into the child activity
  1858. - All members from a parent activity are copied into members in the current activity + accessed from there.
  1859. [This is partly to simplify the access expressions from colocal children]
  1860. Evaluation of expressions:
  1861. o Table-invariant
  1862. - Do in the onCreate of the activity.
  1863. - Could also serialize from parent to child queries, so only evaluated once.
  1864. = Trade off between re-eveluation and cloning/extra data serialized to child query.
  1865. o Other expressions
  1866. - Each expression has a minimal level it can be evaluated at.
  1867. - main decision is whether to evaluate in the parent's build ctx, or in a local onStart()
  1868. = If there is no onStart, then always evaluate in parent build ctx.
  1869. = If there is an onStart, then you are trading off the cost of serializing the values required to compute
  1870. the expression, and possibly multiple evaluations, against the cost of serializing the expression.
  1871. - For non-local always evaluate in the parent build ctx.
  1872. - For colocal, it is debatable - I'll add as a flag.
  1873. o non-local
  1874. o COUNTER, MATCHTEXT, XMLTEXT, FAILCODE/FAILMESSAGE
  1875. - These are all context dependent in different ways.
  1876. - They all need to be evaluated in the correct context (not too deep or too shallow)
  1877. - Simplest is to evaluate any expression that contains them at the deepest level, and actual expressions evaluated
  1878. at higher levels.
  1879. Activity
  1880. Invariant: evaluate in onCreateContext
  1881. Nested
  1882. Evaluate in parent. If
  1883. General
  1884. * see codegen.txt for more details.
  1885. */