hqliproj.cpp 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391
  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 "platform.h"
  14. #include "jlib.hpp"
  15. #include "jmisc.hpp"
  16. #include "jstream.ipp"
  17. #include "hql.hpp"
  18. #include "hqliproj.ipp"
  19. #include "hqlutil.hpp"
  20. #include "hqlcpputil.hpp"
  21. #include "hqlthql.hpp"
  22. #include "hqlhtcpp.ipp"
  23. #include "hqltcppc.ipp"
  24. #include "hqlcatom.hpp"
  25. #include "hqlfold.hpp"
  26. #include "hqlpmap.hpp"
  27. #include "hqlopt.hpp"
  28. #include "hqlcerrors.hpp"
  29. #include "hqlsource.ipp"
  30. #include "hqlattr.hpp"
  31. #include "jsort.hpp"
  32. //#define PRESERVE_TRANSFORM_ANNOTATION // improves the text in the graph, occasionally prevents optimizations. Maybe add on a debug flag.
  33. //#define POST_COMMON_ANNOTATION // would be better if this just commoned up transforms...
  34. //#define PARANOID_CHECKING
  35. //MORE: Extend UsedFieldSet/NestedField so that once all is set it is never modified and then allow a LINK from
  36. //a nestedfield to clone.
  37. //Problems:
  38. // combine the two classes for simplicity
  39. // save links in the complexextra rather than objects.
  40. // Ensure all functions that modify now return objects + clone if modifying and was all.
  41. // Need to implement gathered-enough in a different way so it isn't shared.
  42. enum
  43. {
  44. CostMemoryCopy = 1,
  45. CostNetworkGroup = 1,
  46. CostGlobalTopN = 2,
  47. CostManyCopy = 3,
  48. CostNetworkCopy = 10,
  49. };
  50. //-------------------------------------------------------------------------------------------------
  51. void UsedFieldSet::addUnique(IHqlExpression * field)
  52. {
  53. //MORE: Add if (!all test to short-circuit contains)
  54. if (!contains(*field))
  55. appendField(*LINK(field));
  56. }
  57. NestedField * UsedFieldSet::addNested(IHqlExpression * field)
  58. {
  59. NestedField * match = findNested(field);
  60. if (!match)
  61. {
  62. assertex(originalFields);
  63. #ifdef PARANOID_CHECKING
  64. assertex(!contains(*field));
  65. assertex(originalFields->contains(*field));
  66. #endif
  67. NestedField * original = originalFields->findNested(field);
  68. if (!original)
  69. {
  70. EclIR::dump_ir(field, originalFields->findNestedByName(field)->field);
  71. throwUnexpected();
  72. }
  73. match = new NestedField(field, &original->used);
  74. appendNested(*LINK(field), match);
  75. }
  76. else
  77. {
  78. assertex(contains(*field));
  79. }
  80. return match;
  81. }
  82. void UsedFieldSet::appendNested(IHqlExpression & ownedField, NestedField * ownedNested)
  83. {
  84. appendField(ownedField);
  85. nested.append(*ownedNested);
  86. }
  87. bool UsedFieldSet::checkAllFieldsUsed()
  88. {
  89. if (all)
  90. return true;
  91. assertex(originalFields);
  92. if (fields.ordinality() != originalFields->fields.ordinality())
  93. return false;
  94. ForEachItemIn(i, nested)
  95. {
  96. NestedField & cur = nested.item(i);
  97. if (!cur.used.checkAllFieldsUsed())
  98. return false;
  99. }
  100. all = true;
  101. return true;
  102. }
  103. bool UsedFieldSet::allGathered() const
  104. {
  105. if (maxGathered == (unsigned)-1)
  106. return true;
  107. if (maxGathered < fields.ordinality())
  108. return false;
  109. ForEachItemIn(i, nested)
  110. {
  111. if (!nested.item(i).used.allGathered())
  112. return false;
  113. }
  114. return true;
  115. }
  116. void UsedFieldSet::appendField(IHqlExpression & ownedField)
  117. {
  118. #ifdef PARANOID_CHECKING
  119. assertex(!contains(ownedField));
  120. #endif
  121. #ifdef USE_IPROJECT_HASH
  122. if (!all)
  123. hash.add(&ownedField);
  124. #endif
  125. fields.append(ownedField);
  126. #ifdef PARANOID_CHECKING
  127. if (originalFields)
  128. assertex(originalFields->contains(ownedField));
  129. #endif
  130. }
  131. void UsedFieldSet::clone(const UsedFieldSet & source)
  132. {
  133. if (originalFields != source.originalFields)
  134. {
  135. //In very rare circumstances it is possible to have non-identical records with identical structures
  136. //A typical case is the same structure defined in two different places, using a symbol to specify a maximum
  137. //length. The locations of the symbols differ, so the records do not match exactly.
  138. assertex(recordTypesMatch(queryOriginalRecord(), source.queryOriginalRecord()));
  139. }
  140. ForEachItemIn(i, source.fields)
  141. appendField(OLINK(source.fields.item(i)));
  142. ForEachItemIn(i1, source.nested)
  143. nested.append(*source.nested.item(i1).clone());
  144. if (source.all)
  145. all = true;
  146. finalRecord.set(source.finalRecord);
  147. }
  148. unsigned UsedFieldSet::getOriginalPosition(IHqlExpression * field) const
  149. {
  150. assertex(originalFields == this);
  151. unsigned match = fields.find(*field);
  152. if (match != NotFound)
  153. return match;
  154. assertex(field->isDatarow());
  155. assertex(finalRecord);
  156. OwnedHqlExpr originalField = finalRecord->querySimpleScope()->lookupSymbol(field->queryId());
  157. assertex(originalField && originalField != field);
  158. unsigned matchOriginal = fields.find(*originalField);
  159. assertex(matchOriginal != NotFound);
  160. return matchOriginal;
  161. }
  162. int UsedFieldSet::compareOrder(IHqlExpression * left, IHqlExpression * right) const
  163. {
  164. return (int)(getOriginalPosition(left) - getOriginalPosition(right));
  165. }
  166. bool UsedFieldSet::contains(IHqlExpression & field) const
  167. {
  168. if (all)
  169. return true;
  170. #ifdef USE_IPROJECT_HASH
  171. return hash.find(&field) != NULL;
  172. #else
  173. return fields.contains(field);
  174. #endif
  175. }
  176. bool UsedFieldSet::contains(IAtom * name) const
  177. {
  178. if (all)
  179. return true;
  180. ForEachItemIn(i, fields)
  181. {
  182. if (fields.item(i).queryName() == name)
  183. return true;
  184. }
  185. return false;
  186. }
  187. IHqlExpression * UsedFieldSet::findByName(IAtom * name) const
  188. {
  189. ForEachItemIn(i, fields)
  190. {
  191. if (fields.item(i).queryName() == name)
  192. return &fields.item(i);
  193. }
  194. return NULL;
  195. }
  196. //Calculate left - right
  197. void UsedFieldSet::createDifference(const UsedFieldSet & left, const UsedFieldSet & right)
  198. {
  199. if (right.includeAll())
  200. return;
  201. //if all are used and non modifyable this code will need changing.
  202. ForEachItemIn(i, left.fields)
  203. {
  204. IHqlExpression & cur = left.fields.item(i);
  205. if (cur.isDatarow())
  206. {
  207. NestedField * leftNested = left.findNested(&cur);
  208. NestedField * rightNested = right.findNested(&cur);
  209. assertex(leftNested);
  210. if (rightNested)
  211. {
  212. Owned<NestedField> diffNested = new NestedField(&cur, leftNested->used.queryOriginal());
  213. diffNested->used.createDifference(leftNested->used, rightNested->used);
  214. if (!diffNested->isEmpty())
  215. appendNested(OLINK(cur), diffNested.getClear());
  216. }
  217. else if (!leftNested->isEmpty())
  218. {
  219. appendNested(OLINK(cur), leftNested->clone());
  220. }
  221. }
  222. else
  223. {
  224. if (!right.contains(cur))
  225. appendField(OLINK(cur));
  226. }
  227. }
  228. }
  229. NestedField * UsedFieldSet::findNested(IHqlExpression * field) const
  230. {
  231. ForEachItemIn(i, nested)
  232. {
  233. NestedField & cur = nested.item(i);
  234. if (cur.field == field)
  235. return &cur;
  236. }
  237. return NULL;
  238. }
  239. NestedField * UsedFieldSet::findNestedByName(IHqlExpression * field) const
  240. {
  241. ForEachItemIn(i2, nested)
  242. {
  243. NestedField & cur = nested.item(i2);
  244. if (cur.field->queryName() == field->queryName())
  245. return &cur;
  246. }
  247. return NULL;
  248. }
  249. IHqlExpression * UsedFieldSet::createFilteredAssign(IHqlExpression * field, IHqlExpression * value, IHqlExpression * newSelf, const UsedFieldSet * exceptions) const
  250. {
  251. if (!contains(*field))
  252. {
  253. IHqlExpression * match = findByName(field->queryName());
  254. if (match)
  255. return createFilteredAssign(match, value, newSelf, exceptions);
  256. return NULL;
  257. }
  258. OwnedHqlExpr newValue = LINK(value);
  259. OwnedHqlExpr newField;
  260. if (field->isDatarow())
  261. {
  262. NestedField * match = findNested(field);
  263. assertex(match);
  264. NestedField * exception = exceptions ? exceptions->findNested(field) : NULL;
  265. if (!match->isEmpty())
  266. {
  267. bool createSubset = true;
  268. if (match->used.checkAllFieldsUsed())
  269. {
  270. if (!exception || exception->isEmpty())
  271. {
  272. createSubset = false;
  273. }
  274. else if (exception && exception->used.checkAllFieldsUsed())
  275. {
  276. newValue.setown(createNullExpr(field));
  277. createSubset = false;
  278. }
  279. }
  280. if (createSubset)
  281. {
  282. newField.setown(finalRecord->querySimpleScope()->lookupSymbol(field->queryId()));
  283. assertex(newField);
  284. assertex(exception || newField != field);
  285. //Two options - this is either a no_createrow, and we extract the assignments from the transform
  286. //or we create a no_createrow to extract the values from the other record
  287. OwnedHqlExpr newTransform;
  288. UsedFieldSet * exceptionFields = exception ? &exception->used : NULL;
  289. if (value->getOperator() == no_createrow)
  290. {
  291. newTransform.setown(match->used.createFilteredTransform(value->queryChild(0), exceptionFields));
  292. }
  293. else if (value->getOperator() == no_select)
  294. {
  295. newTransform.setown(match->used.createRowTransform(value, exceptionFields));
  296. }
  297. else
  298. {
  299. OwnedHqlExpr row = createRow(no_newrow, LINK(value));
  300. newTransform.setown(match->used.createRowTransform(row, exceptionFields));
  301. }
  302. newValue.setown(createRow(no_createrow, newTransform.getClear()));
  303. #if defined(PRESERVE_TRANSFORM_ANNOTATION)
  304. newValue.setown(value->cloneAllAnnotations(newValue));
  305. #endif
  306. }
  307. }
  308. else
  309. newValue.clear();
  310. }
  311. else
  312. {
  313. if (exceptions && exceptions->contains(*field))
  314. {
  315. newValue.setown(createNullExpr(newField ? newField.get() : field));
  316. }
  317. }
  318. if (newValue)
  319. {
  320. IHqlExpression * rhs = newField ? newField.get() : field;
  321. OwnedHqlExpr newLhs = createSelectExpr(LINK(newSelf), LINK(rhs));
  322. return createAssign(newLhs.getClear(), newValue.getClear());
  323. }
  324. return NULL;
  325. }
  326. void UsedFieldSet::createFilteredAssigns(HqlExprArray & assigns, IHqlExpression * transform, IHqlExpression * newSelf, const UsedFieldSet * exceptions) const
  327. {
  328. ForEachChild(i, transform)
  329. {
  330. IHqlExpression * cur = transform->queryChild(i);
  331. switch (cur->getOperator())
  332. {
  333. case no_assign:
  334. {
  335. IHqlExpression * lhs = cur->queryChild(0);
  336. IHqlExpression * field = lhs->queryChild(1);
  337. IHqlExpression * value = cur->queryChild(1);
  338. IHqlExpression * assign = createFilteredAssign(field, value, newSelf, exceptions);
  339. if (assign)
  340. assigns.append(*assign);
  341. break;
  342. }
  343. case no_assignall:
  344. createFilteredAssigns(assigns, cur, newSelf, exceptions);
  345. break;
  346. default:
  347. assigns.append(*LINK(cur));
  348. break;
  349. }
  350. }
  351. }
  352. IHqlExpression * UsedFieldSet::createFilteredTransform(IHqlExpression * transform, const UsedFieldSet * exceptions) const
  353. {
  354. OwnedHqlExpr self = getSelf(finalRecord);
  355. HqlExprArray assigns;
  356. createFilteredAssigns(assigns, transform, self, exceptions);
  357. OwnedHqlExpr ret = createValue(transform->getOperator(), makeTransformType(finalRecord->getType()), assigns);
  358. #if defined(PRESERVE_TRANSFORM_ANNOTATION)
  359. return transform->cloneAllAnnotations(ret);
  360. #else
  361. return ret.getClear();
  362. #endif
  363. }
  364. IHqlExpression * UsedFieldSet::createRowTransform(IHqlExpression * row, const UsedFieldSet * exceptions) const
  365. {
  366. OwnedHqlExpr self = getSelf(finalRecord);
  367. HqlExprArray assigns;
  368. ForEachItemIn(i, fields)
  369. {
  370. IHqlExpression & field = fields.item(i);
  371. OwnedHqlExpr value = createSelectExpr(LINK(row), LINK(&field));
  372. OwnedHqlExpr assign = createFilteredAssign(&field, value, self, exceptions);
  373. if (assign)
  374. assigns.append(*assign.getClear());
  375. }
  376. return createValue(no_transform, makeTransformType(finalRecord->getType()), assigns);
  377. }
  378. void UsedFieldSet::calcFinalRecord(bool canPack, bool ignoreIfEmpty, bool disallowEmpty)
  379. {
  380. assertex(originalFields);
  381. if (finalRecord)
  382. return;
  383. ForEachItemIn(i1, nested)
  384. nested.item(i1).used.calcFinalRecord(canPack, true, false);
  385. IHqlExpression * originalRecord = queryOriginalRecord();
  386. if (checkAllFieldsUsed())
  387. {
  388. if (canPack)
  389. finalRecord.setown(getPackedRecord(originalRecord));
  390. else
  391. finalRecord.set(originalRecord);
  392. return;
  393. }
  394. HqlExprArray recordFields;
  395. ForEachItemIn(i, fields)
  396. {
  397. IHqlExpression & cur = fields.item(i);
  398. if (cur.isDatarow())
  399. {
  400. NestedField * match = findNested(&cur);
  401. assertex(match);
  402. IHqlExpression * record = cur.queryRecord();
  403. IHqlExpression * newRecord = match->used.queryFinalRecord();
  404. if (record == newRecord)
  405. {
  406. recordFields.append(OLINK(cur));
  407. }
  408. else if (newRecord)
  409. {
  410. HqlExprArray args;
  411. unwindChildren(args, &cur);
  412. //MORE: Any default will now have the wrong type => remove it for the moment (ideally it would be projected)
  413. removeAttribute(args, defaultAtom);
  414. OwnedHqlExpr newField = createField(cur.queryId(), makeRowType(newRecord->getType()), args);
  415. recordFields.append(*newField.getClear());
  416. }
  417. }
  418. else
  419. recordFields.append(OLINK(cur));
  420. }
  421. if (originalFields)
  422. {
  423. //Reorder the record to match the original fields
  424. RecordOrderComparer compare(*originalFields);
  425. qsortvec((void * *)recordFields.getArray(), recordFields.ordinality(), compare);
  426. }
  427. if (recordFields.ordinality() == 0)
  428. {
  429. if (ignoreIfEmpty)
  430. return;
  431. if (disallowEmpty)
  432. recordFields.append(*LINK(queryOriginalRecord()->queryChild(0)));
  433. else
  434. recordFields.append(*createAttribute(_nonEmpty_Atom));
  435. }
  436. finalRecord.setown(createRecord(recordFields));
  437. if (canPack)
  438. finalRecord.setown(getPackedRecord(finalRecord));
  439. }
  440. void UsedFieldSet::gatherExpandSelectsUsed(HqlExprArray * selfSelects, HqlExprArray * parentSelects, IHqlExpression * selector, IHqlExpression * source)
  441. {
  442. assertex(selfSelects ? selector != NULL : true);
  443. for (unsigned i1 = maxGathered; i1 < fields.ordinality(); i1++)
  444. {
  445. IHqlExpression & cur = fields.item(i1);
  446. if (!cur.isDatarow())
  447. {
  448. if (selfSelects)
  449. {
  450. OwnedHqlExpr selected = createSelectExpr(LINK(selector), LINK(&cur));
  451. selfSelects->append(*selected.getClear());
  452. }
  453. if (parentSelects)
  454. {
  455. OwnedHqlExpr sourceSelected = createSelectExpr(LINK(source), LINK(&cur));
  456. parentSelects->append(*sourceSelected.getClear());
  457. }
  458. }
  459. }
  460. maxGathered = fields.ordinality();
  461. ForEachItemIn(i2, nested)
  462. {
  463. NestedField & curNested = nested.item(i2);
  464. IHqlExpression * field = curNested.field;
  465. OwnedHqlExpr selected = selector ? createSelectExpr(LINK(selector), LINK(field)) : NULL;
  466. OwnedHqlExpr sourceSelected = createSelectExpr(LINK(source), LINK(field));
  467. if (!curNested.includeAll())
  468. {
  469. curNested.used.gatherExpandSelectsUsed(selfSelects, parentSelects, selected, sourceSelected);
  470. sourceSelected.clear();
  471. }
  472. else
  473. {
  474. curNested.used.noteGatheredAll();
  475. if (selfSelects)
  476. selfSelects->append(*selected.getClear());
  477. if (parentSelects)
  478. parentSelects->append(*sourceSelected.getClear());
  479. }
  480. }
  481. }
  482. inline bool isSelector(IHqlExpression * expr)
  483. {
  484. return (expr->getOperator() == no_select) && !isNewSelector(expr);
  485. }
  486. void UsedFieldSet::gatherTransformValuesUsed(HqlExprArray * selfSelects, HqlExprArray * parentSelects, HqlExprArray * values, IHqlExpression * selector, IHqlExpression * transform)
  487. {
  488. for (unsigned i = maxGathered; i < fields.ordinality(); i++)
  489. {
  490. IHqlExpression & cur = fields.item(i);
  491. if (!cur.isDatarow())
  492. {
  493. if (selfSelects)
  494. {
  495. OwnedHqlExpr selected = createSelectExpr(LINK(selector), LINK(&cur));
  496. selfSelects->append(*selected.getClear());
  497. }
  498. if (values)
  499. {
  500. IHqlExpression * transformValue = queryTransformAssignValue(transform, &cur);
  501. //If no transform value is found then we almost certainly have an invalid query (e.g, LEFT inside a
  502. //global). Don't add the value - you'll definitely get a later follow on error
  503. if (!transformValue)
  504. throwError1(HQLERR_NoMappingForField, str(cur.queryId()));
  505. values->append(*LINK(transformValue));
  506. }
  507. }
  508. }
  509. maxGathered = fields.ordinality();
  510. ForEachItemIn(i2, nested)
  511. {
  512. NestedField & curNested = nested.item(i2);
  513. if (!curNested.isEmpty() && !curNested.used.allGathered())
  514. {
  515. IHqlExpression * field = curNested.field;
  516. OwnedHqlExpr selected = selector ? createSelectExpr(LINK(selector), LINK(field)) : NULL;
  517. IHqlExpression * transformValue = queryTransformAssignValue(transform, field);
  518. assertex(transformValue);
  519. bool includeThis = true;
  520. if (!curNested.includeAll() && !containsSkip(transformValue))
  521. {
  522. if (transformValue->getOperator() == no_createrow)
  523. {
  524. curNested.used.gatherTransformValuesUsed(selfSelects, parentSelects, values, selected, transformValue->queryChild(0));
  525. includeThis = false;
  526. }
  527. else if (isAlwaysActiveRow(transformValue) || isSelector(transformValue))
  528. {
  529. curNested.used.gatherExpandSelectsUsed(selfSelects, parentSelects, selected, transformValue);
  530. includeThis = false;
  531. }
  532. //otherwise use the whole value.
  533. }
  534. if (includeThis)
  535. {
  536. curNested.used.noteGatheredAll();
  537. if (selfSelects)
  538. selfSelects->append(*selected.getClear());
  539. if (values)
  540. values->append(*LINK(transformValue));
  541. }
  542. }
  543. }
  544. }
  545. void UsedFieldSet::getText(StringBuffer & s) const
  546. {
  547. if (all)
  548. s.append("ALL");
  549. s.append("[");
  550. ForEachItemIn(i, fields)
  551. {
  552. IHqlExpression & cur = fields.item(i);
  553. if (i) s.append(",");
  554. s.append(cur.queryName());
  555. if (cur.isDatarow())
  556. {
  557. NestedField * match = findNested(&cur);
  558. assertex(match);
  559. if (!match->used.checkAllFieldsUsed())
  560. match->used.getText(s);
  561. }
  562. }
  563. s.append("]");
  564. }
  565. void UsedFieldSet::intersectFields(const UsedFieldSet & source)
  566. {
  567. if (source.includeAll())
  568. return;
  569. if (includeAll())
  570. set(source);
  571. else
  572. {
  573. finalRecord.clear();
  574. ForEachItemInRev(i1, fields)
  575. {
  576. IHqlExpression & field = fields.item(i1);
  577. if (!field.isDatarow() && !source.contains(field))
  578. {
  579. fields.remove(i1);
  580. #ifdef USE_IPROJECT_HASH
  581. hash.remove(&field);
  582. #endif
  583. }
  584. }
  585. ForEachItemInRev(i2, nested)
  586. {
  587. NestedField & cur = nested.item(i2);
  588. NestedField * match = source.findNested(cur.field);
  589. //MORE: If we never modify items that have been all set then the following will need changing:
  590. if (match)
  591. {
  592. cur.used.intersectFields(match->used);
  593. }
  594. else
  595. {
  596. cur.clear();
  597. }
  598. }
  599. }
  600. }
  601. void UsedFieldSet::optimizeFieldsToBlank(const UsedFieldSet & allAssigned, IHqlExpression * transform)
  602. {
  603. //MORE:
  604. //this contains a list of fields that can be blanked instead of assigning.
  605. //If there is a sequence of assignments SELF.x := LEFT.x then
  606. //a) the the field will already be in the input and output records (since it is a rollup/iterate)
  607. //b) if the previous field is assigned, then it may generate more efficient code to also assign this
  608. // field rather than blanking it.
  609. //Therefore we should walk the transform, and if a field is an exception and previous field is used
  610. //and possibly the exception is fixed length, then remove it from the exceptions.
  611. }
  612. bool UsedFieldSet::requiresFewerFields(const UsedFieldSet & other) const
  613. {
  614. if (includeAll())
  615. return false;
  616. return (fields.ordinality() < other.fields.ordinality());
  617. }
  618. void UsedFieldSet::unionFields(const UsedFieldSet & source)
  619. {
  620. if (includeAll())
  621. return;
  622. if (source.includeAll())
  623. set(source);
  624. else
  625. {
  626. ForEachItemIn(i, source.fields)
  627. {
  628. IHqlExpression & field = source.fields.item(i);
  629. if (!contains(field))
  630. appendField(OLINK(field));
  631. }
  632. ForEachItemIn(i1, source.nested)
  633. {
  634. NestedField & cur = source.nested.item(i1);
  635. NestedField * match = findNested(cur.field);
  636. if (match)
  637. match->used.unionFields(cur.used);
  638. else
  639. nested.append(*cur.clone());
  640. }
  641. }
  642. }
  643. bool UsedFieldSet::isEmpty() const
  644. {
  645. ForEachItemIn(i1, fields)
  646. {
  647. IHqlExpression & cur = fields.item(i1);
  648. if (!cur.isDatarow())
  649. return false;
  650. }
  651. ForEachItemIn(i2, nested)
  652. {
  653. if (!nested.item(i2).isEmpty())
  654. return false;
  655. }
  656. return true;
  657. }
  658. void UsedFieldSet::kill()
  659. {
  660. #ifdef USE_IPROJECT_HASH
  661. hash.kill();
  662. #endif
  663. fields.kill();
  664. nested.kill();
  665. all = false;
  666. maxGathered = 0;
  667. finalRecord.clear();
  668. }
  669. void UsedFieldSet::set(const UsedFieldSet & source)
  670. {
  671. kill();
  672. clone(source);
  673. }
  674. void UsedFieldSet::setAll()
  675. {
  676. if (all)
  677. return;
  678. assertex(originalFields);
  679. kill();
  680. clone(*originalFields);
  681. }
  682. void UsedFieldSet::setRecord(IHqlExpression * record)
  683. {
  684. assertex(fields.ordinality() == 0);
  685. all = true;
  686. unwindFields(fields, record);
  687. ForEachItemIn(i, fields)
  688. {
  689. IHqlExpression & cur = fields.item(i);
  690. if (cur.isDatarow())
  691. {
  692. NestedField * child = new NestedField(&cur, NULL);
  693. child->used.setRecord(cur.queryRecord());
  694. nested.append(*child);
  695. }
  696. }
  697. finalRecord.set(record->queryBody());
  698. originalFields = this;
  699. }
  700. static UsedFieldSet * addNestedField(UsedFieldSet & fields, IHqlExpression * expr, IHqlExpression * selector)
  701. {
  702. if (expr == selector)
  703. return &fields;
  704. IHqlExpression * ds = expr->queryChild(0);
  705. UsedFieldSet * parent = addNestedField(fields, ds, selector);
  706. if (parent)
  707. {
  708. NestedField * nested = parent->addNested(expr->queryChild(1));
  709. if (!nested || nested->includeAll())
  710. return NULL;
  711. return &nested->used;
  712. }
  713. return NULL;
  714. }
  715. bool processMatchingSelector(UsedFieldSet & fields, IHqlExpression * select, IHqlExpression * selector)
  716. {
  717. if (select == selector)
  718. {
  719. fields.setAll();
  720. return true;
  721. }
  722. if (select->getOperator() != no_select)
  723. return false;
  724. //Could be <root>.blah.ds - queryDatasetSelector needs to be applied to the lhs.
  725. IHqlExpression * root = queryDatasetCursor(select->queryChild(0));
  726. if (root == selector)
  727. {
  728. if (select->isDatarow())
  729. {
  730. UsedFieldSet * nested = addNestedField(fields, select, selector);
  731. if (nested)
  732. nested->setAll();
  733. }
  734. else
  735. {
  736. IHqlExpression * ds = select->queryChild(0);
  737. IHqlExpression * field = select->queryChild(1);
  738. UsedFieldSet * nested = addNestedField(fields, ds, selector);
  739. if (nested)
  740. nested->addUnique(field);
  741. }
  742. }
  743. return false;
  744. }
  745. //---------------------------------------------------------------------------------------------------------------------
  746. int RecordOrderComparer::docompare(const void * l,const void * r) const
  747. {
  748. IHqlExpression * lExpr = (IHqlExpression *)l;
  749. IHqlExpression * rExpr = (IHqlExpression *)r;
  750. return fields.compareOrder(lExpr, rExpr);
  751. }
  752. //---------------------------------------------------------------------------------------------------------------------
  753. static unsigned getActivityCost(IHqlExpression * expr, ClusterType targetClusterType)
  754. {
  755. switch (targetClusterType)
  756. {
  757. case ThorLCRCluster:
  758. {
  759. switch (expr->getOperator())
  760. {
  761. case no_sort:
  762. //MORE: What about checking for grouped!
  763. if (!expr->hasAttribute(localAtom))
  764. return CostNetworkCopy;
  765. return CostManyCopy;
  766. case no_subsort:
  767. if (!expr->hasAttribute(localAtom) && !isGrouped(expr))
  768. return CostNetworkCopy;
  769. break;
  770. case no_group:
  771. if (!expr->hasAttribute(localAtom))
  772. return CostNetworkGroup;
  773. break;
  774. case no_keyeddistribute:
  775. case no_distribute:
  776. case no_cosort:
  777. return CostNetworkCopy;
  778. case no_topn:
  779. if (!expr->hasAttribute(localAtom))
  780. return CostGlobalTopN;
  781. break;
  782. case no_selfjoin:
  783. if (!expr->hasAttribute(localAtom))
  784. return CostNetworkCopy;
  785. break;
  786. case no_denormalize:
  787. case no_denormalizegroup:
  788. case no_join:
  789. case no_joincount:
  790. if (!expr->hasAttribute(localAtom))
  791. {
  792. if (isKeyedJoin(expr))
  793. break;
  794. if (expr->hasAttribute(lookupAtom))
  795. return CostNetworkCopy/2; //insert on rhs.
  796. return CostNetworkCopy;
  797. }
  798. break;
  799. //case no_dedup: all non local, may be worth it..
  800. }
  801. }
  802. }
  803. return 0;
  804. }
  805. //MORE: Should cache this in the extra for a record, quite possibly with the unwound fields as well.
  806. bool isSensibleRecord(IHqlExpression * record)
  807. {
  808. ForEachChild(i, record)
  809. {
  810. IHqlExpression * cur = record->queryChild(i);
  811. switch (cur->getOperator())
  812. {
  813. case no_record:
  814. if (!isSensibleRecord(cur))
  815. return false;
  816. break;
  817. case no_ifblock:
  818. return false;
  819. case no_field:
  820. //Could loosen this condition so that it didn't use any fields within the record.
  821. switch (cur->queryType()->getTypeCode())
  822. {
  823. case type_alien:
  824. return false;
  825. case type_table:
  826. case type_groupedtable:
  827. {
  828. //disqualify datasets with no_selfref counts/lengths
  829. IHqlExpression * limit = cur->queryAttribute(countAtom);
  830. if (!limit)
  831. limit = cur->queryAttribute(sizeAtom);
  832. if (limit && !limit->isConstant())
  833. return false;
  834. break;
  835. }
  836. }
  837. break;
  838. }
  839. }
  840. return true;
  841. }
  842. IHqlExpression * queryRootSelector(IHqlExpression * select)
  843. {
  844. for (;;)
  845. {
  846. if (select->hasAttribute(newAtom))
  847. return select;
  848. IHqlExpression * ds = select->queryChild(0);
  849. if (ds->getOperator() != no_select)
  850. return select;
  851. select = ds;
  852. }
  853. }
  854. static node_operator queryCompoundOp(IHqlExpression * expr)
  855. {
  856. switch (expr->getOperator())
  857. {
  858. case no_table:
  859. return no_compound_diskread;
  860. case no_newkeyindex:
  861. return no_compound_indexread;
  862. case no_dataset_alias:
  863. case no_preservemeta:
  864. return queryCompoundOp(expr->queryChild(0));
  865. }
  866. throwUnexpectedOp(expr->getOperator());
  867. }
  868. static int compareHqlExprPtr(IInterface * * left, IInterface * * right)
  869. {
  870. return *left == *right ? 0 : *left < *right ? -1 : +1;
  871. }
  872. inline bool hasActivityType(IHqlExpression * expr)
  873. {
  874. return (expr->isDataset() || expr->isDatarow() || expr->isDictionary());
  875. }
  876. //------------------------------------------------------------------------
  877. ImplicitProjectInfo::ImplicitProjectInfo(IHqlExpression * _original, ProjectExprKind _kind) : NewTransformInfo(_original), kind(_kind)
  878. {
  879. visited = false;
  880. gatheredSelectsUsed = false;
  881. //The following logically belong to the complexProjectInfo, see note in header
  882. canOptimize = true;
  883. insertProject = false;
  884. alreadyInScope = false;
  885. canReorderOutput = true;
  886. calcedReorderOutput = false;
  887. visitedAllowingActivity = false;
  888. }
  889. void ImplicitProjectInfo::addActiveSelect(IHqlExpression * select)
  890. {
  891. if (selectsUsed.find(*select) == NotFound)
  892. selectsUsed.append(*select);
  893. }
  894. void ImplicitProjectInfo::addActiveSelects(const SelectUsedArray & src)
  895. {
  896. unsigned numSrc = src.ordinality();
  897. if (numSrc == 0)
  898. return;
  899. if (selectsUsed.ordinality() == 0)
  900. {
  901. //No need to check for pre-existence, can be significant
  902. selectsUsed.ensure(numSrc);
  903. for (unsigned i=0; i < numSrc; i++)
  904. selectsUsed.append(src.item(i));
  905. }
  906. else
  907. {
  908. //MORE: Should only check if exists in pre-existing selects otherwise O(N^2) in items added
  909. for (unsigned i=0; i < numSrc; i++)
  910. addActiveSelect(&src.item(i));
  911. }
  912. }
  913. void ImplicitProjectInfo::removeProductionSelects()
  914. {
  915. ForEachItemInRev(i, selectsUsed)
  916. {
  917. IHqlExpression & cur = selectsUsed.item(i);
  918. if ((cur.getOperator() == no_matchattr) || (cur.queryChild(0)->getOperator() == no_matchattr))
  919. selectsUsed.remove(i);
  920. }
  921. }
  922. void ImplicitProjectInfo::removeScopedFields(IHqlExpression * selector)
  923. {
  924. ForEachItemInRev(i, selectsUsed)
  925. {
  926. IHqlExpression & cur = selectsUsed.item(i);
  927. if ((&cur == selector) ||
  928. ((cur.getOperator() == no_select) && (queryDatasetCursor(cur.queryChild(0)) == selector)))
  929. selectsUsed.remove(i);
  930. }
  931. }
  932. void ImplicitProjectInfo::removeRowsFields(IHqlExpression * expr, IHqlExpression * left, IHqlExpression * right)
  933. {
  934. node_operator rowsSide = queryHasRows(expr);
  935. if (rowsSide == no_none)
  936. return;
  937. IHqlExpression * rowsid = expr->queryAttribute(_rowsid_Atom);
  938. switch (rowsSide)
  939. {
  940. case no_left:
  941. {
  942. OwnedHqlExpr rowsExpr = createDataset(no_rows, LINK(left), LINK(rowsid));
  943. removeScopedFields(rowsExpr);
  944. break;
  945. }
  946. case no_right:
  947. {
  948. OwnedHqlExpr rowsExpr = createDataset(no_rows, LINK(right), LINK(rowsid));
  949. removeScopedFields(rowsExpr);
  950. break;
  951. }
  952. default:
  953. throwUnexpectedOp(rowsSide);
  954. }
  955. }
  956. //------------------------------------------------------------------------
  957. ComplexImplicitProjectInfo::ComplexImplicitProjectInfo(IHqlExpression * _original, ProjectExprKind _kind) : ImplicitProjectInfo(_original, _kind)
  958. {
  959. }
  960. void ComplexImplicitProjectInfo::addAllOutputs()
  961. {
  962. outputFields.setAll();
  963. }
  964. IHqlExpression * ComplexImplicitProjectInfo::createOutputProject(IHqlExpression * ds)
  965. {
  966. if (ds->getOperator() == no_null)
  967. return createDataset(no_null, LINK(queryOutputRecord()));
  968. OwnedHqlExpr seq = createSelectorSequence();
  969. OwnedHqlExpr left = createSelector(no_left, ds, seq);
  970. OwnedHqlExpr self = getSelf(queryOutputRecord());
  971. MultiErrorReceiver errs;
  972. ECLlocation dummyLocation(0, 0, 0, NULL); // MORE - shame there is not a global one that could be used for this?
  973. IHqlExpression * transform = createMappingTransform(self, left, false, errs, dummyLocation);
  974. assertex(!errs.errCount());
  975. if (ds->isDataset())
  976. return createDataset(no_hqlproject, LINK(ds), createComma(transform, LINK(seq)));
  977. else
  978. assertex(!ds->isDictionary());
  979. return createRow(no_projectrow, LINK(ds), createComma(transform, LINK(seq)));
  980. }
  981. void ComplexImplicitProjectInfo::finalizeOutputRecord(bool disallowEmpty)
  982. {
  983. //MORE: Create them in the same order as the original record + don't change if numOutputFields = numOriginalOutputFields
  984. if (!queryOutputRecord())
  985. {
  986. bool canPack = (safeToReorderOutput() && okToOptimize());
  987. outputFields.calcFinalRecord(canPack, false, disallowEmpty);
  988. }
  989. }
  990. unsigned ComplexImplicitProjectInfo::queryCostFactor(ClusterType targetClusterType)
  991. {
  992. //MORE: Could cache the value, but this option isn't really used, and not called a lot.
  993. return getActivityCost(original, targetClusterType);
  994. }
  995. void ComplexImplicitProjectInfo::stopOptimizeCompound(bool cascade)
  996. {
  997. if (cascade)
  998. {
  999. canOptimize = false;
  1000. ForEachItemIn(i, inputs)
  1001. inputs.item(i).stopOptimizeCompound(cascade);
  1002. }
  1003. else if (kind == CompoundableActivity)
  1004. canOptimize = false;
  1005. }
  1006. void ComplexImplicitProjectInfo::trace()
  1007. {
  1008. StringBuffer s;
  1009. if (original->queryName())
  1010. s.append(original->queryName()).append(" := ");
  1011. s.append(getOpString(original->getOperator()));
  1012. DBGLOG("%s", s.str());
  1013. switch (getChildDatasetType(original))
  1014. {
  1015. case childdataset_none:
  1016. case childdataset_many_noscope:
  1017. case childdataset_many:
  1018. case childdataset_if:
  1019. case childdataset_case:
  1020. case childdataset_map:
  1021. case childdataset_dataset_noscope:
  1022. case childdataset_nway_left_right:
  1023. break;
  1024. case childdataset_dataset:
  1025. case childdataset_datasetleft:
  1026. case childdataset_top_left_right:
  1027. case childdataset_same_left_right:
  1028. trace("input", leftFieldsRequired);
  1029. break;
  1030. case childdataset_left:
  1031. trace("left", leftFieldsRequired);
  1032. break;
  1033. case childdataset_leftright:
  1034. trace("left", leftFieldsRequired);
  1035. trace("right", rightFieldsRequired);
  1036. break;
  1037. }
  1038. trace("output", outputFields);
  1039. }
  1040. void ComplexImplicitProjectInfo::trace(const char * label, const UsedFieldSet & fields)
  1041. {
  1042. StringBuffer s;
  1043. s.append(" ").append(label).append(": ");
  1044. fields.getText(s);
  1045. DBGLOG("%s", s.str());
  1046. }
  1047. void ComplexImplicitProjectInfo::inheritRequiredFields(const UsedFieldSet & requiredList)
  1048. {
  1049. //Temporary code to avoid a check. It is permissible for the fields of an AnyTypeActivity to not match
  1050. if ((activityKind() == AnyTypeActivity) && !outputFields.includeAll() && requiredList.includeAll())
  1051. outputFields.setOriginal(requiredList.queryOriginal());
  1052. outputFields.unionFields(requiredList);
  1053. }
  1054. void ComplexImplicitProjectInfo::notifyRequiredFields(ComplexImplicitProjectInfo * whichInput)
  1055. {
  1056. if (activityKind() == PassThroughActivity)
  1057. {
  1058. whichInput->inheritRequiredFields(outputFields);
  1059. }
  1060. else if ((activityKind() == RollupTransformActivity) || (activityKind() == IterateTransformActivity))
  1061. {
  1062. whichInput->inheritRequiredFields(leftFieldsRequired);
  1063. whichInput->inheritRequiredFields(rightFieldsRequired);
  1064. }
  1065. else if (original->getOperator() == no_fetch)
  1066. {
  1067. assertex(whichInput == &inputs.item(0));
  1068. whichInput->inheritRequiredFields(rightFieldsRequired);
  1069. }
  1070. else if (whichInput == &inputs.item(0))
  1071. {
  1072. whichInput->inheritRequiredFields(leftFieldsRequired);
  1073. //can occur if same dataset is used for left and right - e.g., non-symmetric self join
  1074. if ((inputs.ordinality() > 1) && (whichInput == &inputs.item(1)))
  1075. whichInput->inheritRequiredFields(rightFieldsRequired);
  1076. }
  1077. else if (whichInput == &inputs.item(1))
  1078. {
  1079. whichInput->inheritRequiredFields(rightFieldsRequired);
  1080. }
  1081. else if (inputs.contains(*whichInput))
  1082. whichInput->addAllOutputs();
  1083. else
  1084. throwUnexpected();
  1085. }
  1086. bool ComplexImplicitProjectInfo::safeToReorderOutput()
  1087. {
  1088. if (!calcedReorderOutput)
  1089. {
  1090. canReorderOutput = true;
  1091. switch (activityKind())
  1092. {
  1093. case FixedInputActivity:
  1094. //can occur with weird operations in the middle of a dataset. Should probably only set if an action.
  1095. canReorderOutput = false;
  1096. break;
  1097. default:
  1098. ForEachItemIn(i, outputs)
  1099. {
  1100. if (!outputs.item(i).safeToReorderInput())
  1101. {
  1102. canReorderOutput = false;
  1103. break;
  1104. }
  1105. }
  1106. break;
  1107. }
  1108. calcedReorderOutput = true;
  1109. }
  1110. return canReorderOutput;
  1111. }
  1112. bool ComplexImplicitProjectInfo::safeToReorderInput()
  1113. {
  1114. switch (activityKind())
  1115. {
  1116. case CreateRecordActivity:
  1117. case CreateRecordLRActivity:
  1118. case ScalarSelectActivity:
  1119. //These activities have remove the constraints of the inputs on their outputs.
  1120. return true;
  1121. case FixedInputActivity:
  1122. return false;
  1123. }
  1124. return safeToReorderOutput();
  1125. }
  1126. void ComplexImplicitProjectInfo::setMatchingOutput(ComplexImplicitProjectInfo * other)
  1127. {
  1128. assertex(other->queryOutputRecord());
  1129. outputFields.set(other->outputFields);
  1130. }
  1131. //-----------------------------------------------------------------------------------------------
  1132. static HqlTransformerInfo implicitProjectTransformerInfo("ImplicitProjectTransformer");
  1133. ImplicitProjectTransformer::ImplicitProjectTransformer(HqlCppTranslator & _translator, bool _optimizeSpills)
  1134. : NewHqlTransformer(implicitProjectTransformerInfo), translator(_translator)
  1135. {
  1136. const HqlCppOptions & transOptions = translator.queryOptions();
  1137. targetClusterType = translator.getTargetClusterType();
  1138. options.isRoxie = (targetClusterType == RoxieCluster);
  1139. options.optimizeProjectsPreservePersists = transOptions.optimizeProjectsPreservePersists;
  1140. options.autoPackRecords = transOptions.autoPackRecords;
  1141. options.notifyOptimizedProjects = translator.notifyOptimizedProjectsLevel();
  1142. options.optimizeSpills = _optimizeSpills;
  1143. options.enableCompoundCsvRead = translator.queryOptions().enableCompoundCsvRead;
  1144. options.projectNestedTables = translator.queryOptions().projectNestedTables;
  1145. allowActivity = true;
  1146. options.insertProjectCostLevel = 0;
  1147. if (transOptions.reduceNetworkTraffic)
  1148. options.insertProjectCostLevel = (transOptions.insertProjectCostLevel != (unsigned)-1) ? transOptions.insertProjectCostLevel : CostNetworkCopy;
  1149. }
  1150. void ImplicitProjectTransformer::analyseExpr(IHqlExpression * expr)
  1151. {
  1152. ImplicitProjectInfo * extra = queryBodyExtra(expr);
  1153. ComplexImplicitProjectInfo * complexExtra = extra->queryComplexInfo();
  1154. if (complexExtra)
  1155. {
  1156. if (complexExtra->alreadyInScope)
  1157. return;
  1158. if (!options.autoPackRecords)
  1159. complexExtra->setReorderOutput(false);
  1160. if (extra->checkAlreadyVisited())
  1161. {
  1162. //Don't allow modification if referenced from activity and non-activity context
  1163. if (allowActivity)
  1164. {
  1165. if ((extra->activityKind() != NonActivity) || (expr->getOperator() == no_record))
  1166. return;
  1167. //either allowed before, but tagged as a non
  1168. //If previously this was called in an allowactivity context it must have been explicitly disabled, so no point recursing.
  1169. if (complexExtra->visitedAllowingActivity)
  1170. return;
  1171. //otherwise, probably worth recursing again...
  1172. extra->preventOptimization();
  1173. }
  1174. else
  1175. {
  1176. extra->preventOptimization();
  1177. return;
  1178. }
  1179. }
  1180. if (allowActivity)
  1181. complexExtra->visitedAllowingActivity = true;
  1182. }
  1183. else
  1184. {
  1185. if (extra->checkAlreadyVisited())
  1186. return;
  1187. }
  1188. node_operator op = expr->getOperator();
  1189. switch (op)
  1190. {
  1191. case no_record:
  1192. {
  1193. complexExtra->outputFields.setRecord(expr);
  1194. return;
  1195. }
  1196. case no_constant:
  1197. case no_attr:
  1198. return;
  1199. case no_transform:
  1200. case no_newtransform:
  1201. case no_transformlist:
  1202. case no_list:
  1203. if (expr->isConstant())
  1204. return;
  1205. break;
  1206. }
  1207. ITypeInfo * type = expr->queryType();
  1208. if (allowActivity)
  1209. {
  1210. switch (op)
  1211. {
  1212. case no_evaluate:
  1213. throwUnexpected();
  1214. case no_select:
  1215. if (hasActivityType(expr))
  1216. {
  1217. //MORE: These means that selects from a parent dataset don't project down the parent dataset.
  1218. //I'm not sure how big an issue that would be.
  1219. allowActivity = false;
  1220. Parent::analyseExpr(expr);
  1221. allowActivity = true;
  1222. assertex(extra->activityKind() == SourceActivity);
  1223. activities.append(*LINK(expr));
  1224. IHqlExpression * record = expr->queryRecord();
  1225. complexExtra->setOriginalRecord(queryBodyComplexExtra(record));
  1226. analyseExpr(record);
  1227. }
  1228. else if (isNewSelector(expr))
  1229. {
  1230. Parent::analyseExpr(expr);
  1231. assertex(extra->activityKind() == ScalarSelectActivity);
  1232. if (expr->hasAttribute(newAtom))
  1233. connect(expr->queryChild(0), expr);
  1234. activities.append(*LINK(expr));
  1235. }
  1236. gatherFieldsUsed(expr, extra);
  1237. return;
  1238. case no_activerow:
  1239. {
  1240. assertex(extra->activityKind() == SourceActivity);
  1241. allowActivity = false;
  1242. Parent::analyseExpr(expr);
  1243. allowActivity = true;
  1244. break;
  1245. }
  1246. case no_attr:
  1247. case no_attr_expr:
  1248. case no_attr_link:
  1249. allowActivity = false;
  1250. Parent::analyseExpr(expr);
  1251. allowActivity = true;
  1252. return;
  1253. case no_thor:
  1254. if (hasActivityType(expr))
  1255. {
  1256. assertex(extra->activityKind() == SimpleActivity);
  1257. Parent::analyseExpr(expr);
  1258. connect(expr->queryChild(0), expr);
  1259. }
  1260. else
  1261. {
  1262. assertex(extra->activityKind() == NonActivity);
  1263. Parent::analyseExpr(expr);
  1264. }
  1265. break;
  1266. case no_compound:
  1267. if (hasActivityType(expr))
  1268. {
  1269. assertex(extra->activityKind() == SimpleActivity);
  1270. Parent::analyseExpr(expr);
  1271. connect(expr->queryChild(1), expr);
  1272. break;
  1273. }
  1274. assertex(extra->activityKind() == NonActivity);
  1275. Parent::analyseExpr(expr);
  1276. break;
  1277. case no_executewhen:
  1278. if (expr->isDataset() || expr->isDatarow())
  1279. {
  1280. assertex(extra->activityKind() == SimpleActivity);
  1281. Parent::analyseExpr(expr);
  1282. connect(expr->queryChild(0), expr);
  1283. break;
  1284. }
  1285. assertex(extra->activityKind() == NonActivity);
  1286. Parent::analyseExpr(expr);
  1287. break;
  1288. case no_subgraph:
  1289. assertex(extra->activityKind() == NonActivity);
  1290. Parent::analyseExpr(expr);
  1291. break;
  1292. case no_libraryselect:
  1293. assertex(extra->activityKind() == SourceActivity);
  1294. analyseExpr(expr->queryChild(1));
  1295. break;
  1296. case no_libraryscopeinstance:
  1297. {
  1298. assertex(extra->activityKind() == NonActivity);
  1299. ForEachChild(i, expr)
  1300. {
  1301. IHqlExpression * cur = expr->queryChild(i);
  1302. if (cur->isDataset())
  1303. {
  1304. analyseExpr(cur);
  1305. queryBodyExtra(cur)->preventOptimization();
  1306. }
  1307. }
  1308. break;
  1309. }
  1310. case no_mergejoin:
  1311. case no_nwayjoin: // could probably project output of this one...
  1312. case no_nwaymerge:
  1313. {
  1314. assertex(extra->activityKind() == SourceActivity);
  1315. //Don't allow any of the inputs to be optimized - otherwise the can end up with inconsistent record types
  1316. allowActivity = false;
  1317. Parent::analyseExpr(expr->queryChild(0));
  1318. allowActivity = true;
  1319. break;
  1320. }
  1321. case no_setresult:
  1322. case no_ensureresult:
  1323. {
  1324. IHqlExpression * value = expr->queryChild(0);
  1325. if (value->isDataset() || value->isDatarow())// || value->isList())
  1326. {
  1327. assertex(extra->activityKind() == FixedInputActivity);
  1328. analyseExpr(value);
  1329. //no need to analyse other fields since they are all constant
  1330. connect(value, expr);
  1331. }
  1332. else
  1333. {
  1334. assertex(extra->activityKind() == NonActivity);
  1335. Parent::analyseExpr(expr);
  1336. }
  1337. break;
  1338. }
  1339. case no_newtransform:
  1340. case no_transform:
  1341. case no_transformlist:
  1342. assertex(extra->kind == NonActivity);
  1343. if (!expr->isConstant())
  1344. Parent::analyseExpr(expr);
  1345. return;
  1346. default:
  1347. {
  1348. unsigned numArgs = expr->numChildren();
  1349. unsigned first = 0;
  1350. unsigned last = numArgs;
  1351. unsigned start = 0;
  1352. if (!expr->isAction() && !expr->isDataset() && !expr->isDatarow() && !expr->isDictionary())
  1353. {
  1354. switch (op)
  1355. {
  1356. case NO_AGGREGATE:
  1357. case no_createset:
  1358. last = 1;
  1359. break;
  1360. case no_sizeof:
  1361. last = 0;
  1362. break;
  1363. default:
  1364. extra->kind = NonActivity;
  1365. break;
  1366. }
  1367. }
  1368. else
  1369. {
  1370. IHqlExpression * record = expr->queryRecord();
  1371. if (!record && expr->queryChild(0))
  1372. record = expr->queryChild(0)->queryRecord();
  1373. if (!record || !isSensibleRecord(record))
  1374. extra->preventOptimization();
  1375. first = getFirstActivityArgument(expr);
  1376. last = first + getNumActivityArguments(expr);
  1377. switch (expr->getOperator())
  1378. {
  1379. case no_dedup:
  1380. if (dedupMatchesWholeRecord(expr))
  1381. extra->preventOptimization();
  1382. break;
  1383. case no_process:
  1384. extra->preventOptimization();
  1385. break;
  1386. case no_executewhen:
  1387. last = 1;
  1388. break;
  1389. case no_newkeyindex:
  1390. // case no_dataset:
  1391. //No point walking the transform for an index
  1392. start = 3;
  1393. numArgs = 4;
  1394. break;
  1395. case no_compound_diskaggregate:
  1396. case no_compound_diskcount:
  1397. case no_compound_diskgroupaggregate:
  1398. case no_compound_indexaggregate:
  1399. case no_compound_indexcount:
  1400. case no_compound_indexgroupaggregate:
  1401. //walk inside these... they're not compoundable, but they may be able to lose some fields from the transform.
  1402. last = 1;
  1403. break;
  1404. }
  1405. }
  1406. for (unsigned i =start; i < numArgs; i++)
  1407. {
  1408. IHqlExpression * cur = expr->queryChild(i);
  1409. allowActivity = (i >= first) && (i < last);
  1410. analyseExpr(cur);
  1411. if (allowActivity)
  1412. {
  1413. if (extra->kind == NonActivity)
  1414. {
  1415. ImplicitProjectInfo * childExtra = queryBodyExtra(cur);
  1416. childExtra->preventOptimization();
  1417. }
  1418. else if (!cur->isAction() && !cur->isAttribute())
  1419. {
  1420. connect(cur, expr);
  1421. }
  1422. }
  1423. }
  1424. if (extra->kind == NonActivity)
  1425. gatherFieldsUsed(expr, extra);
  1426. allowActivity = true;
  1427. }
  1428. }
  1429. }
  1430. else
  1431. {
  1432. extra->preventOptimization();
  1433. switch (op)
  1434. {
  1435. case no_attr_expr:
  1436. analyseChildren(expr);
  1437. break;
  1438. case no_newkeyindex:
  1439. // case no_sizeof:
  1440. //no point analysing parameters to keyed joins
  1441. break;
  1442. default:
  1443. Parent::analyseExpr(expr);
  1444. break;
  1445. }
  1446. }
  1447. //Add activities in depth first order, so traversing them backwards is guaranteed to be top down.
  1448. if (extra->activityKind() != NonActivity)
  1449. {
  1450. assertex(complexExtra);
  1451. switch (extra->activityKind())
  1452. {
  1453. case CreateRecordActivity:
  1454. case CreateRecordLRActivity:
  1455. case RollupTransformActivity:
  1456. case IterateTransformActivity:
  1457. case DenormalizeActivity:
  1458. case CreateRecordSourceActivity:
  1459. case CreateNonEmptyRecordSourceActivity:
  1460. if (hasUnknownTransform(expr))
  1461. complexExtra->preventOptimization();
  1462. break;
  1463. }
  1464. activities.append(*LINK(expr));
  1465. IHqlExpression * child = expr->queryChild(0);
  1466. switch (extra->activityKind())
  1467. {
  1468. case CreateRecordActivity:
  1469. setOriginal(complexExtra->leftFieldsRequired, child);
  1470. break;
  1471. case CreateRecordLRActivity:
  1472. setOriginal(complexExtra->leftFieldsRequired, child);
  1473. setOriginal(complexExtra->rightFieldsRequired, expr->queryChild(1));
  1474. break;
  1475. case CompoundActivity:
  1476. case CompoundableActivity:
  1477. case CreateRecordSourceActivity:
  1478. case CreateNonEmptyRecordSourceActivity:
  1479. case AnyTypeActivity:
  1480. break;
  1481. case RollupTransformActivity:
  1482. case IterateTransformActivity:
  1483. setOriginal(complexExtra->leftFieldsRequired, child);
  1484. setOriginal(complexExtra->rightFieldsRequired, child);
  1485. break;
  1486. case DenormalizeActivity:
  1487. setOriginal(complexExtra->leftFieldsRequired, child);
  1488. setOriginal(complexExtra->rightFieldsRequired, expr->queryChild(1));
  1489. break;
  1490. case FixedInputActivity:
  1491. assertex(child && child->queryRecord());
  1492. setOriginal(complexExtra->leftFieldsRequired, child);
  1493. if (getNumChildTables(expr) >= 2)
  1494. setOriginal(complexExtra->rightFieldsRequired, expr->queryChild(1));
  1495. break;
  1496. case SourceActivity:
  1497. case PassThroughActivity:
  1498. case ScalarSelectActivity:
  1499. break;
  1500. case SinkActivity:
  1501. setOriginal(complexExtra->leftFieldsRequired, child);
  1502. break;
  1503. case SimpleActivity:
  1504. if (expr->getOperator() == no_compound)
  1505. setOriginal(complexExtra->leftFieldsRequired, expr->queryChild(1));
  1506. else
  1507. setOriginal(complexExtra->leftFieldsRequired, child);
  1508. break;
  1509. default:
  1510. throwUnexpected();
  1511. }
  1512. }
  1513. IHqlExpression * record = expr->queryRecord();
  1514. if (record && !isPatternType(type) && !expr->isTransform())
  1515. {
  1516. assertex(complexExtra);
  1517. complexExtra->setOriginalRecord(queryBodyComplexExtra(record));
  1518. analyseExpr(record);
  1519. }
  1520. gatherFieldsUsed(expr, extra);
  1521. }
  1522. void ImplicitProjectTransformer::connect(IHqlExpression * source, IHqlExpression * sink)
  1523. {
  1524. queryBodyComplexExtra(source)->outputs.append(*queryBodyComplexExtra(sink));
  1525. queryBodyComplexExtra(sink)->inputs.append(*queryBodyComplexExtra(source));
  1526. }
  1527. //NB: This is very similar to the code in CHqlExpression::cacheTablesUsed()
  1528. void ImplicitProjectTransformer::gatherFieldsUsed(IHqlExpression * expr, ImplicitProjectInfo * extra)
  1529. {
  1530. if (extra->checkGatheredSelects())
  1531. return;
  1532. node_operator op = expr->getOperator();
  1533. switch (op)
  1534. {
  1535. case no_select:
  1536. {
  1537. if (options.projectNestedTables)
  1538. {
  1539. bool isNew;
  1540. IHqlExpression * ds = querySelectorDataset(expr, isNew);
  1541. if (isNew)
  1542. inheritActiveFields(extra, ds);
  1543. else
  1544. extra->addActiveSelect(expr);
  1545. }
  1546. else
  1547. {
  1548. //Either inherit from the dataset if new, or add the root field (x.a.b only adds x.a)
  1549. IHqlExpression * cur = expr;
  1550. for (;;)
  1551. {
  1552. IHqlExpression * ds = cur->queryChild(0);
  1553. if (cur->hasAttribute(newAtom))
  1554. {
  1555. inheritActiveFields(extra, ds);
  1556. break;
  1557. }
  1558. node_operator dsOp = ds->getOperator();
  1559. if (dsOp != no_select || ds->isDataset())
  1560. {
  1561. if ((dsOp != no_self) && (dsOp != no_selfref))
  1562. extra->addActiveSelect(cur);
  1563. break;
  1564. }
  1565. cur = ds;
  1566. }
  1567. }
  1568. break;
  1569. }
  1570. case no_activerow:
  1571. //active row used in some context
  1572. extra->addActiveSelect(expr->queryChild(0));
  1573. break;
  1574. case no_left:
  1575. case no_right:
  1576. extra->addActiveSelect(expr);
  1577. //left/right used in an expression context - assume the worse..
  1578. break;
  1579. case no_attr:
  1580. case no_attr_link:
  1581. case no_getresult:
  1582. break;
  1583. case no_attr_expr:
  1584. {
  1585. IAtom * name = expr->queryName();
  1586. if (name != _selectors_Atom)
  1587. inheritActiveFields(expr, extra, 0, expr->numChildren());
  1588. }
  1589. break;
  1590. case no_newkeyindex:
  1591. {
  1592. #ifdef _DEBUG
  1593. inheritActiveFields(expr, extra, 1, expr->numChildren());
  1594. extra->removeScopedFields(expr->queryChild(0)->queryNormalizedSelector());
  1595. extra->removeScopedFields(queryActiveTableSelector()); // for distributed() etc,
  1596. inheritActiveFields(expr, extra, 0, 1);
  1597. const SelectUsedArray & selectsUsed = extra->querySelectsUsed();
  1598. if (selectsUsed.ordinality() != 0)
  1599. {
  1600. StringBuffer s;
  1601. ForEachItemIn(i, selectsUsed)
  1602. {
  1603. if (i) s.append(',');
  1604. getExprECL(&selectsUsed.item(i), s);
  1605. }
  1606. throwError1(HQLERR_IndexHasActiveFields, s.str());
  1607. }
  1608. #else
  1609. inheritActiveFields(expr, extra, 3, 4); // just in case the filename is based on a parent row???
  1610. #endif
  1611. break;
  1612. }
  1613. case no_pat_production:
  1614. {
  1615. inheritActiveFields(expr, extra, 0, expr->numChildren());
  1616. extra->removeProductionSelects();
  1617. break;
  1618. }
  1619. case no_assign:
  1620. inheritActiveFields(expr, extra, 1, 2);
  1621. break;
  1622. /*
  1623. The following can be handled using the default mechanism because we're not tracking newtables.
  1624. case NO_AGGREGATE:
  1625. case no_createset:
  1626. */
  1627. case no_table:
  1628. {
  1629. inheritActiveFields(expr, extra, 0, expr->numChildren());
  1630. IHqlExpression * parent = expr->queryChild(3);
  1631. if (parent)
  1632. extra->removeScopedFields(parent->queryNormalizedSelector());
  1633. break;
  1634. }
  1635. default:
  1636. {
  1637. #if 0
  1638. //Optimization to enable later - if no active datasets, then can't have any active fields.
  1639. //should save some processing on root datasets, but may be insignificant
  1640. if (isIndependentOfScope(expr))
  1641. break;
  1642. #endif
  1643. unsigned max = expr->numChildren();
  1644. IHqlExpression * ds = expr->queryChild(0);
  1645. switch (getChildDatasetType(expr))
  1646. {
  1647. case childdataset_none:
  1648. case childdataset_many_noscope:
  1649. case childdataset_if:
  1650. case childdataset_case:
  1651. case childdataset_map:
  1652. case childdataset_dataset_noscope:
  1653. inheritActiveFields(expr, extra, 0, max);
  1654. //None of these have any scoped arguments, so no need to remove them
  1655. break;
  1656. case childdataset_many:
  1657. {
  1658. unsigned firstAttr = getNumChildTables(expr);
  1659. inheritActiveFields(expr, extra, firstAttr, max);
  1660. extra->removeScopedFields(queryActiveTableSelector());
  1661. inheritActiveFields(expr, extra, 0, firstAttr);
  1662. break;
  1663. }
  1664. case childdataset_dataset:
  1665. {
  1666. inheritActiveFields(expr, extra, 1, max);
  1667. extra->removeScopedFields(ds->queryNormalizedSelector());
  1668. inheritActiveFields(expr, extra, 0, 1);
  1669. }
  1670. break;
  1671. case childdataset_datasetleft:
  1672. {
  1673. OwnedHqlExpr left = createSelector(no_left, ds, querySelSeq(expr));
  1674. inheritActiveFields(expr, extra, 1, max);
  1675. extra->removeScopedFields(left);
  1676. extra->removeScopedFields(ds->queryNormalizedSelector());
  1677. extra->removeRowsFields(expr, left, NULL);
  1678. inheritActiveFields(expr, extra, 0, 1);
  1679. break;
  1680. }
  1681. case childdataset_left:
  1682. {
  1683. OwnedHqlExpr left = createSelector(no_left, ds, querySelSeq(expr));
  1684. inheritActiveFields(expr, extra, 1, max);
  1685. extra->removeScopedFields(left);
  1686. extra->removeRowsFields(expr, left, NULL);
  1687. inheritActiveFields(expr, extra, 0, 1);
  1688. break;
  1689. }
  1690. case childdataset_same_left_right:
  1691. case childdataset_nway_left_right:
  1692. {
  1693. IHqlExpression * seq = querySelSeq(expr);
  1694. OwnedHqlExpr left = createSelector(no_left, ds, seq);
  1695. OwnedHqlExpr right = createSelector(no_right, ds, seq);
  1696. inheritActiveFields(expr, extra, 1, max);
  1697. extra->removeScopedFields(left);
  1698. extra->removeScopedFields(right);
  1699. extra->removeRowsFields(expr, left, right);
  1700. inheritActiveFields(expr, extra, 0, 1);
  1701. break;
  1702. }
  1703. case childdataset_top_left_right:
  1704. {
  1705. IHqlExpression * seq = querySelSeq(expr);
  1706. OwnedHqlExpr left = createSelector(no_left, ds, seq);
  1707. OwnedHqlExpr right = createSelector(no_right, ds, seq);
  1708. inheritActiveFields(expr, extra, 1, max);
  1709. extra->removeScopedFields(ds->queryNormalizedSelector());
  1710. extra->removeScopedFields(left);
  1711. extra->removeScopedFields(right);
  1712. extra->removeRowsFields(expr, left, right);
  1713. inheritActiveFields(expr, extra, 0, 1);
  1714. break;
  1715. }
  1716. case childdataset_leftright:
  1717. {
  1718. IHqlExpression * leftDs = expr->queryChild(0);
  1719. IHqlExpression * rightDs = expr->queryChild(1);
  1720. IHqlExpression * seq = querySelSeq(expr);
  1721. OwnedHqlExpr left = createSelector(no_left, leftDs, seq);
  1722. OwnedHqlExpr right = createSelector(no_right, rightDs, seq);
  1723. inheritActiveFields(expr, extra, 2, max);
  1724. extra->removeScopedFields(right);
  1725. extra->removeRowsFields(expr, left, right);
  1726. if (expr->getOperator() == no_normalize)
  1727. {
  1728. inheritActiveFields(expr, extra, 1, 2);
  1729. extra->removeScopedFields(left);
  1730. inheritActiveFields(expr, extra, 0, 1);
  1731. }
  1732. else
  1733. {
  1734. extra->removeScopedFields(left);
  1735. inheritActiveFields(expr, extra, 0, 2);
  1736. }
  1737. break;
  1738. }
  1739. break;
  1740. case childdataset_evaluate:
  1741. //handled elsewhere...
  1742. default:
  1743. UNIMPLEMENTED;
  1744. }
  1745. switch (op)
  1746. {
  1747. case no_newparse:
  1748. case no_parse:
  1749. extra->removeProductionSelects();
  1750. break;
  1751. }
  1752. #ifdef _DEBUG
  1753. //The following code is commented out because it doesn't work with implicit normalize of child datasets
  1754. //E.g., ds(count(ds.x.y(ds.x != 0)))
  1755. //The problem is that it is hard to determine when ds.x is no longer valid. (It is implicitly brought
  1756. //into scope by the use of ds.x.y. The correct solution is for it to be removed by the last thing
  1757. //that uses the dataset operator - i.e. the count, or once normalized the [1] on the no_newaggregate.
  1758. //There are (semi-pathological) examples of this in the regression suite.
  1759. //Until it is revisited and fixed the following lines should stay commented out.
  1760. #if 0
  1761. const SelectUsedArray & selectsUsed = extra->querySelectsUsed();
  1762. if (isIndependentOfScope(expr) && selectsUsed.ordinality() != 0)
  1763. {
  1764. switch (expr->getOperator())
  1765. {
  1766. case no_csv:
  1767. case no_xml:
  1768. break;
  1769. default:
  1770. {
  1771. StringBuffer s;
  1772. ForEachItemIn(i, selectsUsed)
  1773. {
  1774. if (i) s.append(',');
  1775. getExprECL(&selectsUsed.item(i), s);
  1776. }
  1777. throwError1(HQLERR_GlobalHasActiveFields, s.str());
  1778. }
  1779. }
  1780. }
  1781. #endif
  1782. #endif
  1783. break;
  1784. }
  1785. }
  1786. }
  1787. const SelectUsedArray & ImplicitProjectTransformer::querySelectsUsed(IHqlExpression * expr)
  1788. {
  1789. ImplicitProjectInfo * extra = queryBodyExtra(expr);
  1790. // gatherFieldsUsed(expr, extra);
  1791. return extra->querySelectsUsed();
  1792. }
  1793. ProjectExprKind ImplicitProjectTransformer::getProjectExprKind(IHqlExpression * expr)
  1794. {
  1795. switch (expr->getOperator())
  1796. {
  1797. case no_evaluate:
  1798. throwUnexpected();
  1799. case no_select:
  1800. if (hasActivityType(expr))
  1801. return SourceActivity;
  1802. if (isNewSelector(expr))
  1803. return ScalarSelectActivity;
  1804. return NonActivity;
  1805. case no_activerow:
  1806. return SourceActivity;
  1807. case no_attr:
  1808. case no_attr_expr:
  1809. case no_attr_link:
  1810. return NonActivity;
  1811. case no_typetransfer:
  1812. if (hasActivityType(expr))
  1813. return SourceActivity;
  1814. return NonActivity;
  1815. case no_thor:
  1816. if (hasActivityType(expr))
  1817. return SimpleActivity;
  1818. return NonActivity;
  1819. case no_compound:
  1820. if (hasActivityType(expr))
  1821. return SimpleActivity;
  1822. return NonActivity;
  1823. case no_executewhen:
  1824. if (hasActivityType(expr))
  1825. return SimpleActivity;
  1826. return NonActivity;
  1827. case no_subgraph:
  1828. case no_libraryscopeinstance:
  1829. return NonActivity;
  1830. case no_mergejoin:
  1831. case no_nwayjoin: // could probably project output of this one...
  1832. case no_nwaymerge:
  1833. case no_libraryselect:
  1834. case no_datasetfromdictionary:
  1835. return SourceActivity;
  1836. case no_setresult:
  1837. case no_ensureresult:
  1838. {
  1839. IHqlExpression * value = expr->queryChild(0);
  1840. if (value->isDataset() || value->isDatarow())
  1841. return FixedInputActivity;
  1842. return NonActivity;
  1843. }
  1844. case no_newrow: //only used while transforming
  1845. case no_newaggregate:
  1846. case no_hqlproject:
  1847. case no_normalize:
  1848. case no_newusertable:
  1849. case no_newparse:
  1850. case no_newxmlparse:
  1851. case no_createrow:
  1852. case no_rollupgroup:
  1853. case no_projectrow:
  1854. case no_quantile:
  1855. return CreateRecordActivity;
  1856. case no_inlinetable:
  1857. case no_dataset_from_transform:
  1858. return CreateRecordSourceActivity;
  1859. case no_createdictionary:
  1860. return FixedInputActivity;
  1861. case no_selectmap:
  1862. return SourceActivity;
  1863. case no_extractresult:
  1864. case no_apply:
  1865. return SinkActivity;
  1866. case no_denormalizegroup:
  1867. case no_join:
  1868. case no_fetch:
  1869. return CreateRecordLRActivity;
  1870. case no_process: // optimization currently disabled...
  1871. return PassThroughActivity;
  1872. case no_iterate:
  1873. return IterateTransformActivity;
  1874. case no_rollup:
  1875. return RollupTransformActivity;
  1876. case no_denormalize:
  1877. return DenormalizeActivity;
  1878. case no_null:
  1879. if (expr->isAction())
  1880. return NonActivity;
  1881. return AnyTypeActivity;
  1882. case no_skip:
  1883. case no_fail:
  1884. if (hasActivityType(expr))
  1885. return AnyTypeActivity;
  1886. return NonActivity;
  1887. case no_table:
  1888. switch (expr->queryChild(2)->getOperator())
  1889. {
  1890. case no_thor:
  1891. case no_flat:
  1892. if (expr->hasAttribute(_spill_Atom) && options.isRoxie)
  1893. return SourceActivity;
  1894. if (options.optimizeProjectsPreservePersists)
  1895. {
  1896. //Don't project persists because it can mess up the redistibution code.
  1897. if (expr->hasAttribute(_workflowPersist_Atom))
  1898. return SourceActivity;
  1899. }
  1900. return CompoundableActivity;
  1901. case no_csv:
  1902. if (options.enableCompoundCsvRead)
  1903. return CompoundableActivity;
  1904. return SourceActivity;
  1905. default:
  1906. return SourceActivity;
  1907. }
  1908. case no_newkeyindex:
  1909. return CompoundableActivity;
  1910. case no_compound_diskread:
  1911. case no_compound_disknormalize:
  1912. case no_compound_indexread:
  1913. case no_compound_indexnormalize:
  1914. {
  1915. if (options.optimizeProjectsPreservePersists)
  1916. {
  1917. //Don't project persists because it can mess up the redistibution code.
  1918. IHqlExpression * root = queryRoot(expr);
  1919. if (root && root->hasAttribute(_workflowPersist_Atom))
  1920. return SourceActivity;
  1921. }
  1922. return CompoundActivity;
  1923. }
  1924. case no_compound_diskaggregate:
  1925. case no_compound_diskcount:
  1926. case no_compound_diskgroupaggregate:
  1927. case no_compound_indexaggregate:
  1928. case no_compound_indexcount:
  1929. case no_compound_indexgroupaggregate:
  1930. //Don't want to add projects to these...
  1931. return SimpleActivity;
  1932. case no_preload:
  1933. case no_forcelocal:
  1934. case no_workunit_dataset:
  1935. case no_getgraphresult:
  1936. case no_getgraphloopresult:
  1937. case no_rows:
  1938. return SourceActivity;
  1939. case no_getresult:
  1940. if (hasActivityType(expr))
  1941. return SourceActivity;
  1942. return NonActivity;
  1943. case no_allnodes:
  1944. case no_httpcall:
  1945. case no_soapcall:
  1946. case no_newsoapcall:
  1947. case no_libraryinput:
  1948. case no_thisnode:
  1949. if (hasActivityType(expr))
  1950. return SourceActivity;
  1951. return NonActivity;
  1952. case no_pipe:
  1953. case no_nofold:
  1954. case no_nohoist:
  1955. if (hasActivityType(expr))
  1956. return FixedInputActivity;
  1957. return NonActivity;
  1958. case no_soapcall_ds:
  1959. case no_newsoapcall_ds:
  1960. case no_output:
  1961. case no_distribution:
  1962. case no_buildindex:
  1963. case no_spill:
  1964. case no_setgraphresult:
  1965. case no_setgraphloopresult:
  1966. case no_spillgraphresult:
  1967. //MORE: Rethink these later:
  1968. case no_combine:
  1969. case no_combinegroup:
  1970. case no_regroup:
  1971. case no_loop:
  1972. case no_graphloop:
  1973. case no_filtergroup: //anything else would be tricky...
  1974. case no_normalizegroup:
  1975. case no_getgraphloopresultset:
  1976. return FixedInputActivity;
  1977. case no_aggregate:
  1978. if (expr->hasAttribute(mergeTransformAtom))
  1979. return FixedInputActivity;
  1980. return FixedInputActivity; //MORE:???? Should be able to optimize this
  1981. case no_fromxml: // A bit bit like a source activity, no transform..., but has an input
  1982. case no_fromjson:
  1983. return SourceActivity;
  1984. case no_selfjoin:
  1985. return CreateRecordActivity;
  1986. case no_if:
  1987. if (hasActivityType(expr))
  1988. return PassThroughActivity;
  1989. return NonActivity;
  1990. case no_addfiles:
  1991. case no_merge:
  1992. case no_nonempty:
  1993. case no_cogroup:
  1994. case no_chooseds:
  1995. case no_nocombine:
  1996. return PassThroughActivity;
  1997. case no_keydiff:
  1998. case no_keypatch:
  1999. return NonActivity;
  2000. case no_datasetfromrow:
  2001. return SimpleActivity;
  2002. case no_newtransform:
  2003. case no_transform:
  2004. return NonActivity;
  2005. return ComplexNonActivity;
  2006. case no_record:
  2007. case no_assign:
  2008. case no_assignall:
  2009. return NonActivity;
  2010. case NO_AGGREGATE:
  2011. case no_createset:
  2012. return SinkActivity;
  2013. case no_call:
  2014. case no_externalcall:
  2015. if (hasActivityType(expr))
  2016. {
  2017. if (isProjectableCall(expr))
  2018. return CreateNonEmptyRecordSourceActivity;
  2019. else
  2020. return SourceActivity;
  2021. }
  2022. //MORE: What about parameters??
  2023. return NonActivity;
  2024. case no_commonspill:
  2025. case no_readspill:
  2026. return SimpleActivity;
  2027. case no_writespill:
  2028. return SinkActivity;
  2029. case no_preservemeta:
  2030. case no_dataset_alias:
  2031. if (getProjectExprKind(expr->queryChild(0)) == CompoundableActivity)
  2032. return CompoundableActivity;
  2033. return PassThroughActivity;
  2034. case no_serialize:
  2035. case no_deserialize:
  2036. //This needs to map fields by name. Until that is implemented don't project these types.
  2037. return FixedInputActivity;
  2038. }
  2039. ITypeInfo * type = expr->queryType();
  2040. if (!type)
  2041. return NonActivity;
  2042. type_t tc = type->getTypeCode();
  2043. switch (tc)
  2044. {
  2045. case type_void:
  2046. if (getNumChildTables(expr) > 0)
  2047. return SinkActivity;
  2048. return NonActivity;
  2049. case type_row:
  2050. case type_table:
  2051. case type_groupedtable:
  2052. break;
  2053. case type_dictionary:
  2054. return FixedInputActivity;
  2055. case type_transform:
  2056. return NonActivity;
  2057. default:
  2058. return NonActivity;
  2059. }
  2060. if (getNumActivityArguments(expr) == 0)
  2061. return SourceActivity;
  2062. return SimpleActivity;
  2063. }
  2064. void ImplicitProjectTransformer::processSelect(ComplexImplicitProjectInfo * extra, IHqlExpression * curSelect, IHqlExpression * ds, IHqlExpression * leftSelect, IHqlExpression * rightSelect)
  2065. {
  2066. if (leftSelect)
  2067. processMatchingSelector(extra->leftFieldsRequired, curSelect, leftSelect);
  2068. if (ds)
  2069. processMatchingSelector(extra->leftFieldsRequired, curSelect, ds);
  2070. if (rightSelect)
  2071. processMatchingSelector(extra->rightFieldsRequired, curSelect, rightSelect);
  2072. switch (extra->activityKind())
  2073. {
  2074. case DenormalizeActivity:
  2075. //For DENORMALIZE the transform is always called, possibly multiple times. Therefore
  2076. //if a field is used from the output it must be included in the input (but could be blanked)
  2077. //if a field is used from LEFT then it must be in the input and the output
  2078. processMatchingSelector(extra->outputFields, curSelect, leftSelect);
  2079. break;
  2080. case RollupTransformActivity:
  2081. case IterateTransformActivity:
  2082. //For ROLLUP/ITERATE the transform may or may not be called. Therefore
  2083. //if a field is used from the output it is used from the input [ handled in the main processing loop]
  2084. //if a field is used from LEFT then it must be in the input and the output
  2085. //Anything used from the input must be in the output (but could be blanked) - handled elsewhere
  2086. processMatchingSelector(extra->outputFields, curSelect, leftSelect);
  2087. if (ds)
  2088. processMatchingSelector(extra->outputFields, curSelect, ds);
  2089. break;
  2090. }
  2091. }
  2092. void ImplicitProjectTransformer::processSelects(ComplexImplicitProjectInfo * extra, SelectUsedArray const & selectsUsed, IHqlExpression * ds, IHqlExpression * leftSelect, IHqlExpression * rightSelect)
  2093. {
  2094. ForEachItemIn(i2, selectsUsed)
  2095. {
  2096. IHqlExpression * curSelect = &selectsUsed.item(i2);
  2097. processSelect(extra, curSelect, ds, leftSelect, rightSelect);
  2098. }
  2099. }
  2100. void ImplicitProjectTransformer::processSelects(ComplexImplicitProjectInfo * extra, HqlExprArray const & selectsUsed, IHqlExpression * ds, IHqlExpression * leftSelect, IHqlExpression * rightSelect)
  2101. {
  2102. ForEachItemIn(i2, selectsUsed)
  2103. {
  2104. IHqlExpression * curSelect = &selectsUsed.item(i2);
  2105. processSelect(extra, curSelect, ds, leftSelect, rightSelect);
  2106. }
  2107. }
  2108. void ImplicitProjectTransformer::processTransform(ComplexImplicitProjectInfo * extra, IHqlExpression * transform, IHqlExpression * dsSelect, IHqlExpression * leftSelect, IHqlExpression * rightSelect)
  2109. {
  2110. HqlExprCopyArray assigns;
  2111. unwindTransform(assigns, transform);
  2112. ForEachItemIn(itr, assigns)
  2113. {
  2114. IHqlExpression * cur = &assigns.item(itr);
  2115. //Need to handle skip attributes...
  2116. switch (cur->getOperator())
  2117. {
  2118. case no_assign:
  2119. {
  2120. IHqlExpression * value = cur->queryChild(1);
  2121. if (containsSkip(value))
  2122. {
  2123. IHqlExpression * lhs = cur->queryChild(0);
  2124. processMatchingSelector(extra->outputFields, lhs, lhs->queryChild(0));
  2125. const SelectUsedArray & selectsUsed = querySelectsUsed(value);
  2126. processSelects(extra, selectsUsed, dsSelect, leftSelect, rightSelect);
  2127. }
  2128. break;
  2129. }
  2130. case no_attr:
  2131. case no_attr_expr:
  2132. break;
  2133. default:
  2134. {
  2135. const SelectUsedArray & selectsUsed = querySelectsUsed(cur);
  2136. processSelects(extra, selectsUsed, dsSelect, leftSelect, rightSelect);
  2137. break;
  2138. }
  2139. }
  2140. }
  2141. }
  2142. void ImplicitProjectTransformer::calculateFieldsUsed(IHqlExpression * expr)
  2143. {
  2144. ComplexImplicitProjectInfo * extra = queryBodyComplexExtra(expr);
  2145. if (!extra->okToOptimize())
  2146. {
  2147. if (expr->queryRecord() && !expr->isDictionary())
  2148. extra->addAllOutputs();
  2149. }
  2150. else
  2151. {
  2152. ForEachItemIn(i1, extra->outputs)
  2153. extra->outputs.item(i1).notifyRequiredFields(extra);
  2154. if (extra->outputFields.includeAll())
  2155. assertex(extra->queryOutputRecord() != NULL);
  2156. }
  2157. switch (extra->activityKind())
  2158. {
  2159. case CreateRecordActivity:
  2160. case CreateRecordLRActivity:
  2161. {
  2162. //output will now be whatever fields are required by the output fields.
  2163. //input will be whatever is used in the appropriate transforms.
  2164. IHqlExpression * transform = queryNewColumnProvider(expr);
  2165. if (!isKnownTransform(transform))
  2166. {
  2167. extra->leftFieldsRequired.setAll();
  2168. if (extra->activityKind() == CreateRecordLRActivity)
  2169. extra->rightFieldsRequired.setAll();
  2170. break;
  2171. }
  2172. IHqlExpression * ds = expr->queryChild(0)->queryNormalizedSelector();
  2173. IHqlExpression * selSeq = querySelSeq(expr);
  2174. OwnedHqlExpr dsSelect = LINK(ds);
  2175. OwnedHqlExpr leftSelect;
  2176. OwnedHqlExpr rightSelect;
  2177. if (selSeq)
  2178. leftSelect.setown(createSelector(no_left, ds, selSeq));
  2179. if (extra->activityKind() == CreateRecordLRActivity)
  2180. rightSelect.setown(createSelector(no_right, expr->queryChild(1), selSeq));
  2181. if (expr->getOperator() == no_selfjoin)
  2182. dsSelect.setown(createSelector(no_right, ds, selSeq));
  2183. //This is here to ensure that calls that have side-effects don't get removed because the fields are removed
  2184. if (hasSideEffects(transform))
  2185. extra->addAllOutputs();
  2186. //MORE: querySelectsUsedForField() could be optimized by creating a map first, but it is only ~1% of time, so not really worth it.
  2187. HqlExprArray parentSelects;
  2188. HqlExprArray values;
  2189. extra->outputFields.gatherTransformValuesUsed(NULL, &parentSelects, &values, NULL, transform);
  2190. processSelects(extra, parentSelects, dsSelect, leftSelect, rightSelect);
  2191. ForEachItemIn(i, values)
  2192. processSelects(extra, querySelectsUsed(&values.item(i)), dsSelect, leftSelect, rightSelect);
  2193. if (!extra->outputFields.allGathered())
  2194. assertex(extra->outputFields.allGathered());
  2195. processTransform(extra, transform, dsSelect, leftSelect, rightSelect);
  2196. unsigned max = expr->numChildren();
  2197. unsigned first = extra->inputs.ordinality();
  2198. if (expr->getOperator() == no_fetch)
  2199. first = 2;
  2200. for (unsigned i2=first; i2 < max; i2++)
  2201. {
  2202. IHqlExpression * cur = expr->queryChild(i2);
  2203. if (cur == transform)
  2204. continue;
  2205. const SelectUsedArray & selectsUsed = querySelectsUsed(cur);
  2206. processSelects(extra, selectsUsed, dsSelect, leftSelect, rightSelect);
  2207. }
  2208. switch (expr->getOperator())
  2209. {
  2210. case no_newusertable:
  2211. case no_hqlproject:
  2212. if (extra->okToOptimize())
  2213. extra->inputs.item(0).stopOptimizeCompound(false);
  2214. break;
  2215. case no_newaggregate:
  2216. {
  2217. IHqlExpression * grouping = queryRealChild(expr, 3);
  2218. if (grouping)
  2219. {
  2220. //Need to make sure that grouping criteria fields are also in the output
  2221. ForEachChild(i, grouping)
  2222. {
  2223. IHqlExpression * curGrouping = grouping->queryChild(i);
  2224. IHqlExpression * match = NULL;
  2225. //All groupings have entries in the transform - find the corresponding field.
  2226. ForEachChild(j, transform)
  2227. {
  2228. IHqlExpression * cur = transform->queryChild(j);
  2229. IHqlExpression * rhs = cur->queryChild(1);
  2230. if (rhs->getOperator() == no_activerow)
  2231. rhs = rhs->queryChild(0);
  2232. if (rhs == curGrouping)
  2233. {
  2234. match = cur->queryChild(0);
  2235. break;
  2236. }
  2237. }
  2238. //There may possibly be no match if it hasn't been normalized yet.
  2239. if (match)
  2240. processMatchingSelector(extra->outputFields, match, match->queryChild(0));
  2241. }
  2242. }
  2243. break;
  2244. }
  2245. }
  2246. break;
  2247. }
  2248. case CompoundActivity:
  2249. {
  2250. //output will now be whatever fields are required by the output fields.
  2251. //input will be the same as the output fields, since it is just a wrapper node.
  2252. extra->finalizeOutputRecord(false);
  2253. //MORE: Not sure this is neededextra->leftFieldsRequired.clone(extra->outputFields);
  2254. extra->insertProject = true;
  2255. assertex(extra->inputs.ordinality() == 0);
  2256. //extra->inputs.item(0).stopOptimizeCompound(true);
  2257. break;
  2258. }
  2259. case CompoundableActivity:
  2260. {
  2261. //Prevent preserve meta from stripping the disk read down to a single field.
  2262. if (extra->inputs.ordinality())
  2263. extra->inputs.item(0).stopOptimizeCompound(true);
  2264. if (extra->okToOptimize())
  2265. extra->finalizeOutputRecord(false);
  2266. break;
  2267. }
  2268. case CreateNonEmptyRecordSourceActivity:
  2269. case CreateRecordSourceActivity:
  2270. case AnyTypeActivity:
  2271. {
  2272. if (extra->okToOptimize())
  2273. extra->finalizeOutputRecord(extra->activityKind() == CreateNonEmptyRecordSourceActivity);
  2274. break;
  2275. }
  2276. case PassThroughActivity:
  2277. if (extra->okToOptimize())
  2278. {
  2279. node_operator op = expr->getOperator();
  2280. if ((op == no_if) && expr->hasAttribute(_resourced_Atom))
  2281. {
  2282. extra->preventOptimization();
  2283. extra->addAllOutputs();
  2284. }
  2285. else if (op == no_merge)
  2286. {
  2287. //Ensure all the fields used by the sort order are preserved in the input streams
  2288. IHqlExpression * order = expr->queryAttribute(sortedAtom);
  2289. assertex(order);
  2290. ForEachChild(i, order)
  2291. {
  2292. IHqlExpression * cur = order->queryChild(i);
  2293. if (!cur->isAttribute() && !cur->isConstant()) // shouldn't really happen..
  2294. {
  2295. if ((cur->getOperator() == no_select) && !isNewSelector(cur))
  2296. {
  2297. IHqlExpression * ds = queryDatasetCursor(cur);
  2298. if (ds == queryActiveTableSelector())
  2299. processMatchingSelector(extra->outputFields, cur, queryActiveTableSelector());
  2300. else
  2301. extra->addAllOutputs();
  2302. }
  2303. else
  2304. extra->addAllOutputs();
  2305. }
  2306. }
  2307. }
  2308. }
  2309. //No need to do anything - inputs are taken directly from required outputs
  2310. break;
  2311. case ScalarSelectActivity:
  2312. {
  2313. IHqlExpression * root = queryRootSelector(expr);
  2314. extra->leftFieldsRequired.appendField(*LINK(root->queryChild(1)));
  2315. break;
  2316. }
  2317. case RollupTransformActivity:
  2318. case IterateTransformActivity:
  2319. {
  2320. //currently rollup and iterate
  2321. //output record is fixed by input, and never gets changed.
  2322. //input is all fields used required in output (since can't change record format) plus any others used inside the transform
  2323. IHqlExpression * transform = queryNewColumnProvider(expr);
  2324. if (hasSideEffects(transform))
  2325. extra->addAllOutputs();
  2326. if (extra->outputFields.includeAll())
  2327. extra->leftFieldsRequired.setAll();
  2328. else
  2329. {
  2330. IHqlExpression * dsSelect = expr->queryChild(0)->queryNormalizedSelector();
  2331. IHqlExpression * selSeq = querySelSeq(expr);
  2332. OwnedHqlExpr leftSelect = createSelector(no_left, dsSelect, selSeq);
  2333. OwnedHqlExpr rightSelect = createSelector(no_right, dsSelect, selSeq);
  2334. //Need to handle skip attributes...
  2335. processTransform(extra, transform, dsSelect, leftSelect, rightSelect);
  2336. //Rollup criteria need to be included in the fields used!
  2337. unsigned max = expr->numChildren();
  2338. for (unsigned i2=1; i2 < max; i2++)
  2339. {
  2340. if (extra->leftFieldsRequired.includeAll())
  2341. break;
  2342. IHqlExpression * cur = expr->queryChild(i2);
  2343. if (cur != transform)
  2344. {
  2345. const SelectUsedArray & selectsUsed = querySelectsUsed(cur);
  2346. processSelects(extra, selectsUsed, dsSelect, leftSelect, rightSelect);
  2347. }
  2348. }
  2349. //NB: outputfields can extend...
  2350. while (!extra->outputFields.allGathered())
  2351. {
  2352. HqlExprArray parentSelects;
  2353. HqlExprArray values;
  2354. HqlExprArray selfSelects;
  2355. extra->outputFields.gatherTransformValuesUsed(&selfSelects, &parentSelects, &values, dsSelect, transform);
  2356. \
  2357. //For ROLLUP/ITERATE the transform may or may not be called. Therefore
  2358. //if a field is used from the output it is used from the input
  2359. //if a field is used from LEFT then it must be in the input and the output
  2360. //if a field is used from RIGHT it muse be in the output (but could be blanked) - handled elsewhere
  2361. //Ensure all output rows are also included in the input dataset
  2362. ForEachItemIn(i1, selfSelects)
  2363. processMatchingSelector(extra->leftFieldsRequired, &selfSelects.item(i1), dsSelect);
  2364. processSelects(extra, parentSelects, NULL, leftSelect, rightSelect);
  2365. ForEachItemIn(i2, values)
  2366. processSelects(extra, querySelectsUsed(&values.item(i2)), NULL, leftSelect, rightSelect);
  2367. //If all fields selected from the output then select all fields from the input
  2368. if (extra->outputFields.checkAllFieldsUsed())
  2369. extra->leftFieldsRequired.setAll();
  2370. //if selected all fields from the input then already done.
  2371. if (extra->leftFieldsRequired.includeAll())
  2372. {
  2373. extra->outputFields.setAll();
  2374. break;
  2375. }
  2376. }
  2377. if (extra->leftFieldsRequired.includeAll())
  2378. extra->addAllOutputs();
  2379. }
  2380. break;
  2381. }
  2382. case DenormalizeActivity:
  2383. {
  2384. //output record is fixed by input
  2385. //input is all fields used required in output (since can't change record format) plus any others used inside the transform
  2386. IHqlExpression * transform = queryNewColumnProvider(expr);
  2387. if (hasSideEffects(transform))
  2388. extra->addAllOutputs();
  2389. if (extra->outputFields.includeAll())
  2390. extra->leftFieldsRequired.setAll();
  2391. IHqlExpression * left = expr->queryChild(0)->queryNormalizedSelector();
  2392. IHqlExpression * right = expr->queryChild(1)->queryNormalizedSelector();
  2393. IHqlExpression * selSeq = querySelSeq(expr);
  2394. OwnedHqlExpr leftSelect = createSelector(no_left, left, selSeq);
  2395. OwnedHqlExpr rightSelect = createSelector(no_right, right, selSeq);
  2396. processTransform(extra, transform, NULL, leftSelect, rightSelect);
  2397. //include all other attributes except for the transform
  2398. unsigned max = expr->numChildren();
  2399. for (unsigned i2=2; i2 < max; i2++)
  2400. {
  2401. IHqlExpression * cur = expr->queryChild(i2);
  2402. if (cur != transform)
  2403. {
  2404. const SelectUsedArray & selectsUsed = querySelectsUsed(cur);
  2405. processSelects(extra, selectsUsed, NULL, leftSelect, rightSelect);
  2406. }
  2407. }
  2408. while (!extra->outputFields.allGathered())
  2409. {
  2410. HqlExprArray parentSelects;
  2411. HqlExprArray values;
  2412. HqlExprArray selfSelects;
  2413. extra->outputFields.gatherTransformValuesUsed(&selfSelects, &parentSelects, &values, left, transform);
  2414. //For DENORMALIZE the transform is always called, possibly multiple times. Therefore
  2415. //if a field is used from the output it must be included in the input (but could be blanked)
  2416. //if a field is used from LEFT then it must be in the input and the output
  2417. //Ensure all output rows are also included in the input dataset
  2418. ForEachItemIn(i1, selfSelects)
  2419. processMatchingSelector(extra->leftFieldsRequired, &selfSelects.item(i1), left); // more: Could blank
  2420. processSelects(extra, parentSelects, NULL, leftSelect, rightSelect);
  2421. ForEachItemIn(i2, values)
  2422. processSelects(extra, querySelectsUsed(&values.item(i2)), NULL, leftSelect, rightSelect);
  2423. }
  2424. break;
  2425. }
  2426. case FixedInputActivity:
  2427. {
  2428. extra->leftFieldsRequired.setAll();
  2429. extra->rightFieldsRequired.setAllIfAny();
  2430. if (expr->queryRecord())
  2431. extra->addAllOutputs();
  2432. break;
  2433. }
  2434. case SourceActivity:
  2435. {
  2436. //No inputs to worry about, and not compoundable so output record won't change.
  2437. extra->addAllOutputs();
  2438. break;
  2439. }
  2440. case SinkActivity:
  2441. case SimpleActivity:
  2442. {
  2443. //inputs will be outputs required plus any fields used within the function
  2444. //outputs will eventually match inputs when finished percolating.
  2445. if (extra->outputFields.includeAll())
  2446. extra->leftFieldsRequired.setAll();
  2447. else
  2448. {
  2449. if (extra->activityKind() != SinkActivity)
  2450. extra->leftFieldsRequired.clone(extra->outputFields);
  2451. IHqlExpression * ds = expr->queryChild(0)->queryNormalizedSelector();
  2452. IHqlExpression * selSeq = querySelSeq(expr);
  2453. //Left and right are here because of the dedup criteria. It would be better to
  2454. //special case that, but first lets get it working
  2455. OwnedHqlExpr leftSelect = selSeq ? createSelector(no_left, ds, selSeq) : NULL;
  2456. OwnedHqlExpr rightSelect = selSeq ? createSelector(no_right, ds, selSeq) : NULL;
  2457. unsigned max = expr->numChildren();
  2458. for (unsigned i2=1; i2 < max; i2++)
  2459. {
  2460. const SelectUsedArray & selectsUsed = querySelectsUsed(expr->queryChild(i2));
  2461. ForEachItemIn(i3, selectsUsed)
  2462. {
  2463. IHqlExpression * curSelect = &selectsUsed.item(i3);
  2464. processMatchingSelector(extra->leftFieldsRequired, curSelect, leftSelect);
  2465. processMatchingSelector(extra->leftFieldsRequired, curSelect, rightSelect);
  2466. processMatchingSelector(extra->leftFieldsRequired, curSelect, ds);
  2467. if (extra->leftFieldsRequired.includeAll())
  2468. break;
  2469. }
  2470. if (extra->leftFieldsRequired.includeAll())
  2471. break;
  2472. }
  2473. if (extra->activityKind() != SinkActivity)
  2474. {
  2475. if (extra->leftFieldsRequired.includeAll())
  2476. extra->addAllOutputs();
  2477. }
  2478. }
  2479. break;
  2480. }
  2481. default:
  2482. throwUnexpected();
  2483. }
  2484. }
  2485. void ImplicitProjectTransformer::logChange(const char * message, IHqlExpression * expr, const UsedFieldSet & fields)
  2486. {
  2487. IAtom * exprName = expr->queryName();
  2488. if (!exprName && isCompoundSource(expr))
  2489. exprName = expr->queryChild(0)->queryName();
  2490. StringBuffer name, fieldText;
  2491. if (exprName)
  2492. name.append(exprName).append(" ");
  2493. name.append(getOpString(expr->getOperator()));
  2494. const UsedFieldSet * original = fields.queryOriginal();
  2495. assertex(original);
  2496. fieldText.append("(").append(fields.numFields());
  2497. fieldText.append("/").append(original->numFields());
  2498. fieldText.append(")");
  2499. //If number removed < number remaining just log the fields removed.
  2500. if (fields.numFields() * 2 > original->numFields())
  2501. {
  2502. UsedFieldSet removed;
  2503. removed.createDifference(*original, fields);
  2504. fieldText.append(" removed ");
  2505. removed.getText(fieldText);
  2506. }
  2507. else
  2508. fields.getText(fieldText);
  2509. const char * const format = "ImplicitProject: %s %s now %s";
  2510. DBGLOG(format, message, name.str(), fieldText.str());
  2511. if (options.notifyOptimizedProjects)
  2512. {
  2513. if (options.notifyOptimizedProjects >= 2 || exprName)
  2514. {
  2515. StringBuffer messageText;
  2516. messageText.appendf(format, message, name.str(), fieldText.str());
  2517. translator.addWorkunitException(SeverityInformation, 0, messageText.str(), NULL);
  2518. }
  2519. }
  2520. }
  2521. void ImplicitProjectTransformer::getTransformedChildren(IHqlExpression * expr, HqlExprArray & children)
  2522. {
  2523. transformChildren(expr, children);
  2524. }
  2525. IHqlExpression * ImplicitProjectTransformer::createParentTransformed(IHqlExpression * expr)
  2526. {
  2527. OwnedHqlExpr transformed = Parent::createTransformed(expr);
  2528. updateOrphanedSelectors(transformed, expr);
  2529. return transformed.getClear();
  2530. }
  2531. IHqlExpression * ImplicitProjectTransformer::createTransformed(IHqlExpression * expr)
  2532. {
  2533. if (expr->isConstant())
  2534. {
  2535. switch (expr->getOperator())
  2536. {
  2537. case no_transform:
  2538. case no_newtransform:
  2539. case no_transformlist:
  2540. case no_list:
  2541. return LINK(expr);
  2542. }
  2543. }
  2544. //Can't call Parent::createTransformed as a default because the TranformRecordActivities trigger asserts when types mismatch.
  2545. ImplicitProjectInfo * extra = queryBodyExtra(expr);
  2546. ComplexImplicitProjectInfo * complexExtra = extra->queryComplexInfo();
  2547. if (!complexExtra)
  2548. return createParentTransformed(expr);
  2549. OwnedHqlExpr transformed;
  2550. switch (extra->activityKind())
  2551. {
  2552. case DenormalizeActivity:
  2553. case RollupTransformActivity:
  2554. case IterateTransformActivity:
  2555. {
  2556. //Always reduce things that create a new record so they only project the fields they need to
  2557. if (complexExtra->outputChanged() || !complexExtra->fieldsToBlank.isEmpty())
  2558. {
  2559. unsigned transformPos = queryTransformIndex(expr);
  2560. //Walk transform, only including assigns that are in the output list.
  2561. HqlExprArray args;
  2562. getTransformedChildren(expr, args);
  2563. //MORE: If the input's output contains fields that are not required in this transforms output then
  2564. //include them, but assign them default values to stop them pulling in other variables.
  2565. IHqlExpression * transform = &args.item(transformPos);
  2566. IHqlExpression * newTransform = complexExtra->outputFields.createFilteredTransform(transform, &complexExtra->fieldsToBlank);
  2567. args.replace(*newTransform, transformPos);
  2568. transformed.setown(expr->clone(args));
  2569. transformed.setown(updateSelectors(transformed, expr));
  2570. logChange("Transform", expr, complexExtra->outputFields);
  2571. }
  2572. else
  2573. {
  2574. #ifdef _DEBUG
  2575. IHqlExpression * ds = expr->queryChild(0);
  2576. OwnedHqlExpr transformedDs = transform(ds);
  2577. assertex(recordTypesMatch(ds, transformedDs));
  2578. #endif
  2579. transformed.setown(createParentTransformed(expr));
  2580. //MORE: Need to replace left/right with their transformed varieties because the record may have changed format
  2581. transformed.setown(updateSelectors(transformed, expr));
  2582. }
  2583. break;
  2584. }
  2585. case CreateRecordActivity:
  2586. case CreateRecordLRActivity:
  2587. {
  2588. //Always reduce things that create a new record so they only project the fields they need to
  2589. if (complexExtra->outputChanged())
  2590. {
  2591. unsigned transformPos = queryTransformIndex(expr);
  2592. //Walk transform, only including assigns that are in the output list.
  2593. HqlExprArray args;
  2594. getTransformedChildren(expr, args);
  2595. IHqlExpression * transform = &args.item(transformPos);
  2596. IHqlExpression * newTransform = complexExtra->outputFields.createFilteredTransform(transform, NULL);
  2597. args.replace(*newTransform, transformPos);
  2598. if (transform->getOperator() == no_newtransform)
  2599. args.replace(*LINK(complexExtra->queryOutputRecord()), transformPos-1);
  2600. IHqlExpression * onFail = queryAttribute(onFailAtom, args);
  2601. if (onFail)
  2602. {
  2603. IHqlExpression * newTransform = complexExtra->outputFields.createFilteredTransform(onFail->queryChild(0), NULL);
  2604. IHqlExpression * newOnFail = createExprAttribute(onFailAtom, newTransform);
  2605. args.replace(*newOnFail, args.find(*onFail));
  2606. }
  2607. //We may have converted a count project into a project..... (see bug18839.xhql)
  2608. if (expr->getOperator() == no_hqlproject)
  2609. {
  2610. IHqlExpression * countProjectAttr = queryAttribute(_countProject_Atom, args);
  2611. if (countProjectAttr && !transformContainsCounter(newTransform, countProjectAttr->queryChild(0)))
  2612. args.zap(*countProjectAttr);
  2613. }
  2614. transformed.setown(expr->clone(args));
  2615. transformed.setown(updateSelectors(transformed, expr));
  2616. logChange("Minimize", expr, complexExtra->outputFields);
  2617. }
  2618. else
  2619. {
  2620. transformed.setown(createParentTransformed(expr));
  2621. //MORE: Need to replace left/right with their transformed varieties because the record may have changed format
  2622. transformed.setown(updateSelectors(transformed, expr));
  2623. }
  2624. break;
  2625. }
  2626. case CreateRecordSourceActivity:
  2627. {
  2628. assertex(expr->getOperator() == no_inlinetable || expr->getOperator() == no_dataset_from_transform);
  2629. //Always reduce things that create a new record so they only project the fields they need to
  2630. if (complexExtra->outputChanged())
  2631. {
  2632. HqlExprArray args;
  2633. switch (expr->getOperator())
  2634. {
  2635. case no_inlinetable:
  2636. {
  2637. IHqlExpression * transforms = expr->queryChild(0);
  2638. HqlExprArray newTransforms;
  2639. ForEachChild(i, transforms)
  2640. {
  2641. IHqlExpression * transform = transforms->queryChild(i);
  2642. newTransforms.append(*complexExtra->outputFields.createFilteredTransform(transform, NULL));
  2643. }
  2644. args.append(*transforms->clone(newTransforms));
  2645. break;
  2646. }
  2647. case no_dataset_from_transform:
  2648. {
  2649. IHqlExpression * transform = expr->queryChild(1);
  2650. args.append(*LINK(expr->queryChild(0)));
  2651. args.append(*complexExtra->outputFields.createFilteredTransform(transform, NULL));
  2652. break;
  2653. }
  2654. }
  2655. args.append(*LINK(complexExtra->queryOutputRecord()));
  2656. unwindChildren(args, expr, 2);
  2657. transformed.setown(expr->clone(args));
  2658. logChange("Minimize", expr, complexExtra->outputFields);
  2659. }
  2660. else
  2661. {
  2662. transformed.setown(createParentTransformed(expr));
  2663. //MORE: Need to replace left/right with their transformed varieties because the record may have changed format
  2664. transformed.setown(updateSelectors(transformed, expr));
  2665. }
  2666. break;
  2667. }
  2668. case CreateNonEmptyRecordSourceActivity:
  2669. {
  2670. assertex(expr->getOperator() == no_call);
  2671. //Always reduce things that create a new record so they only project the fields they need to
  2672. if (complexExtra->outputChanged())
  2673. {
  2674. HqlExprArray args;
  2675. IHqlExpression * funcdef = expr->queryBody()->queryFunctionDefinition();
  2676. assertex(funcdef);
  2677. IHqlExpression * body = funcdef->queryChild(0);
  2678. assertex(body);
  2679. if ((funcdef->getOperator() == no_funcdef) && (body->getOperator() == no_outofline))
  2680. {
  2681. IHqlExpression * bodycode = body->queryChild(0);
  2682. if (bodycode->getOperator() == no_embedbody)
  2683. {
  2684. OwnedHqlExpr newBodyCode = replaceChild(bodycode, 1, complexExtra->queryOutputRecord());
  2685. OwnedHqlExpr newBody = replaceChild(body, 0, newBodyCode);
  2686. OwnedHqlExpr newFuncdef = replaceChild(funcdef, 0, newBody);
  2687. unwindChildren(args, expr, 0);
  2688. transformed.setown(createBoundFunction(NULL, newFuncdef, args, NULL, false));
  2689. logChange("Auto project embed", expr, complexExtra->outputFields);
  2690. return transformed.getClear();
  2691. }
  2692. }
  2693. throwUnexpected();
  2694. break;
  2695. }
  2696. else
  2697. {
  2698. transformed.setown(createParentTransformed(expr));
  2699. //MORE: Need to replace left/right with their transformed varieties because the record may have changed format
  2700. transformed.setown(updateSelectors(transformed, expr));
  2701. }
  2702. break;
  2703. }
  2704. case CompoundActivity:
  2705. {
  2706. transformed.setown(createParentTransformed(expr));
  2707. if (complexExtra->outputChanged())
  2708. {
  2709. HqlExprArray args;
  2710. args.append(*complexExtra->createOutputProject(transformed->queryChild(0)));
  2711. transformed.setown(transformed->clone(args));
  2712. logChange("Project output from compound", expr, complexExtra->outputFields);
  2713. break;
  2714. }
  2715. }
  2716. case CompoundableActivity:
  2717. {
  2718. transformed.setown(createParentTransformed(expr));
  2719. //insert a project after the record.
  2720. if (complexExtra->outputChanged())
  2721. {
  2722. transformed.setown(complexExtra->createOutputProject(transformed));
  2723. transformed.setown(createWrapper(queryCompoundOp(expr), transformed.getClear()));
  2724. logChange("Project output from", expr, complexExtra->outputFields);
  2725. }
  2726. break;
  2727. }
  2728. case AnyTypeActivity:
  2729. {
  2730. transformed.setown(createParentTransformed(expr));
  2731. //insert a project after the record.
  2732. if (complexExtra->outputChanged())
  2733. {
  2734. logChange("Change format of dataset", expr, complexExtra->outputFields);
  2735. HqlExprArray args;
  2736. args.append(*LINK(complexExtra->queryOutputRecord()));
  2737. unwindChildren(args, transformed, 1);
  2738. transformed.setown(transformed->clone(args));
  2739. }
  2740. break;
  2741. }
  2742. case FixedInputActivity:
  2743. case SourceActivity:
  2744. case NonActivity:
  2745. case ScalarSelectActivity:
  2746. case SinkActivity:
  2747. transformed.setown(createParentTransformed(expr));
  2748. //can't change...
  2749. break;
  2750. case PassThroughActivity:
  2751. if (complexExtra->outputChanged())
  2752. {
  2753. HqlExprArray args;
  2754. ForEachChild(i, expr)
  2755. {
  2756. IHqlExpression * cur = expr->queryChild(i);
  2757. OwnedHqlExpr next = transform(cur);
  2758. if (cur->isDataset() || cur->isDatarow())
  2759. {
  2760. //Ensure all inputs have same format..
  2761. if (next->queryRecord() != complexExtra->queryOutputRecord())
  2762. next.setown(complexExtra->createOutputProject(next));
  2763. }
  2764. args.append(*next.getClear());
  2765. }
  2766. transformed.setown(expr->clone(args));
  2767. transformed.setown(updateSelectors(transformed, expr));
  2768. logChange("Passthrough modified", expr, complexExtra->outputFields);
  2769. }
  2770. else
  2771. transformed.setown(createParentTransformed(expr));
  2772. break;
  2773. case SimpleActivity:
  2774. {
  2775. transformed.setown(createParentTransformed(expr));
  2776. IHqlExpression * onFail = transformed->queryAttribute(onFailAtom);
  2777. if (onFail)
  2778. {
  2779. IHqlExpression * newTransform = complexExtra->outputFields.createFilteredTransform(onFail->queryChild(0), NULL);
  2780. IHqlExpression * newOnFail = createExprAttribute(onFailAtom, newTransform);
  2781. transformed.setown(replaceOwnedAttribute(transformed, newOnFail));
  2782. }
  2783. if (complexExtra->insertProject)
  2784. {
  2785. HqlExprArray args;
  2786. OwnedHqlExpr inputProject = complexExtra->createOutputProject(transformed->queryChild(0));
  2787. OwnedHqlExpr replacement = replaceChildDataset(transformed, inputProject, 0);
  2788. transformed.setown(updateSelectors(replacement, expr));
  2789. logChange("Insert project before", expr, complexExtra->outputFields);
  2790. }
  2791. else
  2792. transformed.setown(updateSelectors(transformed, expr));
  2793. break;
  2794. }
  2795. default:
  2796. throwUnexpected();
  2797. }
  2798. return transformed.getClear();
  2799. }
  2800. ANewTransformInfo * ImplicitProjectTransformer::createTransformInfo(IHqlExpression * expr)
  2801. {
  2802. ProjectExprKind kind = getProjectExprKind(expr);
  2803. node_operator op = expr->getOperator();
  2804. if (kind == NonActivity)
  2805. {
  2806. switch (op)
  2807. {
  2808. case no_record:
  2809. case no_rowset:
  2810. case no_rowsetrange:
  2811. case no_datasetlist:
  2812. break;
  2813. default:
  2814. return CREATE_NEWTRANSFORMINFO2(ImplicitProjectInfo, expr, kind);
  2815. }
  2816. }
  2817. if (kind == ComplexNonActivity)
  2818. kind = NonActivity;
  2819. return CREATE_NEWTRANSFORMINFO2(ComplexImplicitProjectInfo, expr, kind);
  2820. }
  2821. void ImplicitProjectTransformer::finalizeFields()
  2822. {
  2823. ForEachItemIn(i, activities)
  2824. finalizeFields(&activities.item(i));
  2825. }
  2826. static bool requiresFewerFields(const UsedFieldSet & fields, ComplexImplicitProjectInfo & input)
  2827. {
  2828. return fields.requiresFewerFields(input.outputFields);
  2829. }
  2830. void ImplicitProjectTransformer::finalizeFields(IHqlExpression * expr)
  2831. {
  2832. ComplexImplicitProjectInfo * extra = queryBodyComplexExtra(expr);
  2833. if (!extra->okToOptimize())
  2834. return;
  2835. switch (extra->activityKind())
  2836. {
  2837. case CreateRecordActivity:
  2838. case CreateRecordLRActivity:
  2839. case CompoundActivity:
  2840. case CompoundableActivity:
  2841. case CreateRecordSourceActivity:
  2842. case CreateNonEmptyRecordSourceActivity:
  2843. case AnyTypeActivity:
  2844. extra->finalizeOutputRecord(extra->activityKind() == CreateNonEmptyRecordSourceActivity);
  2845. break;
  2846. case DenormalizeActivity:
  2847. case RollupTransformActivity:
  2848. case IterateTransformActivity:
  2849. {
  2850. //output must always match the input..., but any fields that are in the input, but not needed in the output we'll add as exceptions
  2851. //and assign default values to them, otherwise it can cause other fields to be required in the input + causes chaos
  2852. extra->fieldsToBlank.createDifference(extra->inputs.item(0).outputFields, extra->outputFields);
  2853. extra->outputFields.unionFields(extra->fieldsToBlank);
  2854. extra->fieldsToBlank.optimizeFieldsToBlank(extra->outputFields, queryNewColumnProvider(expr));
  2855. if (!extra->fieldsToBlank.isEmpty())
  2856. {
  2857. const char * opString = getOpString(expr->getOperator());
  2858. StringBuffer fieldText;
  2859. extra->fieldsToBlank.getText(fieldText);
  2860. DBGLOG("ImplicitProject: Fields %s for %s not required by outputs - so blank in transform", fieldText.str(), opString);
  2861. }
  2862. extra->finalizeOutputRecord(false);
  2863. break;
  2864. }
  2865. case FixedInputActivity:
  2866. case SourceActivity:
  2867. case ScalarSelectActivity:
  2868. break;
  2869. case PassThroughActivity:
  2870. {
  2871. //Branches coming into this IF/MERGE etc. may have different fields (e.g., because of ITERATEs), and
  2872. //the output fields may be smaller (e.g., no merge sort conditions, no fields used and inputs filter)
  2873. //So use the intersection of the inputfields as the output record. 90% of the time they will be
  2874. //the same so no projects will be introduced.
  2875. bool anyProjected = false;
  2876. unsigned numInputs = extra->inputs.ordinality();
  2877. for (unsigned i=0; i != numInputs; i++)
  2878. {
  2879. ComplexImplicitProjectInfo & cur = extra->inputs.item(i);
  2880. if (!cur.outputFields.includeAll())
  2881. {
  2882. extra->outputFields.set(cur.outputFields);
  2883. for (unsigned i2=i+1; i2 != numInputs; i2++)
  2884. {
  2885. ComplexImplicitProjectInfo & cur = extra->inputs.item(i2);
  2886. extra->outputFields.intersectFields(cur.outputFields);
  2887. }
  2888. extra->finalizeOutputRecord(false);
  2889. anyProjected = true;
  2890. break;
  2891. }
  2892. }
  2893. if (!anyProjected)
  2894. extra->setMatchingOutput(&extra->inputs.item(0));
  2895. break;
  2896. }
  2897. case SinkActivity:
  2898. break;
  2899. case SimpleActivity:
  2900. if (extra->insertProject && requiresFewerFields(extra->leftFieldsRequired, extra->inputs.item(0)))
  2901. {
  2902. extra->outputFields.set(extra->leftFieldsRequired);
  2903. extra->finalizeOutputRecord(false);
  2904. }
  2905. else
  2906. extra->setMatchingOutput(&extra->inputs.item(0));
  2907. break;
  2908. default:
  2909. throwUnexpected();
  2910. }
  2911. }
  2912. void ImplicitProjectTransformer::inheritActiveFields(IHqlExpression * expr, ImplicitProjectInfo * extra, unsigned min, unsigned max)
  2913. {
  2914. for (unsigned i = min; i < max; i++)
  2915. inheritActiveFields(extra, expr->queryChild(i));
  2916. }
  2917. void ImplicitProjectTransformer::inheritActiveFields(ImplicitProjectInfo * target, IHqlExpression * source)
  2918. {
  2919. if (source->queryBody()->queryTransformExtra())
  2920. {
  2921. target->addActiveSelects(querySelectsUsed(source));
  2922. }
  2923. }
  2924. void ImplicitProjectTransformer::insertProjects()
  2925. {
  2926. ForEachItemIn(i, activities)
  2927. insertProjects(&activities.item(i));
  2928. }
  2929. void ImplicitProjectTransformer::insertProjects(IHqlExpression * expr)
  2930. {
  2931. ComplexImplicitProjectInfo * extra = queryBodyComplexExtra(expr);
  2932. if (!extra->okToOptimize())
  2933. return;
  2934. if (options.optimizeSpills && (expr->getOperator() == no_commonspill))
  2935. {
  2936. if (requiresFewerFields(extra->leftFieldsRequired, extra->inputs.item(0)))
  2937. extra->insertProject = true;
  2938. return;
  2939. }
  2940. if (options.insertProjectCostLevel == 0)
  2941. return;
  2942. if (extra->queryCostFactor(targetClusterType) < options.insertProjectCostLevel)
  2943. return;
  2944. switch (extra->activityKind())
  2945. {
  2946. case SimpleActivity:
  2947. if (requiresFewerFields(extra->leftFieldsRequired, extra->inputs.item(0)))
  2948. extra->insertProject = true;
  2949. break;
  2950. }
  2951. }
  2952. void ImplicitProjectTransformer::percolateFields()
  2953. {
  2954. ForEachItemInRev(i, activities)
  2955. calculateFieldsUsed(&activities.item(i));
  2956. }
  2957. IHqlExpression * ImplicitProjectTransformer::process(IHqlExpression * expr)
  2958. {
  2959. cycle_t time1 = msTick();
  2960. analyse(expr, 0); // gather a list of activities, and link them together.
  2961. cycle_t time2 = msTick();
  2962. //DEBUG_TIMERX(translator.queryTimeReporter(), "EclServer: implicit.analyse", time2-time1);
  2963. percolateFields();
  2964. cycle_t time3 = msTick();
  2965. //DEBUG_TIMERX(translator.queryTimeReporter(), "EclServer: implicit.percolate", time3-time2);
  2966. switch (targetClusterType)
  2967. {
  2968. case RoxieCluster:
  2969. //worth inserting projects after sources that can be compound.
  2970. //also may be worth projecting before iterating since an iterate
  2971. //copies data but can't change the fields in use
  2972. break;
  2973. case HThorCluster:
  2974. // same as roxie, but also maybe worth inserting projects to minimise the amount of data that is spilled.
  2975. break;
  2976. case ThorLCRCluster:
  2977. //worth inserting projects to reduce copying, spilling, but primarily data transferred between nodes.
  2978. if (options.insertProjectCostLevel || options.optimizeSpills)
  2979. insertProjects();
  2980. break;
  2981. }
  2982. cycle_t time4 = msTick();
  2983. //DEBUG_TIMERX(translator.queryTimeReporter(), "EclServer: implicit.reduceData", time4-time3);
  2984. finalizeFields();
  2985. cycle_t time5 = msTick();
  2986. //DEBUG_TIMERX(translator.queryTimeReporter(), "EclServer: implicit.finalize", time5-time4);
  2987. //traceActivities();
  2988. OwnedHqlExpr ret = transformRoot(expr);
  2989. cycle_t time6 = msTick();
  2990. //DEBUG_TIMERX(translator.queryTimeReporter(), "EclServer: implicit.transform", time6-time5);
  2991. return ret.getClear();
  2992. }
  2993. void ImplicitProjectTransformer::traceActivities()
  2994. {
  2995. ForEachItemIn(i, activities)
  2996. queryBodyComplexExtra(&activities.item(i))->trace();
  2997. }
  2998. IHqlExpression * ImplicitProjectTransformer::updateSelectors(IHqlExpression * newExpr, IHqlExpression * oldExpr)
  2999. {
  3000. //MORE: Clean me up using new flags when they are merged
  3001. IHqlExpression * newDs = newExpr->queryChild(0);
  3002. IHqlExpression * oldDs = oldExpr->queryChild(0);
  3003. switch (getChildDatasetType(newExpr))
  3004. {
  3005. case childdataset_none:
  3006. case childdataset_many_noscope:
  3007. case childdataset_if:
  3008. case childdataset_case:
  3009. case childdataset_map:
  3010. case childdataset_dataset_noscope:
  3011. return LINK(newExpr);
  3012. //None of these have any scoped arguments, so no need to remove them
  3013. break;
  3014. case childdataset_many:
  3015. //The selectors listed in the sorted list may need updating if nested records have changed
  3016. return updateActiveSelectorFields(newExpr, oldExpr->queryRecord(), newExpr->queryRecord(), getNumChildTables(newExpr));
  3017. case childdataset_dataset:
  3018. {
  3019. return updateMappedFields(newExpr, oldDs->queryNormalizedSelector(), newDs->queryNormalizedSelector(), 1);
  3020. }
  3021. case childdataset_datasetleft:
  3022. {
  3023. OwnedHqlExpr mapped = updateMappedFields(newExpr, oldDs->queryNormalizedSelector(), newDs->queryNormalizedSelector(), 1);
  3024. IHqlExpression * selSeq = querySelSeq(newExpr);
  3025. assertex(selSeq == querySelSeq(oldExpr));
  3026. OwnedHqlExpr newLeft = createSelector(no_left, newDs, selSeq);
  3027. OwnedHqlExpr oldLeft = createSelector(no_left, oldDs, selSeq);
  3028. return updateChildSelectors(mapped, oldLeft, newLeft, 1);
  3029. }
  3030. case childdataset_left:
  3031. {
  3032. IHqlExpression * selSeq = querySelSeq(newExpr);
  3033. assertex(selSeq == querySelSeq(oldExpr));
  3034. OwnedHqlExpr newLeft = createSelector(no_left, newDs, selSeq);
  3035. OwnedHqlExpr oldLeft = createSelector(no_left, oldDs, selSeq);
  3036. return updateChildSelectors(newExpr, oldLeft, newLeft, 1);
  3037. }
  3038. case childdataset_same_left_right:
  3039. case childdataset_top_left_right:
  3040. case childdataset_nway_left_right:
  3041. {
  3042. OwnedHqlExpr mapped = updateMappedFields(newExpr, oldDs->queryNormalizedSelector(), newDs->queryNormalizedSelector(), 1);
  3043. IHqlExpression * selSeq = querySelSeq(newExpr);
  3044. assertex(selSeq == querySelSeq(oldExpr));
  3045. OwnedHqlExpr newLeft = createSelector(no_left, newExpr->queryChild(0), selSeq);
  3046. OwnedHqlExpr oldLeft = createSelector(no_left, oldExpr->queryChild(0), selSeq);
  3047. OwnedHqlExpr ds1 = updateChildSelectors(mapped, oldLeft, newLeft, 1);
  3048. OwnedHqlExpr newRight = createSelector(no_right, newExpr->queryChild(0), selSeq);
  3049. OwnedHqlExpr oldRight = createSelector(no_right, oldExpr->queryChild(0), selSeq);
  3050. return updateChildSelectors(ds1, oldRight, newRight, 1);
  3051. }
  3052. case childdataset_leftright:
  3053. {
  3054. IHqlExpression * selSeq = querySelSeq(newExpr);
  3055. assertex(selSeq == querySelSeq(oldExpr));
  3056. OwnedHqlExpr newLeft = createSelector(no_left, newExpr->queryChild(0), selSeq);
  3057. OwnedHqlExpr oldLeft = createSelector(no_left, oldExpr->queryChild(0), selSeq);
  3058. unsigned firstLeft = (newExpr->getOperator() == no_normalize) ? 1 : 2;
  3059. OwnedHqlExpr ds1 = updateChildSelectors(newExpr, oldLeft, newLeft, firstLeft);
  3060. OwnedHqlExpr newRight = createSelector(no_right, newExpr->queryChild(1), selSeq);
  3061. OwnedHqlExpr oldRight = createSelector(no_right, oldExpr->queryChild(1), selSeq);
  3062. return updateChildSelectors(ds1, oldRight, newRight, 2);
  3063. }
  3064. break;
  3065. default:
  3066. throwUnexpected();
  3067. }
  3068. }
  3069. const SelectUsedArray & ImplicitProjectTransformer::querySelectsUsedForField(IHqlExpression * transform, IHqlExpression * field)
  3070. {
  3071. IHqlExpression * transformValues = queryTransformAssignValue(transform, field);
  3072. assertex(transformValues);
  3073. return querySelectsUsed(transformValues);
  3074. }
  3075. #include "hqlttcpp.ipp"
  3076. IHqlExpression * insertImplicitProjects(HqlCppTranslator & translator, IHqlExpression * expr, bool optimizeSpills)
  3077. {
  3078. #if defined(POST_COMMON_ANNOTATION)
  3079. HqlExprArray ret;
  3080. {
  3081. ImplicitProjectTransformer transformer(translator, optimizeSpills);
  3082. ret.append(*transformer.process(expr));
  3083. }
  3084. normalizeAnnotations(translator, ret);
  3085. return createActionList(ret);
  3086. #else
  3087. ImplicitProjectTransformer transformer(translator, optimizeSpills);
  3088. return transformer.process(expr);
  3089. #endif
  3090. }
  3091. void insertImplicitProjects(HqlCppTranslator & translator, HqlExprArray & exprs)
  3092. {
  3093. if (exprs.ordinality())
  3094. {
  3095. OwnedHqlExpr compound = createActionList(exprs);
  3096. OwnedHqlExpr ret = insertImplicitProjects(translator, compound, false);
  3097. exprs.kill();
  3098. ret->unwindList(exprs, no_actionlist);
  3099. }
  3100. }
  3101. /*
  3102. To Implement field gathering would need to do the following:
  3103. - Could assert that only non-nested fields are considered. That means all field references are in the form (ds.field).
  3104. This would simplify gathering, filtering, and translating the dataset selector....
  3105. - All no_selects on in-scope datasets (or sub fields?) get added to list of active fields in self.
  3106. - analyseExpr() clones all active fields from children into self.
  3107. - active datasets used in a dataset context need to also be added.
  3108. - any item inherits all child fields from non-dataset children, and removes all references to parent datasets (in what ever form)
  3109. very similar to the inScope Table processing. [Only really needed for non-global activities]
  3110. - any activity inherits all inScope fields from its parent.
  3111. - those expressions with scoped dataset inputs need two lists (childrensFieldAccess and inscopeFields)
  3112. - may be worth having different classes for handling the different categories:
  3113. (simpleExpression, scopedExpression, activity) with virtuals to handle differences
  3114. - removingChildReferences
  3115. would need matchesSelector(list, selector) which worked recursively.
  3116. */