rtlrecord.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include <math.h>
  15. #include <stdio.h>
  16. #include "jmisc.hpp"
  17. #include "jlib.hpp"
  18. #include "eclhelper.hpp"
  19. #include "eclrtl_imp.hpp"
  20. #include "rtlds_imp.hpp"
  21. #include "rtlrecord.hpp"
  22. #include "rtldynfield.hpp"
  23. /*
  24. * Potential different implementations (for all fixed size has no penalty):
  25. *
  26. * a. when row changes, update offsets of all offset fields
  27. * + access is simple, single array lookup
  28. * - lots more fields are updated even if the fields aren't used.
  29. *
  30. * b. when a row changes update the next offset for all variable size fields.
  31. * + minimal updates
  32. * - offset access is more complex
  33. *
  34. * c. when row changes clear cache, and calculate on demand.
  35. * + trivial update on row change
  36. * + no cost of fields not accessed
  37. * + may be required to implement ifblocks - since fields in the test expression must be evaluated before all the
  38. * offsets are known. Depends on the implementation of ifblocks!
  39. * - accessing an offset will involve a loop and be more expensive
  40. *
  41. * Handling complications:
  42. * a. Nested rows
  43. * A dilemma. Should they be expanded?
  44. * + If they are expanded then it makes it much simpler to use externally.
  45. * - The nested fields have compound names storing and matching them is a complication.
  46. * - The code generator doesn't expand.
  47. * + It is easier to calculate offsets since all fields are supported by the same class.
  48. * + Sizes for unexpanded rows would need their own size caching classes. That is more complex!
  49. * + The meta information currently processes child information.
  50. *
  51. * Not expanding is complicated if you also try and only calculate the offsets of fields in nested records once - since you end
  52. * up needing to call back and forth between instances and the static information.
  53. * However, if nested records are processed by using size(), and selections from them are processed by instantiating
  54. * an instance of the nested row it is all much simpler. The cost is potentially re-evaluating sizes of nested fields. Potentially
  55. * inefficient if most are fixed size, but some are variable.
  56. *
  57. * b. bitfields
  58. * the last bitfield in a bitfield container has the size of the container, the others have 0 size.
  59. *
  60. * c. ifblocks
  61. * A field in an ifblock can be viewed as a variable size field - size is either 0 or normal size
  62. * depending on the condition in question. We add a flag and a pointer to the ifblock info into
  63. * each contained field. Only properly supported in expanded mode.
  64. * Might be cleanest to have a different derived implementation which was used if the record
  65. * contained ifblocks, though this would mean we would need to make some functions virtual or else move
  66. * the responsibility for knowing about ifblocks out to all customers?
  67. * Added calls to check ifblocks before size()/getValue() etc. will require an extra row parameter
  68. * to every RtlTypeInfo::getX() function? Not if it is required that the offsets have been set first.
  69. * Evaluating the test expression without compiling will require expression interpreting.
  70. *
  71. * d. alien datatypes
  72. * As long as the rtlField class implements the functions then it shouldn't cause any problems. Evaluating at
  73. * from a record at runtime without compiling will be tricky - requires an interpreter.
  74. *
  75. * Other
  76. * Add a minSize to each field (unless already stored in the record information)
  77. *
  78. * Expression interpreting:
  79. * Replace the no_select with a CHqlBoundExpr(no_select, fieldid).
  80. * Evaluate (ctx [ logical->RtlFieldOffsetCalculator mapping ]).
  81. * Even better if mapped direct to something that represents the base cursor so no need to search ctx.
  82. * For nested selects the code would need to be consistent.
  83. */
  84. static unsigned countFields(const RtlFieldInfo * const * fields, bool & containsNested, unsigned &numIfBlocks)
  85. {
  86. unsigned cnt = 0;
  87. for (;*fields;fields++)
  88. {
  89. const RtlTypeInfo * type = (*fields)->type;
  90. if (type->getType() == type_record || type->getType()==type_ifblock)
  91. {
  92. containsNested = true;
  93. if (type->getType()==type_ifblock)
  94. numIfBlocks++;
  95. const RtlFieldInfo * const * nested = type->queryFields();
  96. if (nested)
  97. cnt += countFields(nested, containsNested, numIfBlocks);
  98. }
  99. else
  100. cnt++;
  101. }
  102. return cnt;
  103. }
  104. class IfBlockInfo
  105. {
  106. public:
  107. IfBlockInfo(const RtlFieldInfo &_field, const IfBlockInfo *_parent, unsigned _idx, unsigned _startIdx, unsigned _prevFields)
  108. : field(_field), parent(_parent), idx(_idx), startIdx(_startIdx), prevFields(_prevFields)
  109. {
  110. }
  111. bool excluded(const byte *row, byte *conditions) const
  112. {
  113. if (conditions[idx]==2)
  114. {
  115. if (parent && parent->excluded(row, conditions))
  116. conditions[idx] = 0;
  117. else
  118. {
  119. const RtlIfBlockTypeInfo *cond = static_cast<const RtlIfBlockTypeInfo *>(field.type);
  120. conditions[idx] = cond->getCondition(row) ? 1 : 0;
  121. }
  122. }
  123. return conditions[idx]==0;
  124. }
  125. inline unsigned queryStartField() const { return startIdx; }
  126. inline unsigned numPrevFields() const { return prevFields; }
  127. inline bool matchIfBlock(const RtlIfBlockTypeInfo * ifblock) const { return ifblock == field.type; }
  128. private:
  129. const RtlFieldInfo &field;
  130. const IfBlockInfo *parent = nullptr; // for nested ifblocks
  131. unsigned idx;
  132. unsigned startIdx; // For ifblocks inside child records
  133. unsigned prevFields; // number of fields before the if block
  134. };
  135. class RtlCondFieldStrInfo : public RtlFieldStrInfo
  136. {
  137. public:
  138. RtlCondFieldStrInfo(const RtlFieldInfo &from, const IfBlockInfo &_ifblock)
  139. : RtlFieldStrInfo(from.name, from.xpath, from.type, from.flags | (RFTMinifblock|RFTMdynamic), (const char *) from.initializer),
  140. origField(from),ifblock(_ifblock)
  141. {
  142. }
  143. public:
  144. const RtlFieldInfo &origField;
  145. const IfBlockInfo &ifblock;
  146. };
  147. static unsigned expandNestedRows(unsigned idx, unsigned startIdx, const char *prefix, const RtlFieldInfo * const * fields, const RtlFieldInfo * * target, const char * *names, const IfBlockInfo *inIfBlock, ConstPointerArrayOf<IfBlockInfo> &ifblocks)
  148. {
  149. for (;*fields;fields++)
  150. {
  151. const RtlFieldInfo * cur = *fields;
  152. const RtlTypeInfo * type = cur->type;
  153. bool isIfBlock = type->getType()==type_ifblock;
  154. if (isIfBlock || type->getType() == type_record)
  155. {
  156. const IfBlockInfo *nestIfBlock = inIfBlock;
  157. if (isIfBlock)
  158. {
  159. nestIfBlock = new IfBlockInfo(*cur, inIfBlock, ifblocks.ordinality(), startIdx, idx);
  160. ifblocks.append(nestIfBlock);
  161. }
  162. const RtlFieldInfo * const * nested = type->queryFields();
  163. if (nested)
  164. {
  165. StringBuffer newPrefix(prefix);
  166. if (cur->name && *cur->name)
  167. newPrefix.append(cur->name).append('.');
  168. idx = expandNestedRows(idx, isIfBlock ? startIdx : idx, newPrefix.str(), nested, target, names, nestIfBlock, ifblocks);
  169. }
  170. }
  171. else
  172. {
  173. if (prefix)
  174. {
  175. StringBuffer name(prefix);
  176. name.append(cur->name);
  177. names[idx] = name.detach();
  178. }
  179. else
  180. names[idx] = nullptr;
  181. if (inIfBlock && !(cur->flags & RFTMinifblock))
  182. target[idx++] = new RtlCondFieldStrInfo(*cur, *inIfBlock);
  183. else
  184. {
  185. dbgassertex((cur->flags & RFTMdynamic) == 0);
  186. target[idx++] = cur;
  187. }
  188. }
  189. }
  190. return idx;
  191. }
  192. class FieldNameToFieldNumMap
  193. {
  194. public:
  195. FieldNameToFieldNumMap(const RtlRecord &record) : map(true)
  196. {
  197. unsigned numFields = record.getNumFields();
  198. for (unsigned idx = 0; idx < numFields;idx++)
  199. map.setValue(record.queryName(idx), idx);
  200. }
  201. unsigned lookup(const char *name) const
  202. {
  203. unsigned *result = map.getValue(name);
  204. if (result)
  205. return *result;
  206. else
  207. return (unsigned) -1;
  208. }
  209. MapConstStringTo<unsigned> map; // Note - does not copy strings - they should all have sufficient lifetime
  210. };
  211. RtlRecord::RtlRecord(const RtlRecordTypeInfo & record, bool expandFields)
  212. : RtlRecord(record.fields, expandFields) // delegated constructor
  213. {
  214. }
  215. RtlRecord::RtlRecord(const RtlFieldInfo * const *_fields, bool expandFields) : fields(_fields), originalFields(_fields), names(nullptr), nameMap(nullptr)
  216. {
  217. numVarFields = 0;
  218. numTables = 0;
  219. numIfBlocks = 0;
  220. ifblocks = nullptr;
  221. //Optionally expand out nested rows.
  222. if (expandFields)
  223. {
  224. bool containsNested = false;
  225. numFields = countFields(fields, containsNested, numIfBlocks);
  226. if (containsNested)
  227. {
  228. ConstPointerArrayOf<IfBlockInfo> _ifblocks;
  229. const RtlFieldInfo * * allocated = new const RtlFieldInfo * [numFields+1];
  230. names = new const char *[numFields];
  231. fields = allocated;
  232. unsigned idx = expandNestedRows(0, 0, nullptr, originalFields, allocated, names, nullptr, _ifblocks);
  233. ifblocks = _ifblocks.detach();
  234. assertex(idx == numFields);
  235. allocated[idx] = nullptr;
  236. }
  237. }
  238. else
  239. {
  240. numFields = countFields(fields);
  241. }
  242. for (unsigned i=0; i < numFields; i++)
  243. {
  244. const RtlTypeInfo *curType = queryType(i);
  245. if (!curType->isFixedSize() || (fields[i]->flags & RFTMinifblock))
  246. numVarFields++;
  247. if (curType->getType()==type_table || curType->getType()==type_record || curType->getType()==type_dictionary)
  248. numTables++;
  249. }
  250. fixedOffsets = new size_t[numFields + 1];
  251. whichVariableOffset = new unsigned[numFields + 1];
  252. variableFieldIds = new unsigned[numVarFields];
  253. if (numTables)
  254. {
  255. nestedTables = new const RtlRecord *[numTables];
  256. tableIds = new unsigned[numTables];
  257. }
  258. else
  259. {
  260. nestedTables = nullptr;
  261. tableIds = nullptr;
  262. }
  263. unsigned curVariable = 0;
  264. unsigned curTable = 0;
  265. size_t fixedOffset = 0;
  266. for (unsigned i=0;; i++)
  267. {
  268. whichVariableOffset[i] = curVariable;
  269. fixedOffsets[i] = fixedOffset;
  270. if (i == numFields)
  271. break;
  272. const RtlTypeInfo * curType = queryType(i);
  273. if (curType->isFixedSize() && !(fields[i]->flags & RFTMinifblock))
  274. {
  275. size_t thisSize = curType->size(nullptr, nullptr);
  276. fixedOffset += thisSize;
  277. }
  278. else
  279. {
  280. variableFieldIds[curVariable] = i;
  281. curVariable++;
  282. fixedOffset = 0;
  283. }
  284. switch (curType->getType())
  285. {
  286. case type_table:
  287. case type_dictionary:
  288. tableIds[curTable] = i;
  289. nestedTables[curTable++] = new RtlRecord(curType->queryChildType()->queryFields(), expandFields);
  290. break;
  291. case type_record:
  292. tableIds[curTable] = i;
  293. nestedTables[curTable++] = new RtlRecord(curType->queryFields(), expandFields);
  294. break;
  295. }
  296. }
  297. //Zero length records cause problems (allocation, and indicating if skipped) => force the length to 1 byte so it is possible to tell
  298. if (numFields == 0)
  299. fixedOffsets[0] = 1;
  300. }
  301. RtlRecord::~RtlRecord()
  302. {
  303. if (names)
  304. {
  305. for (unsigned i = 0; i < numFields; i++)
  306. {
  307. free((char *) names[i]);
  308. }
  309. delete [] names;
  310. }
  311. if (fields != originalFields)
  312. {
  313. for (const RtlFieldInfo * const * finger = fields; *finger; finger++)
  314. {
  315. const RtlFieldInfo *thisField = *finger;
  316. if (thisField->flags & RFTMdynamic)
  317. delete thisField;
  318. }
  319. delete [] fields;
  320. }
  321. if (ifblocks)
  322. {
  323. for (unsigned i = 0; i < numIfBlocks; i++)
  324. {
  325. delete(ifblocks[i]);
  326. }
  327. //following as allocated as a ConstPointerArrayOf<IfBlockInfo>, rather than new []
  328. free(ifblocks);
  329. }
  330. delete [] fixedOffsets;
  331. delete [] whichVariableOffset;
  332. delete [] variableFieldIds;
  333. delete [] tableIds;
  334. if (nestedTables)
  335. {
  336. for (unsigned i = 0; i < numTables; i++)
  337. delete nestedTables[i];
  338. delete [] nestedTables;
  339. }
  340. delete nameMap;
  341. }
  342. unsigned RtlRecord::getNumKeyedFields() const
  343. {
  344. unsigned ret = 0;
  345. for (const RtlFieldInfo * const * finger = originalFields; *finger; finger++)
  346. {
  347. if ((*finger)->flags & RFTMispayloadfield)
  348. break;
  349. ret++;
  350. }
  351. return ret;
  352. }
  353. void RtlRecord::calcRowOffsets(size_t * variableOffsets, const void * _row, unsigned numFieldsUsed) const
  354. {
  355. const byte * row = static_cast<const byte *>(_row);
  356. unsigned maxVarField = (numFieldsUsed>=numFields) ? numVarFields : whichVariableOffset[numFieldsUsed];
  357. if (numIfBlocks)
  358. {
  359. byte *conditions = (byte *) alloca(numIfBlocks * sizeof(byte));
  360. memset(conditions, 2, numIfBlocks); // Meaning condition not yet calculated
  361. for (unsigned i = 0; i < maxVarField; i++)
  362. {
  363. unsigned fieldIndex = variableFieldIds[i];
  364. const RtlFieldInfo *field = fields[fieldIndex];
  365. size_t offset = getOffset(variableOffsets, fieldIndex);
  366. if (field->flags & RFTMinifblock)
  367. {
  368. const RtlCondFieldStrInfo *condfield = static_cast<const RtlCondFieldStrInfo *>(field);
  369. unsigned startField = condfield->ifblock.queryStartField();
  370. const byte *childRow = row;
  371. if (startField)
  372. childRow += getOffset(variableOffsets, startField);
  373. if (condfield->ifblock.excluded(childRow, conditions))
  374. {
  375. variableOffsets[i+1] = offset; // (meaning size ends up as zero);
  376. continue;
  377. }
  378. }
  379. size_t fieldSize = queryType(fieldIndex)->size(row + offset, row);
  380. variableOffsets[i+1] = offset+fieldSize;
  381. }
  382. }
  383. else
  384. {
  385. size32_t varoffset = 0;
  386. for (unsigned i = 0; i < maxVarField; i++)
  387. {
  388. unsigned fieldIndex = variableFieldIds[i];
  389. size32_t offset = fixedOffsets[fieldIndex] + varoffset;
  390. size32_t fieldSize = queryType(fieldIndex)->size(row + offset, row);
  391. varoffset = offset+fieldSize;
  392. variableOffsets[i+1] = varoffset;
  393. }
  394. }
  395. #ifdef _DEBUG
  396. for (unsigned i = maxVarField; i < numVarFields; i++)
  397. {
  398. variableOffsets[i+1] = 0x7fffffff;
  399. }
  400. #endif
  401. }
  402. size32_t RtlRecord::getMinRecordSize() const
  403. {
  404. if (numVarFields == 0)
  405. return fixedOffsets[numFields];
  406. size32_t minSize = 0;
  407. for (unsigned i=0; i < numFields; i++)
  408. {
  409. const RtlFieldInfo *field = fields[i];
  410. if (!(field->flags & RFTMinifblock))
  411. minSize += queryType(i)->getMinSize();
  412. }
  413. return minSize;
  414. }
  415. size32_t RtlRecord::deserialize(ARowBuilder & rowBuilder, IRowDeserializerSource & in) const
  416. {
  417. size32_t offset = 0;
  418. byte *conditionValues = (byte *) alloca(numIfBlocks);
  419. memset(conditionValues, 2, numIfBlocks);
  420. for (unsigned i = 0; i < numVarFields; i++)
  421. {
  422. unsigned fieldIndex = variableFieldIds[i];
  423. const RtlFieldInfo *field = fields[fieldIndex];
  424. size32_t fixedSize = fixedOffsets[fieldIndex];
  425. byte * self = rowBuilder.ensureCapacity(offset + fixedSize, ""); // Why not field->name?
  426. in.read(fixedSize, self + offset);
  427. if (excluded(field, self, conditionValues))
  428. continue;
  429. offset = queryType(fieldIndex)->deserialize(rowBuilder, in, offset + fixedSize);
  430. }
  431. size32_t lastFixedSize = fixedOffsets[numFields];
  432. byte * self = rowBuilder.ensureCapacity(offset + lastFixedSize, "");
  433. in.read(lastFixedSize, self + offset);
  434. return offset + lastFixedSize;
  435. }
  436. void RtlRecord::readAhead(IRowPrefetcherSource & in) const
  437. {
  438. // Note - this should not and can not be used when ifblocks present - canprefetch flag should take care of that.
  439. if (numIfBlocks==0)
  440. {
  441. for (unsigned i=0; i < numVarFields; i++)
  442. {
  443. unsigned fieldIndex = variableFieldIds[i];
  444. in.skip(fixedOffsets[fieldIndex]);
  445. queryType(fieldIndex)->readAhead(in);
  446. }
  447. in.skip(fixedOffsets[numFields]);
  448. }
  449. else
  450. {
  451. const RtlFieldInfo * const * field;
  452. for (field = originalFields; *field; field++)
  453. (*field)->type->readAhead(in);
  454. }
  455. }
  456. int RtlRecord::compare(const byte * left, const byte * right) const
  457. {
  458. //Use originalFields so that ifblocks are processed correctly
  459. return compareFields(originalFields, left, right, false);
  460. }
  461. unsigned RtlRecord::queryIfBlockLimit(const RtlIfBlockTypeInfo * ifblock) const
  462. {
  463. for (unsigned i=0; i < numIfBlocks; i++)
  464. {
  465. if (ifblocks[i]->matchIfBlock(ifblock))
  466. return ifblocks[i]->numPrevFields();
  467. }
  468. throwUnexpected();
  469. }
  470. static const FieldNameToFieldNumMap *setupNameMap(const RtlRecord &record, std::atomic<const FieldNameToFieldNumMap *> &aNameMap)
  471. {
  472. const FieldNameToFieldNumMap *lnameMap = new FieldNameToFieldNumMap(record);
  473. const FieldNameToFieldNumMap *expected = nullptr;
  474. if (aNameMap.compare_exchange_strong(expected, lnameMap))
  475. return lnameMap;
  476. else
  477. {
  478. // Other thread already set it while we were creating
  479. delete lnameMap;
  480. return expected; // has been updated to the value set by other thread
  481. }
  482. }
  483. unsigned RtlRecord::getFieldNum(const char *fieldName) const
  484. {
  485. // NOTE: the nameMap field cannot be declared as atomic, since the class definition is included in generated
  486. // code which is not (yet) compiled using C++11. If that changes then the reinterpret_cast can be removed.
  487. std::atomic<const FieldNameToFieldNumMap *> &aNameMap = reinterpret_cast<std::atomic<const FieldNameToFieldNumMap *> &>(nameMap);
  488. const FieldNameToFieldNumMap *useMap = aNameMap.load(std::memory_order_relaxed);
  489. if (!useMap)
  490. useMap = setupNameMap(*this, aNameMap);
  491. return useMap->lookup(fieldName);
  492. }
  493. const char *RtlRecord::queryName(unsigned field) const
  494. {
  495. if (names && names[field])
  496. return names[field];
  497. return fields[field]->name;
  498. }
  499. const RtlRecord *RtlRecord::queryNested(unsigned fieldId) const
  500. {
  501. // Map goes in wrong direction (for size reasons). We could replace with a hashtable or binsearch but
  502. // should not be enough nested tables for it to be worth it;
  503. for (unsigned i = 0; i < numTables; i++)
  504. if (tableIds[i]==fieldId)
  505. return nestedTables[i];
  506. return nullptr;
  507. }
  508. const RtlFieldInfo *RtlRecord::queryOriginalField(unsigned idx) const
  509. {
  510. const RtlFieldInfo *field = queryField(idx);
  511. if (field->flags & RFTMdynamic)
  512. return &static_cast<const RtlCondFieldStrInfo *>(field)->origField;
  513. else
  514. return field;
  515. }
  516. bool RtlRecord::excluded(const RtlFieldInfo *field, const byte *row, byte *conditionValues) const
  517. {
  518. if (!field->omitable())
  519. return false;
  520. const RtlCondFieldStrInfo *condfield = static_cast<const RtlCondFieldStrInfo *>(field);
  521. unsigned startField = condfield->ifblock.queryStartField();
  522. const byte *childRow = row;
  523. if (startField)
  524. childRow += calculateOffset(row, startField);
  525. return condfield->ifblock.excluded(childRow, conditionValues);
  526. }
  527. size_t RtlRecord::getFixedOffset(unsigned field) const
  528. {
  529. assert(isFixedOffset(field));
  530. return fixedOffsets[field];
  531. }
  532. bool RtlRecord::isFixedOffset(unsigned field) const
  533. {
  534. return (whichVariableOffset[field]==0);
  535. }
  536. size32_t RtlRecord::getRecordSize(const void *_row) const
  537. {
  538. if (numIfBlocks)
  539. {
  540. unsigned numOffsets = getNumVarFields() + 1;
  541. size_t * variableOffsets = (size_t *)alloca(numOffsets * sizeof(size_t));
  542. RtlRow sourceRow(*this, _row, numOffsets, variableOffsets);
  543. return sourceRow.getOffset(numFields+1);
  544. }
  545. else
  546. {
  547. size32_t size = getFixedSize();
  548. if (!size)
  549. {
  550. const byte * row = static_cast<const byte *>(_row);
  551. size32_t varoffset = 0;
  552. for (unsigned i = 0; i < numVarFields; i++)
  553. {
  554. unsigned fieldIndex = variableFieldIds[i];
  555. size32_t offset = fixedOffsets[fieldIndex] + varoffset;
  556. size32_t fieldSize = queryType(fieldIndex)->size(row + offset, row);
  557. varoffset = offset + fieldSize;
  558. }
  559. size = fixedOffsets[numFields] + varoffset;
  560. }
  561. return size;
  562. }
  563. }
  564. size32_t RtlRecord::calculateOffset(const void *_row, unsigned field) const
  565. {
  566. if (numIfBlocks)
  567. {
  568. unsigned numOffsets = getNumVarFields() + 1;
  569. size_t * variableOffsets = (size_t *)alloca(numOffsets * sizeof(size_t));
  570. RtlRow sourceRow(*this, nullptr, numOffsets, variableOffsets);
  571. sourceRow.setRow(_row, field);
  572. return sourceRow.getOffset(field);
  573. }
  574. else
  575. {
  576. const byte * row = static_cast<const byte *>(_row);
  577. size32_t varoffset = 0;
  578. unsigned varFields = whichVariableOffset[field];
  579. for (unsigned i = 0; i < varFields; i++)
  580. {
  581. unsigned fieldIndex = variableFieldIds[i];
  582. size32_t offset = fixedOffsets[fieldIndex] + varoffset;
  583. size32_t fieldSize = queryType(fieldIndex)->size(row + offset, row);
  584. varoffset = offset + fieldSize;
  585. }
  586. return fixedOffsets[field] + varoffset;
  587. }
  588. }
  589. //---------------------------------------------------------------------------------------------------------------------
  590. RtlRow::RtlRow(const RtlRecord & _info, const void * optRow, unsigned numOffsets, size_t * _variableOffsets) : info(_info), variableOffsets(_variableOffsets)
  591. {
  592. assertex(numOffsets == info.getNumVarFields()+1);
  593. //variableOffset[0] is used for all fixed offset fields to avoid any special casing.
  594. variableOffsets[0] = 0;
  595. setRow(optRow);
  596. }
  597. size_t RtlRow::noVariableOffsets [1] = {0};
  598. RtlRow::RtlRow(const RtlRecord & _info, const void *_row) : info(_info), variableOffsets(noVariableOffsets)
  599. {
  600. row = (const byte *)_row;
  601. }
  602. __int64 RtlRow::getInt(unsigned field) const
  603. {
  604. const byte * self = reinterpret_cast<const byte *>(row);
  605. const RtlFieldInfo *fieldInfo = info.queryField(field);
  606. const RtlTypeInfo * type = fieldInfo->type;
  607. if (!fieldInfo->omitable() || getSize(field))
  608. return type->getInt(self + getOffset(field));
  609. else if (fieldInfo->initializer && !isVirtualInitializer(fieldInfo->initializer))
  610. return type->getInt(fieldInfo->initializer);
  611. else
  612. return 0;
  613. }
  614. double RtlRow::getReal(unsigned field) const
  615. {
  616. const byte * self = reinterpret_cast<const byte *>(row);
  617. const RtlFieldInfo *fieldInfo = info.queryField(field);
  618. const RtlTypeInfo * type = fieldInfo->type;
  619. if (!fieldInfo->omitable() || getSize(field))
  620. return type->getReal(self + getOffset(field));
  621. else if (fieldInfo->initializer && !isVirtualInitializer(fieldInfo->initializer))
  622. return type->getReal(fieldInfo->initializer);
  623. else
  624. return 0;
  625. }
  626. void RtlRow::getString(size32_t & resultLen, char * & result, unsigned field) const
  627. {
  628. const byte * self = reinterpret_cast<const byte *>(row);
  629. const RtlFieldInfo *fieldInfo = info.queryField(field);
  630. const RtlTypeInfo * type = fieldInfo->type;
  631. if (!fieldInfo->omitable() || getSize(field))
  632. type->getString(resultLen, result, self + getOffset(field));
  633. else if (fieldInfo->initializer && !isVirtualInitializer(fieldInfo->initializer))
  634. type->getString(resultLen, result, fieldInfo->initializer);
  635. else
  636. {
  637. resultLen = 0;
  638. result = nullptr;
  639. }
  640. }
  641. void RtlRow::getUtf8(size32_t & resultLen, char * & result, unsigned field) const
  642. {
  643. const byte * self = reinterpret_cast<const byte *>(row);
  644. const RtlFieldInfo *fieldInfo = info.queryField(field);
  645. const RtlTypeInfo * type = fieldInfo->type;
  646. if (!fieldInfo->omitable() || getSize(field))
  647. type->getUtf8(resultLen, result, self + getOffset(field));
  648. else if (fieldInfo->initializer && !isVirtualInitializer(fieldInfo->initializer))
  649. type->getUtf8(resultLen, result, fieldInfo->initializer);
  650. else
  651. {
  652. resultLen = 0;
  653. result = nullptr;
  654. }
  655. }
  656. void RtlRow::setRow(const void * _row, unsigned _numFieldsUsed)
  657. {
  658. row = (const byte *)_row;
  659. if (_row)
  660. {
  661. numFieldsUsed = _numFieldsUsed;
  662. if (numFieldsUsed)
  663. info.calcRowOffsets(variableOffsets, _row, _numFieldsUsed);
  664. #if defined(_DEBUG) && defined(TRACE_ROWOFFSETS)
  665. for (unsigned i = 0; i < info.getNumFields() && i < numFieldsUsed; i++)
  666. {
  667. printf("Field %d (%s) offset %d", i, info.queryName(i), (int) getOffset(i));
  668. if (getSize(i))
  669. {
  670. unsigned bufflen;
  671. rtlDataAttr buff;
  672. getString(bufflen, buff.refstr(), i);
  673. printf(" value %.*s", bufflen, buff.getstr());
  674. }
  675. printf("\n");
  676. }
  677. #endif
  678. }
  679. else
  680. numFieldsUsed = 0;
  681. }
  682. void RtlRow::lazyCalcOffsets(unsigned _numFieldsUsed) const
  683. {
  684. // This is a little iffy as it's not really const - but it clears up a lot of other code if you
  685. // treat it as if it is. Logically it kind-of is, in that we are doing lazy-evaluation of the
  686. // offsets but logically we are not creating information here.
  687. // Another alternative would be to do the lazy eval in getOffset/getRecordSize ?
  688. assert(row);
  689. if (_numFieldsUsed > numFieldsUsed)
  690. {
  691. info.calcRowOffsets(variableOffsets, row, _numFieldsUsed); // MORE - could be optimized to only calc ones not previously calculated
  692. numFieldsUsed = _numFieldsUsed;
  693. }
  694. }
  695. RtlFixedRow::RtlFixedRow(const RtlRecord & _info, const void *_row, unsigned _numFieldsUsed)
  696. : RtlRow(_info, _row)
  697. {
  698. numFieldsUsed = _numFieldsUsed;
  699. dbgassertex(info.isFixedOffset(numFieldsUsed));
  700. }
  701. RtlDynRow::RtlDynRow(const RtlRecord & _info, const void * optRow) : RtlRow(_info, optRow, _info.getNumVarFields()+1, new size_t[_info.getNumVarFields()+1])
  702. {
  703. }
  704. RtlDynRow::~RtlDynRow()
  705. {
  706. delete [] variableOffsets;
  707. }
  708. //---------------------------------------------------------------------------------------------------------------------
  709. class CDefaultDeserializer : public CInterfaceOf<IOutputRowDeserializer>
  710. {
  711. public:
  712. CDefaultDeserializer(const RtlRecord & _record) : record(_record) {}
  713. virtual size32_t deserialize(ARowBuilder & rowBuilder, IRowDeserializerSource & in)
  714. {
  715. return record.deserialize(rowBuilder, in);
  716. }
  717. private:
  718. const RtlRecord & record;
  719. };
  720. class CDefaultPrefetcher : public CInterfaceOf<ISourceRowPrefetcher>
  721. {
  722. public:
  723. CDefaultPrefetcher(const RtlRecord & _record) : record(_record) {}
  724. virtual void readAhead(IRowPrefetcherSource & in)
  725. {
  726. record.readAhead(in);
  727. }
  728. private:
  729. const RtlRecord & record;
  730. };
  731. //-----------------
  732. static const RtlRecord *setupRecordAccessor(const COutputMetaData &meta, bool expand, std::atomic<const RtlRecord *> &aRecordAccessor)
  733. {
  734. const RtlRecord *lRecordAccessor = new RtlRecord(meta.queryTypeInfo()->queryFields(), expand);
  735. const RtlRecord *expected = nullptr;
  736. if (aRecordAccessor.compare_exchange_strong(expected, lRecordAccessor))
  737. return lRecordAccessor;
  738. else
  739. {
  740. // Other thread already set it while we were creating
  741. delete lRecordAccessor;
  742. return expected; // has been updated to the value set by other thread
  743. }
  744. }
  745. COutputMetaData::COutputMetaData()
  746. {
  747. recordAccessor[0] = recordAccessor[1] = NULL;
  748. }
  749. COutputMetaData::~COutputMetaData()
  750. {
  751. delete recordAccessor[0]; delete recordAccessor[1];
  752. }
  753. const RtlRecord &COutputMetaData::queryRecordAccessor(bool expand) const
  754. {
  755. // NOTE: the recordAccessor field cannot be declared as atomic, since the class definition is included in generated
  756. // code which does not include <atomic>. If that changes then the reinterpret_cast can be removed.
  757. std::atomic<const RtlRecord *> &aRecordAccessor = reinterpret_cast<std::atomic<const RtlRecord *> &>(recordAccessor[expand]);
  758. const RtlRecord *useAccessor = aRecordAccessor.load(std::memory_order_relaxed);
  759. if (!useAccessor)
  760. useAccessor = setupRecordAccessor(*this, expand, aRecordAccessor);
  761. return *useAccessor;
  762. }
  763. size32_t COutputMetaData::getRecordSize(const void * data)
  764. {
  765. return queryRecordAccessor(true).getRecordSize(data);
  766. }
  767. class CVariableOutputRowSerializer : public COutputRowSerializer
  768. {
  769. public:
  770. inline CVariableOutputRowSerializer(unsigned _activityId, IOutputMetaData * _meta) : COutputRowSerializer(_activityId) { meta = _meta; }
  771. virtual void serialize(IRowSerializerTarget & out, const byte * self)
  772. {
  773. unsigned size = meta->getRecordSize(self);
  774. out.put(size, self);
  775. }
  776. protected:
  777. IOutputMetaData * meta;
  778. };
  779. //---------------------------------------------------------------------------
  780. IOutputRowSerializer * COutputMetaData::createDiskSerializer(ICodeContext * ctx, unsigned activityId)
  781. {
  782. return new CVariableOutputRowSerializer(activityId, this);
  783. }
  784. ISourceRowPrefetcher * COutputMetaData::createDiskPrefetcher()
  785. {
  786. ISourceRowPrefetcher * fetcher = defaultCreateDiskPrefetcher();
  787. if (fetcher)
  788. return fetcher;
  789. return new CDefaultPrefetcher(queryRecordAccessor(true));
  790. }
  791. IOutputRowDeserializer *COutputMetaData::createDiskDeserializer(ICodeContext * ctx, unsigned activityId)
  792. {
  793. return new CDefaultDeserializer(queryRecordAccessor(true));
  794. }
  795. ISourceRowPrefetcher *COutputMetaData::defaultCreateDiskPrefetcher()
  796. {
  797. if (getMetaFlags() & MDFneedserializedisk)
  798. return querySerializedDiskMeta()->createDiskPrefetcher();
  799. CSourceRowPrefetcher * fetcher = doCreateDiskPrefetcher();
  800. if (fetcher)
  801. {
  802. fetcher->onCreate();
  803. return fetcher;
  804. }
  805. return NULL;
  806. }
  807. IOutputRowSerializer *CFixedOutputMetaData::createDiskSerializer(ICodeContext * ctx, unsigned activityId)
  808. {
  809. return new CFixedOutputRowSerializer(activityId, fixedSize);
  810. }
  811. IOutputRowDeserializer *CFixedOutputMetaData::createDiskDeserializer(ICodeContext * ctx, unsigned activityId)
  812. {
  813. return new CFixedOutputRowDeserializer(activityId, fixedSize);
  814. }
  815. ISourceRowPrefetcher *CFixedOutputMetaData::createDiskPrefetcher()
  816. {
  817. ISourceRowPrefetcher * fetcher = defaultCreateDiskPrefetcher();
  818. if (fetcher)
  819. return fetcher;
  820. return new CFixedSourceRowPrefetcher(fixedSize);
  821. }
  822. IOutputRowSerializer * CActionOutputMetaData::createDiskSerializer(ICodeContext * ctx, unsigned activityId)
  823. {
  824. return new CFixedOutputRowSerializer(activityId, 0);
  825. }
  826. IOutputRowDeserializer * CActionOutputMetaData::createDiskDeserializer(ICodeContext * ctx, unsigned activityId)
  827. {
  828. return new CFixedOutputRowDeserializer(activityId, 0);
  829. }
  830. ISourceRowPrefetcher * CActionOutputMetaData::createDiskPrefetcher()
  831. {
  832. return new CFixedSourceRowPrefetcher(0);
  833. }