hqlckey.cpp 65 KB

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