hqlckey.cpp 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jliball.hpp"
  14. #include "hql.hpp"
  15. #include "platform.h"
  16. #include "jlib.hpp"
  17. #include "jmisc.hpp"
  18. #include "jstream.ipp"
  19. #include "jdebug.hpp"
  20. #include "hql.hpp"
  21. #include "hqlthql.hpp"
  22. #include "hqlhtcpp.ipp"
  23. #include "hqlttcpp.ipp"
  24. #include "hqlutil.hpp"
  25. #include "hqlthql.hpp"
  26. #include "hqlpmap.hpp"
  27. #include "hqlwcpp.hpp"
  28. #include "hqlcpputil.hpp"
  29. #include "hqltcppc.ipp"
  30. #include "hqlopt.hpp"
  31. #include "hqlfold.hpp"
  32. #include "hqlcerrors.hpp"
  33. #include "hqlcatom.hpp"
  34. #include "hqlresource.hpp"
  35. #include "hqlregex.ipp"
  36. #include "hqlsource.ipp"
  37. #include "hqlcse.ipp"
  38. #include "eclhelper.hpp"
  39. //--------------------------------------------------------------------------------------------------
  40. IHqlExpression * getHozedBias(ITypeInfo * type)
  41. {
  42. unsigned __int64 bias = ((unsigned __int64)1 << (type->getSize()*8-1));
  43. return createConstant(type->castFrom(false, bias));
  44. }
  45. bool requiresHozedTransform(ITypeInfo * type)
  46. {
  47. type = type->queryPromotedType();
  48. switch (type->getTypeCode())
  49. {
  50. case type_boolean:
  51. case type_data:
  52. case type_qstring:
  53. return false;
  54. case type_littleendianint:
  55. return type->isSigned() || type->getSize() != 1;
  56. case type_bigendianint:
  57. return type->isSigned();
  58. case type_string:
  59. case type_varstring:
  60. return (type->queryCharset()->queryName() != asciiAtom);
  61. case type_decimal:
  62. return type->isSigned();
  63. default:
  64. //anything else is a payload field, don't do any transformations...
  65. return false;
  66. }
  67. }
  68. bool requiresHozedTransform(IHqlExpression * value, ITypeInfo * keyFieldType)
  69. {
  70. ITypeInfo * type = value->queryType();
  71. if (type != keyFieldType)
  72. return true;
  73. return requiresHozedTransform(type);
  74. }
  75. bool isKeyableType(ITypeInfo * type)
  76. {
  77. switch (type->getTypeCode())
  78. {
  79. case type_boolean:
  80. case type_swapint:
  81. case type_int:
  82. case type_decimal:
  83. return true;
  84. case type_string:
  85. case type_varstring:
  86. case type_qstring:
  87. case type_data:
  88. case type_unicode:
  89. case type_varunicode:
  90. case type_utf8:
  91. return (type->getSize() != UNKNOWN_LENGTH);
  92. default:
  93. return false;
  94. }
  95. }
  96. IHqlExpression * getHozedKeyValue(IHqlExpression * _value)
  97. {
  98. HqlExprAttr value = _value;
  99. Linked<ITypeInfo> type = _value->queryType()->queryPromotedType();
  100. type_t tc = type->getTypeCode();
  101. switch (tc)
  102. {
  103. case type_boolean:
  104. case type_data:
  105. case type_qstring:
  106. break;
  107. case type_int:
  108. case type_swapint:
  109. if (type->isSigned())
  110. {
  111. type.setown(makeIntType(type->getSize(), false));
  112. value.setown(ensureExprType(value, type));
  113. value.setown(createValue(no_add, LINK(type), LINK(value), getHozedBias(type)));
  114. }
  115. if ((type->getTypeCode() == type_littleendianint) && (type->getSize() != 1))
  116. type.setown(makeSwapIntType(type->getSize(), false));
  117. break;
  118. case type_string:
  119. if (type->queryCharset()->queryName() != asciiAtom)
  120. type.setown(makeStringType(type->getSize(), NULL, NULL));
  121. break;
  122. case type_varstring:
  123. if (type->queryCharset()->queryName() != asciiAtom)
  124. type.setown(makeVarStringType(type->getStringLen(), NULL, NULL));
  125. break;
  126. case type_decimal:
  127. if (!type->isSigned())
  128. break;
  129. //fallthrough
  130. default:
  131. //anything else is a payload field, don't do any transformations...
  132. break;
  133. }
  134. return ensureExprType(value, type);
  135. }
  136. IHqlExpression * convertIndexPhysical2LogicalValue(IHqlExpression * cur, IHqlExpression * physicalSelect, bool allowTranslate)
  137. {
  138. if (cur->hasAttribute(blobAtom))
  139. {
  140. if (cur->isDataset())
  141. return createDataset(no_id2blob, LINK(physicalSelect), LINK(cur->queryRecord()));
  142. else if (cur->isDatarow())
  143. return createRow(no_id2blob, LINK(physicalSelect), LINK(cur->queryRecord()));
  144. else
  145. return createValue(no_id2blob, cur->getType(), LINK(physicalSelect));
  146. }
  147. else if (allowTranslate)
  148. {
  149. LinkedHqlExpr newValue = physicalSelect;
  150. OwnedHqlExpr target = createSelectExpr(getActiveTableSelector(), LINK(cur)); // select not used, just created to get correct types.
  151. ITypeInfo * type = target->queryType();
  152. type_t tc = type->getTypeCode();
  153. if (tc == type_int || tc == type_swapint)
  154. {
  155. if (type->isSigned())
  156. {
  157. Owned<ITypeInfo> tempType = makeIntType(type->getSize(), false);
  158. newValue.setown(ensureExprType(newValue, tempType));
  159. newValue.setown(createValue(no_sub, newValue->getType(), LINK(newValue), getHozedBias(newValue->queryType())));
  160. }
  161. }
  162. return ensureExprType(newValue, type);
  163. }
  164. else
  165. return LINK(physicalSelect);
  166. }
  167. //--------------------------------------------------------------------------------------------------
  168. void HqlCppTranslator::buildJoinMatchFunction(BuildCtx & ctx, const char * name, IHqlExpression * left, IHqlExpression * right, IHqlExpression * match, IHqlExpression * selSeq)
  169. {
  170. if (match)
  171. {
  172. StringBuffer s;
  173. BuildCtx matchctx(ctx);
  174. matchctx.addQuotedCompound(s.append("virtual bool ").append(name).append("(const void * _left, const void * _right)"));
  175. matchctx.addQuotedLiteral("const unsigned char * left = (const unsigned char *) _left;");
  176. matchctx.addQuotedLiteral("const unsigned char * right = (const unsigned char *) _right;");
  177. bindTableCursor(matchctx, left, "left", no_left, selSeq);
  178. bindTableCursor(matchctx, right, "right", no_right, selSeq);
  179. OwnedHqlExpr cseMatch = options.spotCSE ? spotScalarCSE(match, NULL, queryOptions().spotCseInIfDatasetConditions) : LINK(match);
  180. buildReturn(matchctx, cseMatch);
  181. }
  182. }
  183. //--------------------------------------------------------------------------------------------------
  184. class KeyedJoinInfo : public CInterface
  185. {
  186. public:
  187. KeyedJoinInfo(HqlCppTranslator & _translator, IHqlExpression * _expr, bool _canOptimizeTransfer);
  188. ~KeyedJoinInfo();
  189. void buildClearRightFunction(BuildCtx & classctx);
  190. void buildExtractFetchFields(BuildCtx & ctx);
  191. void buildExtractIndexReadFields(BuildCtx & ctx);
  192. void buildExtractJoinFields(ActivityInstance & instance);
  193. void buildFailureTransform(BuildCtx & ctx, IHqlExpression * transform);
  194. void buildFetchMatch(BuildCtx & ctx);
  195. void buildIndexReadMatch(BuildCtx & ctx);
  196. void buildLeftOnly(BuildCtx & ctx);
  197. void buildMonitors(BuildCtx & ctx);
  198. void buildTransform(BuildCtx & ctx);
  199. IHqlExpression * getMatchExpr(bool isKeyFilter) { return isKeyFilter ? LINK(monitors->queryExtraFilter()) : LINK(fileFilter); }
  200. bool isFetchFiltered() { return fileFilter != NULL; }
  201. bool isFullJoin() { return file != NULL; }
  202. bool isHalfJoin() { return !file; }
  203. bool processFilter();
  204. IHqlExpression * queryKey() { return key; }
  205. IHqlExpression * queryOriginalKey() { return originalKey; }
  206. IHqlExpression * queryKeyFilename() { return hasComplexIndex ? NULL : key->queryChild(3); }
  207. IHqlExpression * queryFile() { return file; }
  208. IHqlExpression * queryFileFilename() { return file->queryChild(0); }
  209. IHqlExpression * queryRawKey() { return rawKey; }
  210. IHqlExpression * queryRawRhs() { return rawRhs; }
  211. bool isKeyOpt() { return key->hasAttribute(optAtom); }
  212. bool isFileOpt() { return file && file->hasAttribute(optAtom); }
  213. bool needToExtractJoinFields() const { return extractJoinFieldsTransform != NULL; }
  214. bool hasPostFilter() const { return monitors->queryExtraFilter() || fileFilter; }
  215. bool requireActivityForKey() const { return hasComplexIndex; }
  216. void reportFailureReason(IHqlExpression * cond) { monitors->reportFailureReason(cond); }
  217. protected:
  218. void buildClearRecord(BuildCtx & ctx, RecordSelectIterator & rawIter, RecordSelectIterator & keyIter);
  219. void buildTransformBody(BuildCtx & ctx, IHqlExpression * transform);
  220. IHqlExpression * createKeyFromComplexKey(IHqlExpression * expr);
  221. IHqlExpression * expandDatasetReferences(IHqlExpression * expr, IHqlExpression * ds);
  222. IHqlExpression * optimizeTransfer(HqlExprArray & fields, HqlExprArray & values, IHqlExpression * expr, IHqlExpression * leftSelector);
  223. void optimizeExtractJoinFields();
  224. void optimizeTransfer(SharedHqlExpr & targetDataset, SharedHqlExpr & targetTransform, SharedHqlExpr & keyedFilter, OwnedHqlExpr * extraFilter);
  225. IHqlExpression * querySimplifiedKey(IHqlExpression * expr);
  226. void splitFilter(IHqlExpression * filter, SharedHqlExpr & keyTarget);
  227. protected:
  228. HqlCppTranslator & translator;
  229. HqlExprAttr expr;
  230. HqlExprAttr originalKey; // even if computed/parameter
  231. HqlExprAttr key;
  232. HqlExprAttr rawKey;
  233. HqlExprAttr expandedKey;
  234. HqlExprAttr file;
  235. HqlExprAttr expandedFile;
  236. HqlExprAttr rawFile;
  237. HqlExprAttr keyAccessDataset;
  238. HqlExprAttr keyAccessTransform;
  239. HqlExprAttr fileAccessDataset;
  240. HqlExprAttr fileAccessTransform;
  241. HqlExprAttr joinSeq;
  242. MonitorExtractor * monitors;
  243. HqlExprAttr fileFilter;
  244. HqlExprAttr leftOnlyMatch;
  245. HqlExprAttr rawRhs;
  246. TableProjectMapper keyedMapper;
  247. OwnedHqlExpr counter;
  248. OwnedHqlExpr extractJoinFieldsRecord;
  249. OwnedHqlExpr extractJoinFieldsTransform;
  250. bool canOptimizeTransfer;
  251. bool hasComplexIndex;
  252. bool keyHasFileposition;
  253. };
  254. KeyedJoinInfo::KeyedJoinInfo(HqlCppTranslator & _translator, IHqlExpression * _expr, bool _canOptimizeTransfer) : translator(_translator)
  255. {
  256. expr.set(_expr);
  257. joinSeq.set(querySelSeq(expr));
  258. hasComplexIndex = false;
  259. IHqlExpression * right = expr->queryChild(1);
  260. IHqlExpression * keyed = expr->queryAttribute(keyedAtom);
  261. if (keyed && keyed->queryChild(0))
  262. {
  263. key.set(keyed->queryChild(0));
  264. if (right->getOperator() == no_keyed)
  265. right = right->queryChild(0);
  266. assertex(getBoolAttribute(right, filepositionAtom, true));
  267. file.set(right);
  268. IHqlExpression * rightTable = queryPhysicalRootTable(right);
  269. if (!rightTable || rightTable->queryNormalizedSelector() != right->queryNormalizedSelector())
  270. translator.throwError(HQLERR_FullKeyedNeedsFile);
  271. expandedFile.setown(convertToPhysicalTable(rightTable, true));
  272. rawFile.set(queryPhysicalRootTable(expandedFile));
  273. keyedMapper.setDataset(key);
  274. }
  275. else if (right->getOperator() == no_newkeyindex)
  276. {
  277. key.set(right);
  278. }
  279. else
  280. {
  281. originalKey.set(right);
  282. //We could call key.set(querySimplifiedKey(right)) to succeed in some cases instead of generating an error.
  283. if (translator.getTargetClusterType() == RoxieCluster)
  284. {
  285. hasComplexIndex = true;
  286. key.setown(createKeyFromComplexKey(right));
  287. }
  288. else
  289. translator.throwError1(HQLERR_KeyedJoinNoRightIndex_X, getOpString(right->getOperator()));
  290. }
  291. keyHasFileposition = getBoolAttribute(key, filepositionAtom, true);
  292. if (!originalKey)
  293. originalKey.set(key);
  294. expandedKey.setown(translator.convertToPhysicalIndex(key));
  295. rawKey.set(queryPhysicalRootTable(expandedKey));
  296. canOptimizeTransfer = _canOptimizeTransfer;
  297. monitors = NULL;
  298. counter.set(queryAttributeChild(expr, _countProject_Atom, 0));
  299. if (isFullJoin())
  300. rawRhs.set(rawFile);
  301. else
  302. rawRhs.set(rawKey);
  303. }
  304. KeyedJoinInfo::~KeyedJoinInfo()
  305. {
  306. delete monitors;
  307. }
  308. IHqlExpression * KeyedJoinInfo::querySimplifiedKey(IHqlExpression * expr)
  309. {
  310. loop
  311. {
  312. switch (expr->getOperator())
  313. {
  314. case no_sorted:
  315. case no_distributed:
  316. case no_sort:
  317. case no_distribute:
  318. case no_preservemeta:
  319. case no_assertsorted:
  320. case no_assertgrouped:
  321. case no_assertdistributed:
  322. case no_nofold:
  323. break;
  324. case no_newkeyindex:
  325. return LINK(expr);
  326. default:
  327. return NULL;
  328. }
  329. expr = expr->queryChild(0);
  330. }
  331. }
  332. IHqlExpression * KeyedJoinInfo::createKeyFromComplexKey(IHqlExpression * expr)
  333. {
  334. IHqlExpression * base = queryPhysicalRootTable(expr);
  335. if (!base)
  336. {
  337. translator.throwError1(HQLERR_KeyedJoinNoRightIndex_X, getOpString(expr->getOperator()));
  338. return NULL;
  339. }
  340. if (base->getOperator() == no_newkeyindex)
  341. return LINK(base);
  342. translator.throwError1(HQLERR_KeyedJoinNoRightIndex_X, getOpString(base->getOperator()));
  343. return NULL;
  344. }
  345. void KeyedJoinInfo::buildClearRecord(BuildCtx & ctx, RecordSelectIterator & rawIter, RecordSelectIterator & keyIter)
  346. {
  347. keyIter.first();
  348. ForEach(rawIter)
  349. {
  350. assert(keyIter.isValid());
  351. OwnedHqlExpr rawSelect = rawIter.get();
  352. OwnedHqlExpr keySelect = keyIter.get();
  353. OwnedHqlExpr null = createNullExpr(keySelect);
  354. OwnedHqlExpr keyNull = (rawIter.isInsideIfBlock() || (rawIter.isInsideNested() && isInPayload())) ? LINK(null) : getHozedKeyValue(null);
  355. OwnedHqlExpr folded = foldHqlExpression(keyNull);
  356. translator.buildAssign(ctx, rawSelect, folded);
  357. keyIter.next();
  358. }
  359. }
  360. void KeyedJoinInfo::buildClearRightFunction(BuildCtx & classctx)
  361. {
  362. if (extractJoinFieldsTransform || isFullJoin())
  363. {
  364. OwnedHqlExpr ds = createDataset(no_anon, LINK(extractJoinFieldsRecord));
  365. translator.buildClearRecordMember(classctx, "Right", ds);
  366. }
  367. else
  368. {
  369. //Need to initialize the record with the zero logical values, not zero key values
  370. //which differs for biased integers etc.
  371. BuildCtx funcctx(classctx);
  372. funcctx.addQuotedCompound("virtual size32_t createDefaultRight(ARowBuilder & crSelf)");
  373. translator.ensureRowAllocated(funcctx, "crSelf");
  374. BoundRow * selfCursor = translator.bindSelf(funcctx, rawKey, "crSelf");
  375. IHqlExpression * rawSelf = selfCursor->querySelector();
  376. RecordSelectIterator rawIter(rawKey->queryRecord(), rawSelf);
  377. RecordSelectIterator keyIter(key->queryRecord(), key);
  378. buildClearRecord(funcctx, rawIter, keyIter);
  379. translator.buildReturnRecordSize(funcctx, selfCursor);
  380. }
  381. }
  382. void KeyedJoinInfo::buildExtractFetchFields(BuildCtx & ctx)
  383. {
  384. // For the data going to the fetch remote activity:
  385. //virtual size32_t extractFetchFields(ARowBuilder & crSelf, const void * _left) = 0;
  386. if (fileAccessDataset)
  387. {
  388. BuildCtx ctx1(ctx);
  389. ctx1.addQuotedCompound("virtual size32_t extractFetchFields(ARowBuilder & crSelf, const void * _left)");
  390. translator.ensureRowAllocated(ctx1, "crSelf");
  391. if (fileAccessTransform)
  392. {
  393. translator.buildTransformBody(ctx1, fileAccessTransform, expr->queryChild(0), NULL, fileAccessDataset, joinSeq);
  394. }
  395. else
  396. {
  397. translator.buildRecordSerializeExtract(ctx1, fileAccessDataset->queryRecord());
  398. }
  399. }
  400. //virtual IOutputMetaData * queryFetchInputRecordSize() = 0;
  401. translator.buildMetaMember(ctx, fileAccessDataset, false, "queryFetchInputRecordSize");
  402. }
  403. void KeyedJoinInfo::buildExtractIndexReadFields(BuildCtx & ctx)
  404. {
  405. //virtual size32_t extractIndexReadFields(ARowBuilder & crSelf, const void * _left) = 0;
  406. BuildCtx ctx1(ctx);
  407. ctx1.addQuotedCompound("virtual size32_t extractIndexReadFields(ARowBuilder & crSelf, const void * _left)");
  408. translator.ensureRowAllocated(ctx1, "crSelf");
  409. if (keyAccessTransform)
  410. {
  411. translator.buildTransformBody(ctx1, keyAccessTransform, expr->queryChild(0), NULL, keyAccessDataset, joinSeq);
  412. }
  413. else
  414. {
  415. translator.buildRecordSerializeExtract(ctx1, keyAccessDataset->queryRecord());
  416. }
  417. //virtual IOutputMetaData * queryIndexReadInputRecordSize() = 0;
  418. translator.buildMetaMember(ctx, keyAccessDataset, isGrouped(keyAccessDataset), "queryIndexReadInputRecordSize"); //->false
  419. }
  420. void KeyedJoinInfo::buildExtractJoinFields(ActivityInstance & instance)
  421. {
  422. //virtual size32_t extractJoinFields(void *dest, const void *diskRow, IBlobProvider * blobs) = 0;
  423. BuildCtx extractctx(instance.startctx);
  424. extractctx.addQuotedCompound("virtual size32_t extractJoinFields(ARowBuilder & crSelf, const void *_left, unsigned __int64 _filepos, IBlobProvider * blobs)");
  425. translator.ensureRowAllocated(extractctx, "crSelf");
  426. if (needToExtractJoinFields())
  427. {
  428. OwnedHqlExpr extracted = createDataset(no_anon, LINK(extractJoinFieldsRecord));
  429. OwnedHqlExpr raw = createDataset(no_anon, LINK(rawRhs->queryRecord()));
  430. BoundRow * selfCursor = translator.buildTransformCursors(extractctx, extractJoinFieldsTransform, raw, NULL, extracted, joinSeq);
  431. if (isHalfJoin())
  432. {
  433. OwnedHqlExpr left = createSelector(no_left, raw, joinSeq);
  434. translator.associateBlobHelper(extractctx, left, "blobs");
  435. OwnedHqlExpr fileposExpr = getFilepos(left, false);
  436. OwnedHqlExpr fileposVar = createVariable("_filepos", fileposExpr->getType());
  437. extractctx.associateExpr(fileposExpr, fileposVar);
  438. }
  439. translator.doBuildTransformBody(extractctx, extractJoinFieldsTransform, selfCursor);
  440. }
  441. else
  442. {
  443. translator.buildRecordSerializeExtract(extractctx, extractJoinFieldsRecord);
  444. }
  445. //virtual IOutputMetaData * queryJoinFieldsRecordSize() = 0;
  446. translator.buildMetaMember(instance.classctx, extractJoinFieldsRecord, false, "queryJoinFieldsRecordSize");
  447. }
  448. void KeyedJoinInfo::buildFetchMatch(BuildCtx & ctx)
  449. {
  450. translator.buildJoinMatchFunction(ctx, "fetchMatch", fileAccessDataset, expr->queryChild(1), fileFilter, joinSeq);
  451. }
  452. void KeyedJoinInfo::buildIndexReadMatch(BuildCtx & ctx)
  453. {
  454. LinkedHqlExpr matchExpr = monitors->queryExtraFilter();
  455. if (matchExpr)
  456. {
  457. BuildCtx matchctx(ctx);
  458. matchctx.addQuotedCompound("virtual bool indexReadMatch(const void * _left, const void * _right, unsigned __int64 _filepos, IBlobProvider * blobs)");
  459. matchctx.addQuotedLiteral("const unsigned char * left = (const unsigned char *) _left;");
  460. matchctx.addQuotedLiteral("const unsigned char * right = (const unsigned char *) _right;");
  461. OwnedHqlExpr fileposExpr = getFilepos(rawKey, false);
  462. OwnedHqlExpr fileposVar = createVariable("_filepos", fileposExpr->getType());
  463. if (translator.queryOptions().spotCSE)
  464. matchExpr.setown(spotScalarCSE(matchExpr, NULL, translator.queryOptions().spotCseInIfDatasetConditions));
  465. translator.associateBlobHelper(matchctx, rawKey, "blobs");
  466. translator.bindTableCursor(matchctx, keyAccessDataset, "left", no_left, joinSeq);
  467. translator.bindTableCursor(matchctx, rawKey, "right");
  468. matchctx.associateExpr(fileposExpr, fileposVar);
  469. translator.buildReturn(matchctx, matchExpr);
  470. }
  471. }
  472. void KeyedJoinInfo::buildLeftOnly(BuildCtx & ctx)
  473. {
  474. if (leftOnlyMatch)
  475. {
  476. BuildCtx funcctx(ctx);
  477. funcctx.addQuotedCompound("virtual bool leftCanMatch(const void * _left)");
  478. funcctx.addQuotedLiteral("const unsigned char * left = (const unsigned char *)_left;");
  479. translator.bindTableCursor(funcctx, expr->queryChild(0), "left", no_left, joinSeq);
  480. translator.buildReturn(funcctx, leftOnlyMatch);
  481. }
  482. }
  483. void KeyedJoinInfo::buildMonitors(BuildCtx & ctx)
  484. {
  485. monitors->optimizeSegments(keyAccessDataset->queryRecord());
  486. //---- virtual void createSegmentMonitors(struct IIndexReadContext *) { ... } ----
  487. BuildCtx createSegmentCtx(ctx);
  488. createSegmentCtx.addQuotedCompound("virtual void createSegmentMonitors(IIndexReadContext *irc, const void * _left)");
  489. createSegmentCtx.addQuotedLiteral("const unsigned char * left = (const unsigned char *) _left;");
  490. translator.bindTableCursor(createSegmentCtx, keyAccessDataset, "left", no_left, joinSeq);
  491. monitors->buildSegments(createSegmentCtx, "irc", false);
  492. }
  493. void KeyedJoinInfo::buildTransform(BuildCtx & ctx)
  494. {
  495. BuildCtx funcctx(ctx);
  496. switch (expr->getOperator())
  497. {
  498. case no_join:
  499. case no_denormalize:
  500. {
  501. funcctx.addQuotedCompound("virtual size32_t transform(ARowBuilder & crSelf, const void * _left, const void * _right, unsigned __int64 _filepos, unsigned counter)");
  502. translator.associateCounter(funcctx, counter, "counter");
  503. break;
  504. }
  505. case no_denormalizegroup:
  506. {
  507. funcctx.addQuotedCompound("virtual size32_t transform(ARowBuilder & crSelf, const void * _left, const void * _right, unsigned numRows, const void * * _rows)");
  508. funcctx.addQuotedLiteral("unsigned char * * rows = (unsigned char * *) _rows;");
  509. break;
  510. }
  511. }
  512. translator.ensureRowAllocated(funcctx, "crSelf");
  513. buildTransformBody(funcctx, expr->queryChild(3));
  514. }
  515. //expand references to oldDataset using ds.
  516. //First expand references like a.b using the table definition
  517. //Then need to expand references to the complete table as a usertable projection.
  518. //e.g., left.x := l.x + right;
  519. static IHqlExpression * expandDatasetReferences(IHqlExpression * expr, IHqlExpression * ds, IHqlExpression * oldDataset, IHqlExpression * newDataset)
  520. {
  521. TableProjectMapper mapper(ds);
  522. OwnedHqlExpr expanded = mapper.expandFields(expr, oldDataset, newDataset);
  523. OwnedHqlExpr mapParent;
  524. IHqlExpression * dsParent = ds->queryChild(0);
  525. OwnedHqlExpr seq = createSelectorSequence();
  526. switch (getChildDatasetType(ds))
  527. {
  528. case childdataset_dataset:
  529. mapParent.set(dsParent->queryNormalizedSelector());
  530. break;
  531. case childdataset_left:
  532. UNIMPLEMENTED;
  533. mapParent.setown(createSelector(no_left, dsParent, querySelSeq(ds)));
  534. break;
  535. default:
  536. UNIMPLEMENTED;
  537. break;
  538. }
  539. OwnedHqlExpr newLeft = createSelector(no_left, dsParent, seq);
  540. OwnedHqlExpr newKeyTransform = replaceSelector(queryNewColumnProvider(ds), mapParent, newLeft);
  541. HqlExprArray args;
  542. unwindChildren(args, newKeyTransform);
  543. newKeyTransform.setown(createValue(no_transform, makeTransformType(LINK(ds->queryRecordType())), args));
  544. OwnedHqlExpr rightProject = createRow(no_projectrow, LINK(newDataset), createComma(LINK(newKeyTransform), LINK(seq)));
  545. OwnedHqlExpr wrappedProject = createRow(no_newrow, LINK(rightProject));
  546. return replaceSelector(expanded, oldDataset, wrappedProject);
  547. }
  548. IHqlExpression * KeyedJoinInfo::expandDatasetReferences(IHqlExpression * transform, IHqlExpression * ds)
  549. {
  550. switch (ds->getOperator())
  551. {
  552. case no_newusertable:
  553. case no_hqlproject:
  554. break;
  555. default:
  556. return LINK(transform);
  557. }
  558. OwnedHqlExpr oldRight = createSelector(no_right, ds, joinSeq);
  559. OwnedHqlExpr newRight = createSelector(no_right, ds->queryChild(0), joinSeq);
  560. return ::expandDatasetReferences(transform, ds, oldRight, newRight);
  561. }
  562. void KeyedJoinInfo::buildTransformBody(BuildCtx & ctx, IHqlExpression * transform)
  563. {
  564. IHqlExpression * rhs = expr->queryChild(1);
  565. IHqlExpression * rhsRecord = rhs->queryRecord();
  566. IHqlExpression * rowsid = expr->queryAttribute(_rowsid_Atom);
  567. OwnedHqlExpr originalRight = createSelector(no_right, rhsRecord, joinSeq);
  568. OwnedHqlExpr serializedRhsRecord = getSerializedForm(rhsRecord, diskAtom);
  569. OwnedHqlExpr serializedRight = createSelector(no_right, serializedRhsRecord, joinSeq);
  570. OwnedHqlExpr joinDataset = createDataset(no_anon, LINK(extractJoinFieldsRecord));
  571. OwnedHqlExpr extractedRight = createSelector(no_right, extractJoinFieldsRecord, joinSeq);
  572. OwnedHqlExpr newTransform = LINK(transform);
  573. //The RIGHT passed into the transform does not match the format of the key.
  574. //In particular all fields will be serialized, and unused fields are likely to be removed.
  575. //So any references in the transform to RIGHT and fields from RIGHT need to be mapped accordingly.
  576. //If ROWS(RIGHT) is used, and RIGHT needs to be deserialized, then it needs to be mapped first.
  577. if (expr->getOperator() == no_denormalizegroup)
  578. {
  579. assertex(extractedRight == serializedRight); // no fields are currently projected out. Needs to change if they ever are.
  580. if (extractedRight != originalRight)
  581. {
  582. //References to ROWS(originalRight) need to be replaced with DESERIALIZE(ROWS(serializedRight))
  583. OwnedHqlExpr originalRows = createDataset(no_rows, LINK(originalRight), LINK(rowsid));
  584. OwnedHqlExpr rowsExpr = createDataset(no_rows, LINK(extractedRight), LINK(rowsid));
  585. OwnedHqlExpr deserializedRows = ensureDeserialized(rowsExpr, rhs->queryType(), diskAtom);
  586. newTransform.setown(replaceExpression(newTransform, originalRows, deserializedRows));
  587. }
  588. }
  589. newTransform.setown(replaceMemorySelectorWithSerializedSelector(newTransform, rhsRecord, no_right, joinSeq, diskAtom));
  590. OwnedHqlExpr fileposExpr = getFilepos(extractedRight, false);
  591. if (extractJoinFieldsTransform)
  592. {
  593. IHqlExpression * fileposField = isFullJoin() ? queryVirtualFileposField(file->queryRecord()) : queryLastField(key->queryRecord());
  594. if (keyHasFileposition && fileposField && (expr->getOperator() != no_denormalizegroup))
  595. {
  596. HqlMapTransformer fileposMapper;
  597. OwnedHqlExpr select = createSelectExpr(LINK(serializedRight), LINK(fileposField));
  598. OwnedHqlExpr castFilepos = ensureExprType(fileposExpr, fileposField->queryType());
  599. fileposMapper.setMapping(select, castFilepos);
  600. newTransform.setown(fileposMapper.transformRoot(newTransform));
  601. }
  602. newTransform.setown(replaceSelector(newTransform, serializedRight, extractedRight));
  603. }
  604. else
  605. {
  606. if (isFullJoin())
  607. newTransform.setown(expandDatasetReferences(newTransform, expandedFile));
  608. else
  609. newTransform.setown(expandDatasetReferences(newTransform, expandedKey));
  610. }
  611. newTransform.setown(optimizeHqlExpression(translator.queryErrorProcessor(), newTransform, HOOfold|HOOcompoundproject));
  612. newTransform.setown(foldHqlExpression(newTransform));
  613. BoundRow * selfCursor = translator.buildTransformCursors(ctx, newTransform, expr->queryChild(0), joinDataset, expr, joinSeq);
  614. if (expr->getOperator() == no_denormalizegroup)
  615. {
  616. //Last parameter is false since implementation of group keyed denormalize in roxie passes in pointers to the serialized slave data
  617. bool rowsAreLinkCounted = false;
  618. translator.bindRows(ctx, no_right, joinSeq, rowsid, joinDataset, "numRows", "rows", rowsAreLinkCounted);
  619. }
  620. //Map the file position field in the file to the incoming parameter
  621. //MORE: This doesn't cope with local/global file position distinctions.
  622. OwnedHqlExpr fileposVar = createVariable("_filepos", makeIntType(8, false));
  623. ctx.associateExpr(fileposExpr, fileposVar);
  624. translator.doBuildTransformBody(ctx, newTransform, selfCursor);
  625. if (isFullJoin())
  626. {
  627. SourceFieldUsage * fileUsage = translator.querySourceFieldUsage(rawFile);
  628. if (fileUsage)
  629. {
  630. OwnedHqlExpr rawTransform = expandDatasetReferences(transform, expandedFile);
  631. OwnedHqlExpr right = createSelector(no_right, rawFile, joinSeq);
  632. ::gatherFieldUsage(fileUsage, rawTransform, right);
  633. }
  634. }
  635. else
  636. {
  637. SourceFieldUsage * keyUsage = translator.querySourceFieldUsage(rawKey);
  638. if (keyUsage)
  639. {
  640. OwnedHqlExpr rawTransform = expandDatasetReferences(transform, expandedKey);
  641. OwnedHqlExpr right = createSelector(no_right, rawKey, joinSeq);
  642. ::gatherFieldUsage(keyUsage, rawTransform, right);
  643. }
  644. }
  645. }
  646. void KeyedJoinInfo::buildFailureTransform(BuildCtx & ctx, IHqlExpression * onFailTransform)
  647. {
  648. BuildCtx funcctx(ctx);
  649. funcctx.addQuotedCompound("virtual size32_t onFailTransform(ARowBuilder & crSelf, const void * _left, const void * _right, unsigned __int64 _filepos, IException * except)");
  650. translator.associateLocalFailure(funcctx, "except");
  651. translator.ensureRowAllocated(funcctx, "crSelf");
  652. buildTransformBody(funcctx, onFailTransform);
  653. }
  654. IHqlExpression * KeyedJoinInfo::optimizeTransfer(HqlExprArray & fields, HqlExprArray & values, IHqlExpression * filter, IHqlExpression * leftSelector)
  655. {
  656. switch (filter->getOperator())
  657. {
  658. case no_join:
  659. return NULL;
  660. case no_left:
  661. if (filter->queryBody() == leftSelector)
  662. return NULL; // Something nasty e.g., evaluate()...
  663. break;
  664. case no_right:
  665. return LINK(filter);
  666. case no_select:
  667. {
  668. //Check for an expression of the form LEFT.x.y.z.a.b.c, but if any of x,y,z, are datasets then process later.
  669. IHqlExpression * cur = filter;
  670. IHqlExpression * ds;
  671. loop
  672. {
  673. ds = cur->queryChild(0);
  674. //if a select from a dataset, then wait until we recurse to here
  675. if ((ds->getOperator() != no_select) || ds->isDataset())
  676. break;
  677. cur = ds;
  678. }
  679. //check it was the correct left..
  680. if (ds->queryBody() == leftSelector)
  681. {
  682. unsigned match = values.find(*filter);
  683. if (match == NotFound)
  684. {
  685. match = fields.ordinality();
  686. LinkedHqlExpr field = filter->queryChild(1);
  687. if (fields.find(*field) != NotFound)
  688. {
  689. //Check same field isn't used in two different nested records.
  690. StringBuffer name;
  691. name.append("__unnamed__").append(fields.ordinality());
  692. field.setown(createField(createIdAtom(name), field->getType(), NULL, NULL));
  693. }
  694. fields.append(*LINK(field));
  695. values.append(*LINK(filter));
  696. }
  697. IHqlExpression * matchField = &fields.item(match);
  698. OwnedHqlExpr serializedField = getSerializedForm(matchField, diskAtom);
  699. OwnedHqlExpr result = createSelectExpr(getActiveTableSelector(), LINK(serializedField));
  700. return ensureDeserialized(result, matchField->queryType(), diskAtom);
  701. }
  702. break;
  703. }
  704. case no_attr_expr:
  705. if (filter->queryName() == _selectors_Atom)
  706. return LINK(filter);
  707. break;
  708. }
  709. HqlExprArray children;
  710. ForEachChild(i, filter)
  711. {
  712. IHqlExpression * next = optimizeTransfer(fields, values, filter->queryChild(i), leftSelector);
  713. if (!next) return NULL;
  714. children.append(*next);
  715. }
  716. return cloneOrLink(filter, children);
  717. }
  718. IHqlExpression * reverseOptimizeTransfer(IHqlExpression * left, IHqlExpression * transform, IHqlExpression * filter)
  719. {
  720. if (!transform)
  721. return LINK(filter);
  722. HqlMapTransformer mapper;
  723. ForEachChild(i, transform)
  724. {
  725. IHqlExpression * cur = transform->queryChild(i);
  726. IHqlExpression * tgt = cur->queryChild(0);
  727. IHqlExpression * src = cur->queryChild(1);
  728. OwnedHqlExpr selector = createSelectExpr(LINK(left), LINK(tgt->queryChild(1)));
  729. mapper.setMapping(selector, src);
  730. }
  731. return mapper.transformRoot(filter);
  732. }
  733. void KeyedJoinInfo::optimizeTransfer(SharedHqlExpr & targetDataset, SharedHqlExpr & targetTransform, SharedHqlExpr & filter, OwnedHqlExpr * extraFilter)
  734. {
  735. IHqlExpression * dataset = expr->queryChild(0);
  736. if (canOptimizeTransfer)
  737. {
  738. if (filter)
  739. {
  740. IHqlExpression * record = dataset->queryRecord();
  741. HqlExprArray fields;
  742. HqlExprArray values;
  743. bool hasExtra = (extraFilter && extraFilter->get());
  744. OwnedHqlExpr oldLeft = createSelector(no_left, dataset, joinSeq);
  745. OwnedHqlExpr newFilter = optimizeTransfer(fields, values, filter, oldLeft);
  746. OwnedHqlExpr newExtraFilter = hasExtra ? optimizeTransfer(fields, values, *extraFilter, oldLeft) : NULL;
  747. if (newFilter && (newExtraFilter || !hasExtra) && fields.ordinality() < getFieldCount(dataset->queryRecord()))
  748. {
  749. OwnedHqlExpr extractedRecord = createRecord(fields);
  750. OwnedHqlExpr serializedRecord = getSerializedForm(extractedRecord, diskAtom);
  751. targetDataset.setown(createDataset(no_anon, LINK(serializedRecord), NULL));
  752. HqlExprArray assigns;
  753. OwnedHqlExpr self = getSelf(serializedRecord);
  754. ForEachItemIn(i, fields)
  755. {
  756. IHqlExpression * curField = &fields.item(i);
  757. OwnedHqlExpr serializedField = getSerializedForm(curField, diskAtom);
  758. OwnedHqlExpr value = ensureSerialized(&values.item(i), diskAtom);
  759. assigns.append(*createAssign(createSelectExpr(LINK(self), LINK(serializedField)), LINK(value)));
  760. }
  761. targetTransform.setown(createValue(no_newtransform, makeTransformType(serializedRecord->getType()), assigns));
  762. OwnedHqlExpr leftSelect = createSelector(no_left, serializedRecord, joinSeq);
  763. filter.setown(replaceSelector(newFilter, queryActiveTableSelector(), leftSelect));
  764. if (hasExtra)
  765. extraFilter->setown(replaceSelector(newExtraFilter, queryActiveTableSelector(), leftSelect));
  766. }
  767. else if (recordRequiresSerialization(record, diskAtom))
  768. {
  769. OwnedHqlExpr serializedRecord = getSerializedForm(record, diskAtom);
  770. targetDataset.setown(createDataset(no_anon, LINK(serializedRecord)));
  771. targetTransform.setown(createRecordMappingTransform(no_transform, serializedRecord, oldLeft));
  772. filter.setown(replaceMemorySelectorWithSerializedSelector(filter, record, no_left, joinSeq, diskAtom));
  773. if (hasExtra)
  774. extraFilter->setown(replaceMemorySelectorWithSerializedSelector(*extraFilter, record, no_left, joinSeq, diskAtom));
  775. }
  776. else
  777. targetDataset.set(dataset);
  778. }
  779. else
  780. {
  781. //any fields will be serialized automatically, and no filter
  782. targetDataset.set(dataset);
  783. }
  784. }
  785. else
  786. {
  787. targetDataset.set(dataset);
  788. }
  789. }
  790. static void expandAllFields(HqlExprArray & fieldsAccessed, IHqlExpression * expr)
  791. {
  792. switch (expr->getOperator())
  793. {
  794. case no_field:
  795. if (fieldsAccessed.find(*expr) == NotFound)
  796. fieldsAccessed.append(*LINK(expr));
  797. break;
  798. case no_ifblock:
  799. expandAllFields(fieldsAccessed, expr->queryChild(1));
  800. break;
  801. case no_record:
  802. ForEachChild(i, expr)
  803. expandAllFields(fieldsAccessed, expr->queryChild(i));
  804. break;
  805. }
  806. }
  807. static void doGatherFieldsAccessed(RecursionChecker & checker, HqlExprArray & fieldsAccessed, IHqlExpression * expr, IHqlExpression * ds)
  808. {
  809. if (checker.alreadyVisited(expr))
  810. return;
  811. checker.setVisited(expr);
  812. switch (expr->getOperator())
  813. {
  814. case no_select:
  815. if (expr->queryChild(0) == ds)
  816. {
  817. IHqlExpression * field = expr->queryChild(1);
  818. if (fieldsAccessed.find(*field) == NotFound)
  819. fieldsAccessed.append(*LINK(field));
  820. return;
  821. }
  822. break;
  823. case no_record:
  824. case no_attr:
  825. return;
  826. case no_left:
  827. case no_right:
  828. //Never walk children
  829. if (expr == ds)
  830. expandAllFields(fieldsAccessed, expr->queryRecord());
  831. return;
  832. default:
  833. if (expr == ds)
  834. {
  835. expandAllFields(fieldsAccessed, expr->queryRecord());
  836. return;
  837. }
  838. break;
  839. }
  840. ForEachChild(i, expr)
  841. doGatherFieldsAccessed(checker, fieldsAccessed, expr->queryChild(i), ds);
  842. }
  843. static void gatherFieldsAccessed(HqlExprArray & fieldsAccessed, IHqlExpression * cond, IHqlExpression * ds)
  844. {
  845. if (!cond)
  846. return;
  847. RecursionChecker checker;
  848. doGatherFieldsAccessed(checker, fieldsAccessed, cond, ds);
  849. }
  850. void KeyedJoinInfo::optimizeExtractJoinFields()
  851. {
  852. HqlExprArray fieldsAccessed;
  853. bool doExtract = false;
  854. IHqlExpression * fileposField = NULL;
  855. OwnedHqlExpr right = createSelector(no_right, expr->queryChild(1), joinSeq);
  856. IHqlExpression * rightRecord = right->queryRecord();
  857. OwnedHqlExpr extractedRecord;
  858. if (expr->getOperator() == no_denormalizegroup)
  859. {
  860. //Version1: Don't remove any fields
  861. doExtract = true;
  862. OwnedHqlExpr rows = createDataset(no_rows, LINK(right), LINK(expr->queryAttribute(_rowsid_Atom)));
  863. if (isFullJoin())
  864. {
  865. //unwindFields(fieldsAccessed, file->queryRecord());
  866. extractedRecord.set(file->queryRecord());
  867. }
  868. else
  869. {
  870. // unwindFields(fieldsAccessed, key->queryRecord());
  871. extractedRecord.set(key->queryRecord());
  872. if (keyHasFileposition)
  873. fileposField = queryLastField(key->queryRecord());
  874. }
  875. }
  876. else
  877. {
  878. gatherFieldsAccessed(fieldsAccessed, expr->queryChild(3), right);
  879. gatherFieldsAccessed(fieldsAccessed, queryAttributeChild(expr, onFailAtom, 0), right);
  880. if (isFullJoin())
  881. {
  882. IHqlExpression * filepos = queryVirtualFileposField(file->queryRecord());
  883. if (filepos)
  884. fieldsAccessed.zap(*filepos);
  885. if (translator.getTargetClusterType() != HThorCluster)
  886. doExtract = (fieldsAccessed.ordinality() < getFlatFieldCount(rawRhs->queryRecord()));
  887. }
  888. else
  889. {
  890. IHqlExpression * keyRecord = key->queryRecord();
  891. IHqlExpression * filepos = queryLastField(keyRecord);
  892. if (filepos && keyHasFileposition)
  893. fieldsAccessed.zap(*filepos);
  894. if (translator.getTargetClusterType() != HThorCluster)
  895. doExtract = (fieldsAccessed.ordinality() < getFlatFieldCount(keyRecord)-1);
  896. if (!doExtract && recordContainsBlobs(keyRecord))
  897. doExtract = true;
  898. }
  899. if (recordRequiresSerialization(rightRecord, diskAtom))
  900. doExtract = true;
  901. }
  902. if (doExtract)
  903. {
  904. HqlExprArray assigns;
  905. OwnedHqlExpr left = createSelector(no_left, rawRhs, joinSeq);
  906. if (extractedRecord || (fieldsAccessed.ordinality() != 0))
  907. {
  908. if (!extractedRecord)
  909. extractedRecord.setown(createRecord(fieldsAccessed));
  910. extractJoinFieldsRecord.setown(getSerializedForm(extractedRecord, diskAtom));
  911. OwnedHqlExpr self = getSelf(extractJoinFieldsRecord);
  912. OwnedHqlExpr memorySelf = getSelf(extractedRecord);
  913. if (isFullJoin() && (rawRhs->queryBody() == expandedFile->queryBody()))
  914. {
  915. assertex(extractedRecord == extractJoinFieldsRecord);
  916. ForEachChild(i, extractedRecord)
  917. {
  918. IHqlExpression * curMemoryField = extractedRecord->queryChild(i);
  919. IHqlExpression * curSerializedField = extractJoinFieldsRecord->queryChild(i);
  920. if (curMemoryField == fileposField)
  921. assigns.append(*createAssign(createSelectExpr(LINK(self), LINK(curSerializedField)), getFilepos(left, false)));
  922. else if (!curMemoryField->isAttribute())
  923. assigns.append(*createAssign(createSelectExpr(LINK(self), LINK(curSerializedField)), createSelectExpr(LINK(left), LINK(curMemoryField)))); // no
  924. }
  925. }
  926. else
  927. {
  928. TableProjectMapper fieldMapper;
  929. if (isFullJoin())
  930. fieldMapper.setDataset(expandedFile);
  931. else
  932. fieldMapper.setDataset(expandedKey);
  933. ForEachChild(i, extractJoinFieldsRecord)
  934. {
  935. IHqlExpression * curMemoryField = extractedRecord->queryChild(i);
  936. IHqlExpression * curSerializedField = extractJoinFieldsRecord->queryChild(i);
  937. if (curMemoryField == fileposField)
  938. assigns.append(*createAssign(createSelectExpr(LINK(self), LINK(curSerializedField)), getFilepos(left, false)));
  939. else if (!curMemoryField->isAttribute())
  940. {
  941. OwnedHqlExpr tgt = createSelectExpr(LINK(self), LINK(curSerializedField));
  942. OwnedHqlExpr src = createSelectExpr(LINK(memorySelf), LINK(curMemoryField));
  943. OwnedHqlExpr mappedSrc = fieldMapper.expandFields(src, memorySelf, left, rawRhs);
  944. assigns.append(*createAssign(tgt.getClear(), ensureSerialized(mappedSrc, diskAtom)));
  945. }
  946. }
  947. }
  948. }
  949. else
  950. {
  951. //A bit of a hack - Richard can't cope with zero length values being returned, so allocate
  952. //a single byte to keep him happy.
  953. OwnedHqlExpr nonEmptyAttr = createAttribute(_nonEmpty_Atom);
  954. extractJoinFieldsRecord.setown(createRecord(nonEmptyAttr));
  955. }
  956. extractJoinFieldsTransform.setown(createValue(no_transform, makeTransformType(extractJoinFieldsRecord->getType()), assigns));
  957. }
  958. else
  959. {
  960. if (isFullJoin())
  961. extractJoinFieldsRecord.set(rawRhs->queryRecord());
  962. else
  963. extractJoinFieldsRecord.set(rawKey->queryRecord());
  964. }
  965. }
  966. bool KeyedJoinInfo::processFilter()
  967. {
  968. IHqlExpression * atmostAttr = expr->queryAttribute(atmostAtom);
  969. AtmostLimit atmost(atmostAttr);
  970. IHqlExpression * cond = expr->queryChild(2);
  971. OwnedHqlExpr fuzzy, hard;
  972. splitFuzzyCondition(cond, atmost.required, fuzzy, hard);
  973. OwnedHqlExpr keyedKeyFilter, fuzzyKeyFilter;
  974. splitFilter(hard, keyedKeyFilter);
  975. if (!keyedKeyFilter)
  976. {
  977. if (!cond->queryValue() || cond->queryValue()->getBoolValue())
  978. {
  979. StringBuffer s;
  980. getExprECL(cond, s);
  981. if (isFullJoin())
  982. translator.throwError1(HQLERR_KeyAccessNoKeyField, s.str());
  983. else
  984. translator.throwError1(HQLERR_KeyedJoinTooComplex, s.str());
  985. }
  986. else
  987. leftOnlyMatch.set(cond);
  988. }
  989. if (atmostAttr && fileFilter)
  990. {
  991. StringBuffer s;
  992. translator.throwError1(HQLERR_BadKeyedJoinConditionAtMost,getExprECL(fileFilter, s.append(" (")).append(")").str());
  993. }
  994. splitFilter(fuzzy, fuzzyKeyFilter);
  995. //Now work out what fields need to be serialized to perform the match
  996. optimizeTransfer(keyAccessDataset, keyAccessTransform, keyedKeyFilter, &fuzzyKeyFilter);
  997. if (file && fileFilter)
  998. optimizeTransfer(fileAccessDataset, fileAccessTransform, fileFilter, NULL);
  999. //Now need to transform the index into its real representation so
  1000. //the hozed transforms take place.
  1001. unsigned payload = numPayloadFields(key);
  1002. TableProjectMapper mapper(expandedKey);
  1003. OwnedHqlExpr rightSelect = createSelector(no_right, key, joinSeq);
  1004. OwnedHqlExpr newFilter = mapper.expandFields(keyedKeyFilter, rightSelect, rawKey, rawKey);
  1005. //Now extract the filters from it.
  1006. OwnedHqlExpr extra;
  1007. monitors = new MonitorExtractor(rawKey, translator, -(int)numPayloadFields(rawKey), false);
  1008. if (newFilter)
  1009. monitors->extractFilters(newFilter, extra);
  1010. if (atmostAttr && extra && (atmost.required || !monitors->isCleanlyKeyedExplicitly()))
  1011. {
  1012. StringBuffer s;
  1013. //map the key references back so the error message refers to RIGHT instead of a weird key expression.
  1014. bool collapsedAll = false;
  1015. OwnedHqlExpr collapsed = mapper.collapseFields(extra, rawKey, rightSelect, rawKey, &collapsedAll);
  1016. translator.throwError1(HQLERR_BadKeyedJoinConditionAtMost,getExprECL(collapsed, s.append(" (")).append(")").str());
  1017. }
  1018. // OwnedHqlExpr oldLeft = createSelector(no_left, expr->queryChild(0), joinSeq);
  1019. OwnedHqlExpr newLeft = createSelector(no_left, keyAccessDataset, joinSeq);
  1020. //Finally extend the non-keyed filter with the non-keyed portion.
  1021. OwnedHqlExpr newFuzzyKeyFilter = mapper.expandFields(fuzzyKeyFilter, rightSelect, rawKey, rawKey);
  1022. // newFuzzyKeyFilter.setown(replaceSelector(newFuzzyKeyFilter, oldLeft, newLeft));
  1023. monitors->appendFilter(newFuzzyKeyFilter);
  1024. //add any key-invariant condition to the leftOnly match
  1025. IHqlExpression * keyedLeftOnly = monitors->queryGlobalGuard();
  1026. if (keyedLeftOnly)
  1027. extendConditionOwn(leftOnlyMatch, no_and, reverseOptimizeTransfer(newLeft, keyAccessTransform, keyedLeftOnly));
  1028. //optimize the fields returned from the rhs to perform the transform
  1029. if (expr->getOperator() != no_keyeddistribute)
  1030. optimizeExtractJoinFields();
  1031. SourceFieldUsage * keyUsage = translator.querySourceFieldUsage(rawKey);
  1032. if (keyUsage)
  1033. {
  1034. gatherFieldUsage(keyUsage, newFilter, rawKey->queryNormalizedSelector());
  1035. if (isFullJoin())
  1036. keyUsage->noteFilepos();
  1037. }
  1038. if (file && fileFilter)
  1039. {
  1040. SourceFieldUsage * fileUsage = translator.querySourceFieldUsage(rawFile);
  1041. if (fileUsage)
  1042. {
  1043. OwnedHqlExpr rawFilter = expandDatasetReferences(fileFilter, expandedFile);
  1044. OwnedHqlExpr fileRight = createSelector(no_right, rawFile, joinSeq);
  1045. gatherFieldUsage(fileUsage, rawFilter, fileRight);
  1046. }
  1047. }
  1048. return monitors->isKeyed();
  1049. }
  1050. void KeyedJoinInfo::splitFilter(IHqlExpression * filter, SharedHqlExpr & keyTarget)
  1051. {
  1052. if (!filter) return;
  1053. if (filter->getOperator() == no_and)
  1054. {
  1055. splitFilter(filter->queryChild(0), keyTarget);
  1056. splitFilter(filter->queryChild(1), keyTarget);
  1057. }
  1058. else if (containsOnlyLeft(filter))
  1059. extendAndCondition(leftOnlyMatch, filter);
  1060. else if (filter->queryValue())
  1061. {
  1062. //remove silly "and true" conditions
  1063. if (!filter->queryValue()->getBoolValue())
  1064. extendAndCondition(keyTarget, filter);
  1065. }
  1066. else
  1067. {
  1068. if (file)
  1069. {
  1070. bool doneAll = false;
  1071. OwnedHqlExpr fileRight = createSelector(no_right, file, joinSeq);
  1072. OwnedHqlExpr keyRight = createSelector(no_right, key, joinSeq);
  1073. OwnedHqlExpr mapped = keyedMapper.collapseFields(filter, fileRight, keyRight, &doneAll);
  1074. if (doneAll)
  1075. extendAndCondition(keyTarget, mapped);
  1076. else
  1077. extendAndCondition(fileFilter, filter);
  1078. }
  1079. else
  1080. extendAndCondition(keyTarget, filter);
  1081. }
  1082. }
  1083. void HqlCppTranslator::buildKeyedJoinExtra(ActivityInstance & instance, IHqlExpression * expr, KeyedJoinInfo * info)
  1084. {
  1085. //virtual IOutputMetaData * queryDiskRecordSize() = 0; // Excluding fpos and sequence
  1086. if (info->isFullJoin())
  1087. buildMetaMember(instance.classctx, info->queryRawRhs(), false, "queryDiskRecordSize");
  1088. //virtual unsigned __int64 extractPosition(const void * _right) = 0; // Gets file position value from rhs row
  1089. if (info->isFullJoin())
  1090. {
  1091. IHqlExpression * index = info->queryKey();
  1092. IHqlExpression * indexRecord = index->queryRecord();
  1093. BuildCtx ctx4(instance.startctx);
  1094. ctx4.addQuotedCompound("virtual unsigned __int64 extractPosition(const void * _right)");
  1095. ctx4.addQuotedLiteral("const unsigned char * right = (const unsigned char *) _right;");
  1096. bindTableCursor(ctx4, index, "right");
  1097. OwnedHqlExpr fileposExpr = createSelectExpr(LINK(index), LINK(indexRecord->queryChild(indexRecord->numChildren()-1)));
  1098. buildReturn(ctx4, fileposExpr);
  1099. }
  1100. //virtual const char * getFileName() = 0; // Returns filename of raw file fpos'es refer into
  1101. if (info->isFullJoin())
  1102. buildFilenameFunction(instance, instance.createctx, "getFileName", info->queryFileFilename(), hasDynamicFilename(info->queryFile()));
  1103. //virtual bool diskAccessRequired() = 0;
  1104. if (info->isFullJoin())
  1105. doBuildBoolFunction(instance.startctx, "diskAccessRequired", true);
  1106. //virtual size32_t transform(ARowBuilder & crSelf, const void * _left, const void * _right) = 0;
  1107. info->buildTransform(instance.startctx);
  1108. IHqlExpression * onFail = expr->queryAttribute(onFailAtom);
  1109. if (onFail)
  1110. {
  1111. //virtual size32_t onFailTransform(ARowBuilder & crSelf, const void * _left, const void * _right, unsigned __int64 _filepos, IException * except)
  1112. info->buildFailureTransform(instance.startctx, onFail->queryChild(0));
  1113. }
  1114. //limit helpers...
  1115. IHqlExpression * limit = expr->queryAttribute(limitAtom);
  1116. if (limit)
  1117. {
  1118. if (limit->hasAttribute(skipAtom))
  1119. {
  1120. BuildCtx ctx1(instance.startctx);
  1121. ctx1.addQuotedCompound("virtual unsigned __int64 getSkipLimit()");
  1122. buildReturn(ctx1, limit->queryChild(0));
  1123. }
  1124. else
  1125. buildLimitHelpers(instance.startctx, limit->queryChild(0), limit->queryChild(1), false, info->queryKeyFilename(), instance.activityId);
  1126. }
  1127. }
  1128. void HqlCppTranslator::buildKeyJoinIndexReadHelper(ActivityInstance & instance, IHqlExpression * expr, KeyedJoinInfo * info)
  1129. {
  1130. //virtual size32_t extractIndexReadFields(ARowBuilder & crSelf, const void * _input) = 0;
  1131. //virtual IOutputMetaData * queryIndexReadInputRecordSize() = 0;
  1132. info->buildExtractIndexReadFields(instance.startctx);
  1133. //virtual const char * getIndexFileName() = 0;
  1134. buildFilenameFunction(instance, instance.startctx, "getIndexFileName", info->queryKeyFilename(), hasDynamicFilename(info->queryKey()));
  1135. //virtual IOutputMetaData * queryIndexRecordSize() = 0; //Excluding fpos and sequence
  1136. buildMetaMember(instance.classctx, info->queryRawKey(), false, "queryIndexRecordSize");
  1137. //virtual void createSegmentMonitors(IIndexReadContext *ctx, const void *lhs) = 0;
  1138. info->buildMonitors(instance.startctx);
  1139. //virtual bool indexReadMatch(const void * indexRow, const void * inputRow) = 0;
  1140. info->buildIndexReadMatch(instance.startctx);
  1141. }
  1142. void HqlCppTranslator::buildKeyJoinFetchHelper(ActivityInstance & instance, IHqlExpression * expr, KeyedJoinInfo * info)
  1143. {
  1144. //virtual size32_t extractFetchFields(ARowBuilder & crSelf, const void * _input) = 0;
  1145. //virtual IOutputMetaData * queryFetchInputRecordSize() = 0;
  1146. info->buildExtractFetchFields(instance.startctx);
  1147. // Inside the fetch remote activity
  1148. //virtual bool fetchMatch(const void * diskRow, const void * inputRow) = 0;
  1149. info->buildFetchMatch(instance.startctx);
  1150. //virtual size32_t extractJoinFields(void *dest, const void *diskRow, IBlobProvider * blobs) = 0;
  1151. info->buildExtractJoinFields(instance);
  1152. }
  1153. ABoundActivity * HqlCppTranslator::doBuildActivityKeyedJoinOrDenormalize(BuildCtx & ctx, IHqlExpression * expr)
  1154. {
  1155. KeyedJoinInfo info(*this, expr, !targetHThor());
  1156. IHqlExpression * cond = expr->queryChild(2);
  1157. if (!info.processFilter() && !cond->isConstant())
  1158. info.reportFailureReason(cond);
  1159. if (info.isFullJoin())
  1160. {
  1161. IHqlExpression * table = info.queryFile();
  1162. if (table->getOperator() != no_table)
  1163. throwError(HQLERR_FullJoinNeedDataset);
  1164. }
  1165. Owned<ABoundActivity> boundDataset1 = buildCachedActivity(ctx, expr->queryChild(0));
  1166. Owned<ABoundActivity> boundIndexActivity;
  1167. if (options.forceActivityForKeyedJoin || info.requireActivityForKey())
  1168. boundIndexActivity.setown(buildCachedActivity(ctx, info.queryOriginalKey()));
  1169. node_operator op = expr->getOperator();
  1170. ThorActivityKind kind;
  1171. switch (op)
  1172. {
  1173. case no_join:
  1174. kind = TAKkeyedjoin;
  1175. break;
  1176. case no_denormalize:
  1177. kind = TAKkeyeddenormalize;
  1178. break;
  1179. case no_denormalizegroup:
  1180. kind = TAKkeyeddenormalizegroup;
  1181. break;
  1182. default:
  1183. throwUnexpected();
  1184. }
  1185. Owned<ActivityInstance> instance = new ActivityInstance(*this, ctx, kind, expr, (op == no_join) ? "KeyedJoin" : "KeyedDenormalize");
  1186. IHqlExpression * indexName = info.queryKeyFilename();
  1187. if (indexName)
  1188. {
  1189. OwnedHqlExpr folded = foldHqlExpression(indexName);
  1190. if (folded->queryValue())
  1191. {
  1192. StringBuffer graphLabel;
  1193. if (instance->isGrouped)
  1194. graphLabel.append("Grouped ");
  1195. else if (instance->isLocal)
  1196. graphLabel.append("Local ");
  1197. graphLabel.append(getActivityText(instance->kind));
  1198. getStringValue(graphLabel.append("\n'"), folded).append("'");
  1199. instance->graphLabel.set(graphLabel.str());
  1200. }
  1201. }
  1202. buildActivityFramework(instance);
  1203. IHqlExpression * rowlimit = expr->queryAttribute(rowLimitAtom);
  1204. StringBuffer s;
  1205. buildInstancePrefix(instance);
  1206. IHqlExpression * atmostAttr = expr->queryAttribute(atmostAtom);
  1207. AtmostLimit atmost(atmostAttr);
  1208. //virtual unsigned getJoinFlags()
  1209. StringBuffer flags;
  1210. bool isLeftOuter = (expr->hasAttribute(leftonlyAtom) || expr->hasAttribute(leftouterAtom));
  1211. if (expr->hasAttribute(leftonlyAtom)) flags.append("|JFexclude");
  1212. if (isLeftOuter) flags.append("|JFleftouter");
  1213. if (expr->hasAttribute(firstAtom)) flags.append("|JFfirst");
  1214. if (expr->hasAttribute(firstLeftAtom)) flags.append("|JFfirstleft");
  1215. if (transformContainsSkip(expr->queryChild(3)))
  1216. flags.append("|JFtransformMaySkip");
  1217. if (info.isFetchFiltered())
  1218. flags.append("|JFfetchMayFilter");
  1219. if (rowlimit && rowlimit->hasAttribute(skipAtom))
  1220. flags.append("|JFmatchAbortLimitSkips");
  1221. if (rowlimit && rowlimit->hasAttribute(countAtom))
  1222. flags.append("|JFcountmatchabortlimit");
  1223. if (expr->hasAttribute(onFailAtom))
  1224. flags.append("|JFonfail");
  1225. if (info.isKeyOpt())
  1226. flags.append("|JFindexoptional");
  1227. if (info.needToExtractJoinFields())
  1228. flags.append("|JFextractjoinfields");
  1229. if (expr->hasAttribute(unorderedAtom))
  1230. flags.append("|JFreorderable");
  1231. if (transformReturnsSide(expr, no_left, 0))
  1232. flags.append("|JFtransformmatchesleft");
  1233. if (info.queryKeyFilename() && !info.queryKeyFilename()->isConstant())
  1234. flags.append("|JFvarindexfilename");
  1235. if (hasDynamicFilename(info.queryKey()))
  1236. flags.append("|JFdynamicindexfilename");
  1237. if (boundIndexActivity)
  1238. flags.append("|JFindexfromactivity");
  1239. if (flags.length())
  1240. doBuildUnsignedFunction(instance->classctx, "getJoinFlags", flags.str()+1);
  1241. //Fetch flags
  1242. flags.clear();
  1243. if (info.isFullJoin())
  1244. {
  1245. if (info.isFileOpt())
  1246. flags.append("|FFdatafileoptional");
  1247. if (!info.queryFileFilename()->isConstant())
  1248. flags.append("|FFvarfilename");
  1249. if (hasDynamicFilename(info.queryFile()))
  1250. flags.append("|FFdynamicfilename");
  1251. }
  1252. if (flags.length())
  1253. doBuildUnsignedFunction(instance->classctx, "getFetchFlags", flags.str()+1);
  1254. //virtual unsigned getJoinLimit()
  1255. if (!isZero(atmost.limit))
  1256. doBuildUnsignedFunction(instance->startctx, "getJoinLimit", atmost.limit);
  1257. //virtual unsigned getKeepLimit()
  1258. LinkedHqlExpr keepLimit = queryAttributeChild(expr, keepAtom, 0);
  1259. if (keepLimit)
  1260. doBuildUnsignedFunction(instance->startctx, "getKeepLimit", keepLimit);
  1261. bool implicitLimit = !rowlimit && !atmostAttr &&
  1262. (!keepLimit || info.hasPostFilter()) &&
  1263. !expr->hasAttribute(leftonlyAtom);
  1264. //virtual unsigned getKeepLimit()
  1265. doBuildJoinRowLimitHelper(*instance, rowlimit, info.queryKeyFilename(), implicitLimit);
  1266. buildFormatCrcFunction(instance->classctx, "getIndexFormatCrc", info.queryRawKey(), info.queryRawKey(), 1);
  1267. if (info.isFullJoin())
  1268. {
  1269. //Remove virtual attributes from the record, so the crc will be compatible with the disk read record
  1270. //can occur with a (highly unusual) full keyed join to a persist file... (see indexread14.ecl)
  1271. OwnedHqlExpr noVirtualRecord = removeVirtualAttributes(info.queryRawRhs()->queryRecord());
  1272. buildFormatCrcFunction(instance->classctx, "getDiskFormatCrc", noVirtualRecord, NULL, 0);
  1273. buildEncryptHelper(instance->startctx, info.queryFile()->queryAttribute(encryptAtom), "getFileEncryptKey");
  1274. }
  1275. IHqlExpression * key = info.queryKey();
  1276. buildSerializedLayoutMember(instance->classctx, key->queryRecord(), "getIndexLayout", numKeyedFields(key));
  1277. //--function to clear right, used for left outer join
  1278. if (isLeftOuter || expr->hasAttribute(onFailAtom))
  1279. info.buildClearRightFunction(instance->createctx);
  1280. buildKeyedJoinExtra(*instance, expr, &info);
  1281. buildKeyJoinIndexReadHelper(*instance, expr, &info);
  1282. buildKeyJoinFetchHelper(*instance, expr, &info);
  1283. info.buildLeftOnly(instance->startctx);
  1284. if (targetRoxie())
  1285. {
  1286. instance->addAttributeBool("_diskAccessRequired", info.isFullJoin());
  1287. instance->addAttributeBool("_isIndexOpt", info.isKeyOpt());
  1288. instance->addAttributeBool("_isOpt", info.isFileOpt());
  1289. }
  1290. buildInstanceSuffix(instance);
  1291. buildConnectInputOutput(ctx, instance, boundDataset1, 0, 0);
  1292. if (boundIndexActivity)
  1293. buildConnectInputOutput(ctx, instance, boundIndexActivity, 0, 1);
  1294. addFileDependency(info.queryKeyFilename(), instance->queryBoundActivity());
  1295. if (info.isFullJoin())
  1296. addFileDependency(info.queryFileFilename(), instance->queryBoundActivity());
  1297. return instance->getBoundActivity();
  1298. }
  1299. //---------------------------------------------------------------------------
  1300. ABoundActivity * HqlCppTranslator::doBuildActivityKeyedDistribute(BuildCtx & ctx, IHqlExpression * expr)
  1301. {
  1302. if (!targetThor() || insideChildQuery(ctx))
  1303. return buildCachedActivity(ctx, expr->queryChild(0));
  1304. IHqlExpression * left = expr->queryChild(0);
  1305. IHqlExpression * right = expr->queryChild(1);
  1306. IHqlExpression * indexRecord = right->queryRecord();
  1307. IHqlExpression * seq = querySelSeq(expr);
  1308. JoinSortInfo joinInfo;
  1309. joinInfo.findJoinSortOrders(expr, false);
  1310. if (joinInfo.hasOptionalEqualities())
  1311. throwError(HQLERR_KeyedDistributeNoSubstringJoin);
  1312. unsigned numUnsortedFields = numPayloadFields(right);
  1313. unsigned numKeyedFields = getFlatFieldCount(indexRecord)-numUnsortedFields;
  1314. if (joinInfo.extraMatch || (!expr->hasAttribute(firstAtom) && (joinInfo.queryLeftReq().ordinality() != numKeyedFields)))
  1315. throwError(HQLERR_MustMatchExactly); //Should already be caught in parser
  1316. KeyedJoinInfo info(*this, expr, false);
  1317. info.processFilter();
  1318. Owned<ABoundActivity> boundDataset1 = buildCachedActivity(ctx, left);
  1319. Owned<ActivityInstance> instance = new ActivityInstance(*this, ctx, TAKkeyeddistribute, expr, "KeyedDistribute");
  1320. buildActivityFramework(instance);
  1321. StringBuffer s;
  1322. buildInstancePrefix(instance);
  1323. IHqlExpression * keyFilename = info.queryKeyFilename();
  1324. bool dynamic = hasDynamicFilename(info.queryKey());
  1325. //virtual unsigned getFlags()
  1326. StringBuffer flags;
  1327. if (!keyFilename->isConstant())
  1328. flags.append("|KDFvarindexfilename");
  1329. if (dynamic)
  1330. flags.append("|KDFdynamicindexfilename");
  1331. if (flags.length())
  1332. doBuildUnsignedFunction(instance->classctx, "getFlags", flags.str()+1);
  1333. //virtual const char * getIndexFileName() = 0;
  1334. buildFilenameFunction(*instance, instance->startctx, "getIndexFileName", keyFilename, dynamic);
  1335. //virtual IOutputMetaData * queryIndexRecordSize() = 0; //Excluding fpos and sequence
  1336. buildMetaMember(instance->classctx, info.queryRawKey(), false, "queryIndexRecordSize");
  1337. //virtual void createSegmentMonitors(IIndexReadContext *ctx, const void *lhs) = 0;
  1338. info.buildMonitors(instance->startctx);
  1339. //The comparison is against the raw key entries, so expand out the logical to the physical
  1340. //virtual ICompare * queryCompareRowKey() = 0;
  1341. OwnedHqlExpr expandedIndex = convertToPhysicalIndex(right);
  1342. assertex(expandedIndex->getOperator() == no_newusertable);
  1343. IHqlExpression * rawIndex = expandedIndex->queryChild(0);
  1344. OwnedHqlExpr oldSelector = createSelector(no_activetable, right, seq);
  1345. OwnedHqlExpr newSelector = createSelector(no_activetable, rawIndex, seq);
  1346. TableProjectMapper mapper(expandedIndex);
  1347. HqlExprArray normalizedRight;
  1348. ForEachItemIn(i, joinInfo.queryRightReq())
  1349. {
  1350. IHqlExpression & curRight = joinInfo.queryRightReq().item(i);
  1351. normalizedRight.append(*mapper.expandFields(&curRight, oldSelector, newSelector));
  1352. }
  1353. DatasetReference leftDs(left, no_activetable, seq);
  1354. DatasetReference rightDs(rawIndex, no_activetable, seq);
  1355. doCompareLeftRight(instance->nestedctx, "CompareRowKey", leftDs, rightDs, joinInfo.queryLeftReq(), normalizedRight);
  1356. buildFormatCrcFunction(instance->classctx, "getFormatCrc", info.queryRawKey(), info.queryRawKey(), 1);
  1357. buildSerializedLayoutMember(instance->classctx, indexRecord, "getIndexLayout", numKeyedFields);
  1358. OwnedHqlExpr matchExpr = info.getMatchExpr(true);
  1359. assertex(!matchExpr);
  1360. buildInstanceSuffix(instance);
  1361. buildConnectInputOutput(ctx, instance, boundDataset1, 0, 0);
  1362. Owned<ABoundActivity> whoAmI = instance->getBoundActivity();
  1363. addFileDependency(keyFilename, whoAmI);
  1364. return instance->getBoundActivity();
  1365. }
  1366. //---------------------------------------------------------------------------
  1367. ABoundActivity * HqlCppTranslator::doBuildActivityKeyDiff(BuildCtx & ctx, IHqlExpression * expr, bool isRoot)
  1368. {
  1369. StringBuffer s;
  1370. IHqlExpression * original = expr->queryChild(0);
  1371. IHqlExpression * updated = expr->queryChild(1);
  1372. IHqlExpression * output = expr->queryChild(2);
  1373. Owned<ActivityInstance> instance = new ActivityInstance(*this, ctx, TAKkeydiff, expr, "KeyDiff");
  1374. buildActivityFramework(instance, isRoot);
  1375. buildInstancePrefix(instance);
  1376. //virtual unsigned getFlags() = 0;
  1377. StringBuffer flags;
  1378. if (expr->hasAttribute(overwriteAtom))
  1379. flags.append("|KDPoverwrite");
  1380. else if (expr->hasAttribute(noOverwriteAtom))
  1381. flags.append("|KDPnooverwrite");
  1382. if (!output->isConstant())
  1383. flags.append("|KDPvaroutputname");
  1384. if (expr->hasAttribute(expireAtom))
  1385. flags.append("|KDPexpires");
  1386. if (flags.length())
  1387. doBuildUnsignedFunction(instance->classctx, "getFlags", flags.str()+1);
  1388. //virtual const char * getOriginalName() = 0; // may be null
  1389. buildRefFilenameFunction(*instance, instance->startctx, "getOriginalName", original);
  1390. noteAllFieldsUsed(original);
  1391. //virtual const char * getUpdatedName() = 0;
  1392. buildRefFilenameFunction(*instance, instance->startctx, "getUpdatedName", updated);
  1393. noteAllFieldsUsed(updated);
  1394. //virtual const char * getOutputName() = 0;
  1395. buildFilenameFunction(*instance, instance->startctx, "getOutputName", output, hasDynamicFilename(expr));
  1396. //virtual int getSequence() = 0;
  1397. doBuildSequenceFunc(instance->classctx, querySequence(expr), false);
  1398. buildExpiryHelper(instance->createctx, expr->queryAttribute(expireAtom));
  1399. buildInstanceSuffix(instance);
  1400. return instance->getBoundActivity();
  1401. }
  1402. ABoundActivity * HqlCppTranslator::doBuildActivityKeyPatch(BuildCtx & ctx, IHqlExpression * expr, bool isRoot)
  1403. {
  1404. StringBuffer s;
  1405. IHqlExpression * original = expr->queryChild(0);
  1406. IHqlExpression * patch = expr->queryChild(1);
  1407. IHqlExpression * output = expr->queryChild(2);
  1408. Owned<ActivityInstance> instance = new ActivityInstance(*this, ctx, TAKkeypatch, expr, "KeyPatch");
  1409. buildActivityFramework(instance, isRoot);
  1410. buildInstancePrefix(instance);
  1411. //virtual unsigned getFlags() = 0;
  1412. StringBuffer flags;
  1413. if (expr->hasAttribute(overwriteAtom))
  1414. flags.append("|KDPoverwrite");
  1415. else if (expr->hasAttribute(noOverwriteAtom))
  1416. flags.append("|KDPnooverwrite");
  1417. if (!output->isConstant())
  1418. flags.append("|KDPvaroutputname");
  1419. if (expr->hasAttribute(expireAtom))
  1420. flags.append("|KDPexpires");
  1421. if (flags.length())
  1422. doBuildUnsignedFunction(instance->classctx, "getFlags", flags.str()+1);
  1423. //virtual const char * getOriginalName() = 0;
  1424. buildRefFilenameFunction(*instance, instance->startctx, "getOriginalName", original);
  1425. noteAllFieldsUsed(original);
  1426. //virtual const char * getPatchName() = 0;
  1427. buildFilenameFunction(*instance, instance->startctx, "getPatchName", patch, true);
  1428. //virtual const char * getOutputName() = 0;
  1429. buildFilenameFunction(*instance, instance->startctx, "getOutputName", output, hasDynamicFilename(expr));
  1430. //virtual int getSequence() = 0;
  1431. doBuildSequenceFunc(instance->classctx, querySequence(expr), false);
  1432. buildExpiryHelper(instance->createctx, expr->queryAttribute(expireAtom));
  1433. buildInstanceSuffix(instance);
  1434. return instance->getBoundActivity();
  1435. }
  1436. //---------------------------------------------------------------------------
  1437. IHqlExpression * querySelectorTable(IHqlExpression * expr)
  1438. {
  1439. loop
  1440. {
  1441. IHqlExpression * selector = expr->queryChild(0);
  1442. if (selector->getOperator() != no_select)
  1443. return selector;
  1444. switch (selector->queryType()->getTypeCode())
  1445. {
  1446. case type_row:
  1447. case type_record:
  1448. break;
  1449. default:
  1450. return selector;
  1451. }
  1452. }
  1453. }
  1454. static IHqlExpression * getBlobAttribute(IHqlExpression * ds)
  1455. {
  1456. return createAttribute(blobHelperAtom, LINK(ds->queryNormalizedSelector()));
  1457. }
  1458. IHqlExpression * queryBlobHelper(BuildCtx & ctx, IHqlExpression * select)
  1459. {
  1460. OwnedHqlExpr search = getBlobAttribute(querySelectorTable(select));
  1461. HqlExprAssociation * match = ctx.queryAssociation(search, AssocExpr, NULL);
  1462. if (!match)
  1463. return NULL;
  1464. return match->queryExpr();
  1465. }
  1466. void HqlCppTranslator::associateBlobHelper(BuildCtx & ctx, IHqlExpression * ds, const char * name)
  1467. {
  1468. OwnedHqlExpr search = getBlobAttribute(ds);
  1469. OwnedHqlExpr matched = createVariable(name, ds->getType());
  1470. ctx.associateExpr(search, matched);
  1471. }
  1472. IHqlExpression * HqlCppTranslator::getBlobRowSelector(BuildCtx & ctx, IHqlExpression * expr)
  1473. {
  1474. IHqlExpression * id = expr->queryChild(0);
  1475. IHqlExpression * helper = queryBlobHelper(ctx, id);
  1476. //MORE: Need to clone the dataset attributes. Really they should be included in the type somehow: via modifiers?
  1477. //or give an error if blob used on alien with ref/
  1478. OwnedHqlExpr field = createField(unnamedId, expr->getType(), NULL, NULL);
  1479. HqlExprArray fields;
  1480. fields.append(*LINK(field));
  1481. OwnedHqlExpr record = createRecord(fields);
  1482. OwnedHqlExpr row = createRow(no_anon, LINK(record), createAttribute(_internal_Atom, LINK(id)));
  1483. if (!ctx.queryAssociation(row, AssocRow, NULL))
  1484. {
  1485. Owned<ITypeInfo> rowType = makeReferenceModifier(row->getType());
  1486. OwnedHqlExpr boundRow = ctx.getTempDeclare(rowType, NULL);
  1487. CHqlBoundExpr boundId;
  1488. buildExpr(ctx, id, boundId);
  1489. HqlExprArray args;
  1490. args.append(*LINK(helper));
  1491. args.append(*LINK(boundId.expr));
  1492. OwnedHqlExpr call = bindTranslatedFunctionCall(lookupBlobId, args);
  1493. ctx.addAssign(boundRow, call);
  1494. bindRow(ctx, row, boundRow);
  1495. }
  1496. return createSelectExpr(LINK(row), LINK(field));
  1497. }
  1498. void HqlCppTranslator::doBuildAssignIdToBlob(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
  1499. {
  1500. if (!queryBlobHelper(ctx, expr->queryChild(0)))
  1501. {
  1502. //Do the assignment by building an expression then assigning
  1503. doBuildExprAssign(ctx, target, expr);
  1504. return;
  1505. }
  1506. OwnedHqlExpr select = getBlobRowSelector(ctx, expr);
  1507. buildExprAssign(ctx, target, select);
  1508. }
  1509. void HqlCppTranslator::doBuildExprIdToBlob(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
  1510. {
  1511. if (!queryBlobHelper(ctx, expr->queryChild(0)))
  1512. {
  1513. if (!buildExprInCorrectContext(ctx, expr, tgt, false))
  1514. throwError(HQLERR_BlobTranslationContextNotFound);
  1515. return;
  1516. }
  1517. OwnedHqlExpr select = getBlobRowSelector(ctx, expr);
  1518. buildExpr(ctx, select, tgt);
  1519. }
  1520. IReferenceSelector * HqlCppTranslator::doBuildRowIdToBlob(BuildCtx & ctx, IHqlExpression * expr, bool isNew)
  1521. {
  1522. if (!queryBlobHelper(ctx, expr->queryChild(0)))
  1523. throwError(HQLERR_AccessRowBlobInsideChildQuery);
  1524. OwnedHqlExpr select = getBlobRowSelector(ctx, expr);
  1525. return buildNewOrActiveRow(ctx, select, isNew);
  1526. }
  1527. void HqlCppTranslator::doBuildExprBlobToId(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
  1528. {
  1529. IHqlExpression * value = expr->queryChild(0);
  1530. IHqlExpression * helper = queryBlobHelper(ctx, value);
  1531. assertex(helper);
  1532. Owned<IReferenceSelector> selector = buildReference(ctx, value);
  1533. CHqlBoundExpr boundSize, boundAddress;
  1534. selector->getSize(ctx, boundSize);
  1535. selector->buildAddress(ctx, boundAddress);
  1536. HqlExprArray args;
  1537. args.append(*LINK(helper));
  1538. args.append(*LINK(boundSize.expr));
  1539. args.append(*LINK(boundAddress.expr));
  1540. tgt.expr.setown(bindTranslatedFunctionCall(createBlobId, args));
  1541. }