layouttrans.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  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 "layouttrans.ipp"
  15. //MORE: handle ifblocks
  16. //MORE: handle non-trivial field translations (for expandable types)
  17. char const * const scopeSeparator = ".";
  18. static _ATOM internalFposAtom;
  19. MODULE_INIT(INIT_PRIORITY_STANDARD)
  20. {
  21. internalFposAtom = createAtom("__internal_fpos__");
  22. return true;
  23. }
  24. RLTFailure * RLTFailure::appendScopeDesc(char const * scope)
  25. {
  26. if(scope)
  27. detail.append(" in ").append(scope);
  28. return this;
  29. }
  30. RLTFailure * RLTFailure::appendFieldName(char const * scope, IDefRecordElement const * field)
  31. {
  32. if(scope)
  33. detail.append(scope).append(scopeSeparator);
  34. detail.append(field->queryName()->str());
  35. return this;
  36. }
  37. RLTFailure * makeFailure(IRecordLayoutTranslator::Failure::Code code)
  38. {
  39. return new RLTFailure(code);
  40. }
  41. FieldSearcher::FieldSearcher(IDefRecordElement const * elem)
  42. {
  43. unsigned num = elem->numChildren();
  44. tab.reinit(num + num/2);
  45. for(unsigned i=0; i<num; ++i)
  46. tab.setValue(elem->queryChild(i)->queryName(), i);
  47. }
  48. bool FieldSearcher::search(_ATOM search, unsigned & pos) const
  49. {
  50. unsigned * ret = tab.getValue(search);
  51. if(ret)
  52. {
  53. pos = *ret;
  54. return true;
  55. }
  56. return false;
  57. }
  58. MappingLevel::MappingLevel(FieldMapping::List & _mappings) : topLevel(true), mappings(_mappings)
  59. {
  60. }
  61. MappingLevel::MappingLevel(MappingLevel * parent, char const * name, FieldMapping::List & _mappings) : topLevel(false), mappings(_mappings)
  62. {
  63. if(!parent->topLevel)
  64. scope.append(parent->scope).append(scopeSeparator);
  65. scope.append(name);
  66. }
  67. void MappingLevel::calculateMappings(IDefRecordElement const * diskRecord, unsigned numKeyedDisk, IDefRecordElement const * activityRecord, unsigned numKeyedActivity)
  68. {
  69. if(diskRecord->getKind() != DEKrecord)
  70. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append("Disk record metadata had unexpected structure (expected record)")->appendScopeDesc(topLevel ? NULL : scope.str());
  71. if(activityRecord->getKind() != DEKrecord)
  72. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append("Activity record metadata had unexpected structure (expected record)")->appendScopeDesc(topLevel ? NULL : scope.str());
  73. unsigned numActivityChildren = activityRecord->numChildren();
  74. unsigned numDiskChildren = diskRecord->numChildren();
  75. bool activityHasInternalFpos = false;
  76. if(topLevel && (numActivityChildren > numKeyedActivity))
  77. {
  78. IDefRecordElement const * lastChild = activityRecord->queryChild(numActivityChildren-1);
  79. if((lastChild->queryName() == internalFposAtom) && (lastChild->queryType()->isInteger()))
  80. activityHasInternalFpos = true;
  81. }
  82. if((numActivityChildren - (activityHasInternalFpos ? 1 : 0)) > numDiskChildren) //if last activity field might be unmatched __internal_fpos__, should be more lenient by 1 as would fill that in (see below)
  83. throw makeFailure(IRecordLayoutTranslator::Failure::MissingDiskField)->append("Activity record requires more fields than index provides")->appendScopeDesc(topLevel ? NULL : scope.str());
  84. if(numKeyedActivity > numKeyedDisk)
  85. throw makeFailure(IRecordLayoutTranslator::Failure::UnkeyedDiskField)->append("Activity record requires more keyed fields than index provides")->appendScopeDesc(topLevel ? NULL : scope.str());
  86. BoolArray activityFieldMapped;
  87. activityFieldMapped.ensure(numActivityChildren);
  88. for(unsigned i=0; i<numActivityChildren; ++i)
  89. activityFieldMapped.append(false);
  90. FieldSearcher searcher(activityRecord);
  91. for(unsigned diskFieldNum = 0; diskFieldNum < numDiskChildren; ++diskFieldNum)
  92. {
  93. checkField(diskRecord, diskFieldNum, "Disk");
  94. bool diskFieldKeyed = (diskFieldNum < numKeyedDisk);
  95. unsigned activityFieldNum;
  96. if(searcher.search(diskRecord->queryChild(diskFieldNum)->queryName(), activityFieldNum))
  97. {
  98. bool activityFieldKeyed = (activityFieldNum < numKeyedActivity);
  99. if(activityFieldKeyed && !diskFieldKeyed)
  100. throw makeFailure(IRecordLayoutTranslator::Failure::UnkeyedDiskField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is keyed in activity but not on disk");
  101. checkField(activityRecord, activityFieldNum, "Activity");
  102. attemptMapping(diskRecord, diskFieldNum, diskFieldKeyed, activityRecord, activityFieldNum, activityFieldKeyed);
  103. activityFieldMapped.replace(true, activityFieldNum);
  104. }
  105. else
  106. {
  107. mappings.append(*new FieldMapping(FieldMapping::None, diskRecord, diskFieldNum, diskFieldKeyed, NULL, 0, false));
  108. }
  109. }
  110. for(unsigned activityFieldNum=0; activityFieldNum<numActivityChildren; ++activityFieldNum)
  111. if(!activityFieldMapped.item(activityFieldNum))
  112. {
  113. checkField(activityRecord, activityFieldNum, "Activity");
  114. if((activityFieldNum != numActivityChildren-1) || !activityHasInternalFpos) //if last activity field is unmatched __internal_fpos__, this is not an error, we need do nothing and it will get correctly set to zero
  115. throw makeFailure(IRecordLayoutTranslator::Failure::MissingDiskField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is required by activity but not present on disk index");
  116. }
  117. }
  118. void MappingLevel::attemptMapping(IDefRecordElement const * diskRecord, unsigned diskFieldNum, bool diskFieldKeyed, IDefRecordElement const * activityRecord, unsigned activityFieldNum, bool activityFieldKeyed)
  119. {
  120. IDefRecordElement const * diskField = diskRecord->queryChild(diskFieldNum);
  121. IDefRecordElement const * activityField = activityRecord->queryChild(activityFieldNum);
  122. IDefRecordElement const * diskChild = NULL;
  123. IDefRecordElement const * diskBlob = NULL;
  124. queryCheckFieldChild(diskField, "Disk", diskChild, diskBlob);
  125. IDefRecordElement const * activityChild = NULL;
  126. IDefRecordElement const * activityBlob = NULL;
  127. queryCheckFieldChild(activityField, "Activity", activityChild, activityBlob);
  128. if(diskBlob)
  129. {
  130. if(!activityBlob)
  131. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is blob on disk but not in activity");
  132. if(!isSameBasicType(diskBlob->queryType(), activityBlob->queryType()))
  133. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append("Blob field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" has differing referenced types on disk and in activity");
  134. }
  135. else
  136. {
  137. if(activityBlob)
  138. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is blob in activity but not on disk");
  139. }
  140. if(diskChild)
  141. {
  142. if(!activityChild)
  143. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is child dataset on disk but not in activity");
  144. if(activityFieldKeyed)
  145. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append("Activity record metadata had unexpected structure (keyed field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is child dataset)");
  146. if(*activityChild == *diskChild)
  147. {
  148. mappings.append(*new FieldMapping(FieldMapping::Simple, diskRecord, diskFieldNum, false, activityRecord, activityFieldNum, false));
  149. }
  150. else
  151. {
  152. Owned<FieldMapping> mapping(new FieldMapping(FieldMapping::ChildDataset, diskRecord, diskFieldNum, false, activityRecord, activityFieldNum, false));
  153. MappingLevel childMappingLevel(this, diskField->queryName()->str(), mapping->queryChildMappings());
  154. childMappingLevel.calculateMappings(diskChild, 0, activityChild, 0);
  155. mappings.append(*mapping.getClear());
  156. }
  157. }
  158. else
  159. {
  160. if(activityChild)
  161. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" is child dataset in activity but not on disk");
  162. if(!isSameBasicType(diskField->queryType(), activityField->queryType()))
  163. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append("Field ")->appendFieldName(topLevel ? NULL : scope.str(), activityRecord->queryChild(activityFieldNum))->append(" has differing types on disk and in activity");
  164. mappings.append(*new FieldMapping(FieldMapping::Simple, diskRecord, diskFieldNum, diskFieldKeyed, activityRecord, activityFieldNum, activityFieldKeyed));
  165. }
  166. }
  167. void MappingLevel::checkField(IDefRecordElement const * record, unsigned num, char const * label)
  168. {
  169. switch(record->queryChild(num)->getKind())
  170. {
  171. case DEKfield:
  172. break;
  173. case DEKifblock:
  174. throw makeFailure(IRecordLayoutTranslator::Failure::UntranslatableField)->append(label)->append(" record metadata field #")->append(num)->append(" is an ifblock which is not currently translatable");
  175. case DEKnone:
  176. case DEKrecord:
  177. case DEKattr:
  178. default:
  179. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (child #")->append(num)->append("was neither field nor ifblock)")->appendScopeDesc(topLevel ? NULL : scope.str());
  180. }
  181. }
  182. void MappingLevel::queryCheckFieldChild(IDefRecordElement const * field, char const * label, IDefRecordElement const * & child, IDefRecordElement const * & blob)
  183. {
  184. if(field->getKind() != DEKfield)
  185. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (non-field found where field expected");
  186. type_t fieldType = field->queryType()->getTypeCode();
  187. switch(fieldType)
  188. {
  189. case type_table:
  190. case type_groupedtable:
  191. if(field->numChildren() != 1)
  192. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (expected exactly one child of table field ")->appendFieldName(topLevel ? NULL : scope.str(), field)->append(")");
  193. child = field->queryChild(0);
  194. if(child->getKind() != DEKrecord)
  195. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (unexpected non-record child of table field ")->appendFieldName(topLevel ? NULL : scope.str(), field)->append(")");
  196. break;
  197. case type_blob:
  198. if(field->numChildren() != 1)
  199. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (expected exactly one child of blob field ")->appendFieldName(topLevel ? NULL : scope.str(), field)->append(")");
  200. blob = field->queryChild(0);
  201. if(blob->getKind() != DEKfield)
  202. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (expected non-field child of blob field ")->appendFieldName(topLevel ? NULL : scope.str(), field)->append(")");
  203. break;
  204. default:
  205. if(field->numChildren() != 0)
  206. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append(label)->append(" record metadata had unexpected structure (unexpected children of field ")->appendFieldName(topLevel ? NULL : scope.str(), field)->append(")");
  207. }
  208. }
  209. void RowTransformer::build(unsigned & seq, FieldMapping::List const & mappings)
  210. {
  211. CIArrayOf<RowRecord> records;
  212. analyseMappings(mappings, records);
  213. keepFpos = false;
  214. copyToFpos = false;
  215. generateCopies(seq, records);
  216. }
  217. RowTransformer::RowRecord & RowTransformer::ensureItem(CIArrayOf<RowRecord> & arr, unsigned pos)
  218. {
  219. while(arr.ordinality() <= pos) arr.append(*new RowRecord);
  220. return arr.item(pos);
  221. }
  222. void RowTransformer::createRowRecord(FieldMapping const & mapping, CIArrayOf<RowRecord> & records, size32_t diskOffset, unsigned numVarFields, bool & prevActivityField, unsigned & prevActivityFieldNum)
  223. {
  224. size32_t diskSize = mapping.queryDiskFieldSize();
  225. unsigned activityFieldNum = mapping.queryActivityFieldNum();
  226. switch(mapping.queryType())
  227. {
  228. case FieldMapping::Simple:
  229. if(mapping.isDiskFieldFpos())
  230. if(mapping.isActivityFieldFpos())
  231. {
  232. ensureItem(records, activityFieldNum).setVals(0, 0, diskSize, false).setFpos(true, true);
  233. prevActivityField = false;
  234. }
  235. else
  236. {
  237. ensureItem(records, activityFieldNum).setVals(0, 0, diskSize, false).setFpos(false, true);
  238. prevActivityField = false;
  239. }
  240. else
  241. if(mapping.isActivityFieldFpos())
  242. {
  243. ensureItem(records, activityFieldNum).setVals(diskOffset, numVarFields, diskSize, false).setFpos(true, false);
  244. prevActivityField = false;
  245. }
  246. else
  247. {
  248. ensureItem(records, activityFieldNum).setVals(diskOffset, numVarFields, diskSize, (prevActivityField && (activityFieldNum == (prevActivityFieldNum+1))));
  249. prevActivityField = true;
  250. prevActivityFieldNum = activityFieldNum;
  251. }
  252. break;
  253. case FieldMapping::ChildDataset:
  254. ensureItem(records, activityFieldNum).setVals(diskOffset, numVarFields, diskSize, false).setChildMappings(&mapping.queryChildMappings());
  255. prevActivityField = false;
  256. break;
  257. case FieldMapping::None:
  258. prevActivityField = false;
  259. break;
  260. default:
  261. throwUnexpected();
  262. }
  263. }
  264. void RowTransformer::analyseMappings(FieldMapping::List const & mappings, CIArrayOf<RowRecord> & records)
  265. {
  266. size32_t diskOffset = 0;
  267. unsigned numRowDiskFields = mappings.ordinality();
  268. bool prevActivityField = false;
  269. unsigned prevActivityFieldNum;
  270. for(unsigned diskFieldNum = 0; diskFieldNum < numRowDiskFields; ++diskFieldNum)
  271. {
  272. FieldMapping const & mapping = mappings.item(diskFieldNum);
  273. createRowRecord(mapping, records, diskOffset, diskVarFieldRelOffsets.ordinality(), prevActivityField, prevActivityFieldNum);
  274. size32_t diskSize = mapping.queryDiskFieldSize();
  275. if(diskSize == UNKNOWN_LENGTH)
  276. {
  277. diskVarFieldRelOffsets.append(diskOffset);
  278. diskVarFieldLenDisplacements.append(mapping.isDiskFieldSet() ? 1 : 0);
  279. diskOffset = 0;
  280. }
  281. else
  282. {
  283. diskOffset += diskSize;
  284. }
  285. }
  286. finalFixedSize = diskOffset;
  287. }
  288. void RowTransformer::generateSimpleCopy(unsigned & seq, RowRecord const & record)
  289. {
  290. if(!record.queryFollowOn())
  291. copies.append(*new FieldCopy(sequence, record.queryRelOffset(), record.queryRelBase()));
  292. FieldCopy & copy = copies.tos();
  293. size32_t size = record.querySize();
  294. if(size == UNKNOWN_LENGTH)
  295. copy.addVarField(record.queryRelBase()+1);
  296. else
  297. copy.addFixedSize(size);
  298. FieldMapping::List const * childMappings = record.queryChildMappings();
  299. if(childMappings)
  300. copy.setChildTransformer(new RowTransformer(seq, *childMappings));
  301. }
  302. void RowTransformer::generateCopyToFpos(RowRecord const & record)
  303. {
  304. copyToFpos = true;
  305. copyToFposRelOffset = record.queryRelOffset();
  306. copyToFposRelBase = record.queryRelBase();
  307. copyToFposSize = record.querySize();
  308. assertex(copyToFposSize != UNKNOWN_LENGTH);
  309. assertex(copyToFposSize <= sizeof(offset_t));
  310. }
  311. void RowTransformer::generateCopyFromFpos(RowRecord const & record)
  312. {
  313. assertex(sequence == 0);
  314. copies.append(*new FieldCopy(static_cast<unsigned>(-1), 0, 0));
  315. size32_t size = record.querySize();
  316. assertex(size != UNKNOWN_LENGTH);
  317. assertex(size <= sizeof(offset_t));
  318. copies.tos().addFixedSize(size);
  319. }
  320. void RowTransformer::generateCopies(unsigned & seq, CIArrayOf<RowRecord> const & records)
  321. {
  322. sequence = seq++;
  323. ForEachItemIn(fieldNum, records)
  324. {
  325. RowRecord const & record = records.item(fieldNum);
  326. if(record.isToFpos())
  327. {
  328. assertex(sequence == 0);
  329. if(record.isFromFpos())
  330. keepFpos = true;
  331. else
  332. generateCopyToFpos(record);
  333. }
  334. else
  335. {
  336. if(record.isFromFpos())
  337. generateCopyFromFpos(record);
  338. else
  339. generateSimpleCopy(seq, record);
  340. }
  341. }
  342. }
  343. void RowTransformer::transform(IRecordLayoutTranslator::RowTransformContext * ctx, byte const * in, size32_t inSize, size32_t & inOffset, byte * out, size32_t outBuffSize, size32_t & outOffset) const
  344. {
  345. ctx->set(sequence, 0, 0, in+inOffset);
  346. for(unsigned varIdx = 1; varIdx <= diskVarFieldRelOffsets.ordinality(); ++varIdx)
  347. {
  348. if(inOffset >= inSize)
  349. throw MakeStringException(0, "Disk row invalid during record layout translation");
  350. inOffset += diskVarFieldRelOffsets.item(varIdx-1);
  351. size32_t disp = diskVarFieldLenDisplacements.item(varIdx-1);
  352. size32_t size = *reinterpret_cast<size32_t const *>(in+inOffset+disp);
  353. // length prefix is little-endian
  354. #if __BYTE_ORDER == __BIG_ENDIAN
  355. _rev(&size);
  356. #endif
  357. size += disp;
  358. size += sizeof(size32_t);
  359. inOffset += size;
  360. ctx->set(sequence, varIdx, size, in+inOffset);
  361. }
  362. inOffset += finalFixedSize;
  363. ForEachItemIn(copyIdx, copies)
  364. copies.item(copyIdx).copy(ctx, out, outBuffSize, outOffset);
  365. }
  366. void RowTransformer::getFposOut(IRecordLayoutTranslator::RowTransformContext const * ctx, offset_t & fpos) const
  367. {
  368. if(copyToFpos)
  369. {
  370. fpos = 0;
  371. const byte * in = ctx->queryPointer(0, copyToFposRelBase) + copyToFposRelOffset;
  372. // integer field in row is big-endian
  373. #if __BYTE_ORDER == __BIG_ENDIAN
  374. memcpy(reinterpret_cast<byte const *>(&fpos) + sizeof(offset_t) - copyToFposSize, in, copyToFposSize);
  375. #else
  376. _cpyrevn(&fpos, in, copyToFposSize);
  377. #endif
  378. }
  379. else if(!keepFpos)
  380. {
  381. fpos = 0;
  382. }
  383. }
  384. void RowTransformer::createRowTransformContext(IRecordLayoutTranslator::RowTransformContext * ctx) const
  385. {
  386. ctx->init(sequence, diskVarFieldRelOffsets.ordinality()+1);
  387. ForEachItemIn(idx, copies)
  388. {
  389. RowTransformer const * child = copies.item(idx).queryChildTransformer();
  390. if(child)
  391. child->createRowTransformContext(ctx);
  392. }
  393. }
  394. void FieldCopy::copy(IRecordLayoutTranslator::RowTransformContext * ctx, byte * out, size32_t outBuffSize, size32_t & outOffset) const
  395. {
  396. if(sequence == static_cast<unsigned>(-1))
  397. {
  398. if(outOffset+fixedSize > outBuffSize)
  399. throw MakeStringException(0, "Activity row exceeded expected size limit during record layout translation");
  400. // integer field in row is big-endian
  401. #if __BYTE_ORDER == __BIG_ENDIAN
  402. memcpy(out+outOffset, reinterpret_cast<byte const *>(ctx->queryFposIn()) + sizeof(offset_t) - fixedSize, fixedSize);
  403. #else
  404. _cpyrevn(out+outOffset, ctx->queryFposIn(), fixedSize);
  405. #endif
  406. outOffset += fixedSize;
  407. return;
  408. }
  409. size32_t diskFieldSize = fixedSize;
  410. ForEachItemIn(varIdx, varFields)
  411. diskFieldSize += ctx->querySize(sequence, varFields.item(varIdx));
  412. byte const * in = ctx->queryPointer(sequence, relBase) + relOffset;
  413. if(childTransformer)
  414. {
  415. size32_t inOffset = sizeof(size32_t);
  416. size32_t * outSizePtr = reinterpret_cast<size32_t *>(out+outOffset);
  417. outOffset += sizeof(size32_t);
  418. size32_t startOutOffset = outOffset;
  419. while(inOffset < diskFieldSize)
  420. childTransformer->transform(ctx, in, diskFieldSize, inOffset, out, outBuffSize, outOffset);
  421. *outSizePtr = outOffset-startOutOffset;
  422. }
  423. else
  424. {
  425. if(outOffset+diskFieldSize > outBuffSize)
  426. throw MakeStringException(0, "Activity row exceeded expected size limit during record layout translation");
  427. memcpy(out+outOffset, in, diskFieldSize);
  428. outOffset += diskFieldSize;
  429. }
  430. }
  431. IRecordLayoutTranslator::RowTransformContext::RowTransformContext(unsigned _num) : num(_num)
  432. {
  433. sizes = new unsigned *[num];
  434. ptrs = new byte const * *[num];
  435. for(unsigned i=0; i<num; ++i)
  436. {
  437. sizes[i] = NULL;
  438. ptrs[i] = NULL;
  439. }
  440. }
  441. IRecordLayoutTranslator::RowTransformContext::~RowTransformContext()
  442. {
  443. for(unsigned i=0; i<num; ++i)
  444. {
  445. if(sizes[i])
  446. delete [] sizes[i];
  447. if(ptrs[i])
  448. delete [] ptrs[i];
  449. }
  450. delete [] sizes;
  451. delete [] ptrs;
  452. }
  453. size32_t calcMetaSize(IDefRecordMeta const * meta)
  454. {
  455. IDefRecordElement * record = meta->queryRecord();
  456. size32_t size = record->getMaxSize();
  457. unsigned numFields = record->numChildren();
  458. if(meta->numKeyedFields() < numFields)
  459. {
  460. ITypeInfo * lastFieldType = record->queryChild(numFields-1)->queryType();
  461. if(lastFieldType->isInteger())
  462. size -= lastFieldType->getSize();
  463. }
  464. return size;
  465. }
  466. CRecordLayoutTranslator::CRecordLayoutTranslator(IDefRecordMeta const * _diskMeta, IDefRecordMeta const * _activityMeta) : diskMeta(const_cast<IDefRecordMeta *>(_diskMeta)), activityMeta(const_cast<IDefRecordMeta *>(_activityMeta)), activityKeySizes(NULL)
  467. {
  468. numKeyedDisk = diskMeta->numKeyedFields();
  469. numKeyedActivity = activityMeta->numKeyedFields();
  470. diskMetaSize = calcMetaSize(diskMeta);
  471. activityMetaSize = calcMetaSize(activityMeta);
  472. MappingLevel topMappingLevel(mappings);
  473. numTransformers = 0;
  474. try
  475. {
  476. if(numKeyedDisk==0)
  477. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append("Disk record had no keyed fields");
  478. if(numKeyedActivity==0)
  479. throw makeFailure(IRecordLayoutTranslator::Failure::BadStructure)->append("Activity record had no keyed fields");
  480. topMappingLevel.calculateMappings(diskMeta->queryRecord(), numKeyedDisk, activityMeta->queryRecord(), numKeyedActivity);
  481. calculateActivityKeySizes();
  482. calculateKeysTransformed();
  483. transformer.build(numTransformers, mappings);
  484. }
  485. catch(Failure * f)
  486. {
  487. failure.setown(f);
  488. }
  489. }
  490. void CRecordLayoutTranslator::calculateActivityKeySizes()
  491. {
  492. activityKeySizes = new size32_t[numKeyedActivity];
  493. for(unsigned activityFieldNum=0; activityFieldNum<numKeyedActivity; ++activityFieldNum)
  494. activityKeySizes[activityFieldNum] = 0;
  495. ForEachItemIn(diskFieldNum, mappings)
  496. {
  497. FieldMapping const & mapping = mappings.item(diskFieldNum);
  498. if(mapping.queryType() == FieldMapping::Simple)
  499. {
  500. unsigned activityFieldNum = mapping.queryActivityFieldNum();
  501. if(activityFieldNum < numKeyedActivity)
  502. activityKeySizes[activityFieldNum] = activityMeta->queryRecord()->queryChild(activityFieldNum)->queryType()->getSize();
  503. }
  504. }
  505. }
  506. void CRecordLayoutTranslator::calculateKeysTransformed()
  507. {
  508. keysTransformed = true;;
  509. if(numKeyedActivity != numKeyedDisk)
  510. return;
  511. for(unsigned diskFieldNum=0; diskFieldNum<numKeyedDisk; ++diskFieldNum)
  512. {
  513. FieldMapping const & mapping = mappings.item(diskFieldNum);
  514. if((mapping.queryType() != FieldMapping::Simple) || (mapping.queryActivityFieldNum() != diskFieldNum))
  515. return;
  516. }
  517. keysTransformed = false;
  518. }
  519. void CRecordLayoutTranslator::createDiskSegmentMonitors(SegmentMonitorContext const & in, IIndexReadContext & out)
  520. {
  521. if(failure) return;
  522. if(in.ordinality() != numKeyedActivity)
  523. {
  524. failure.setown(makeFailure(Failure::UnsupportedFilter)->append("Unsupported filter (segment monitor) type (too few filters)"));
  525. return;
  526. }
  527. size32_t diskOffset = 0;
  528. for(unsigned diskFieldNum = 0; diskFieldNum < numKeyedDisk; ++diskFieldNum)
  529. {
  530. FieldMapping const & mapping = mappings.item(diskFieldNum);
  531. assertex(mapping.queryDiskFieldNum() == diskFieldNum);
  532. size32_t size = mapping.queryDiskFieldSize();
  533. Owned<IKeySegmentMonitor> monitor;
  534. switch(mapping.queryType())
  535. {
  536. case FieldMapping::Simple:
  537. if(mapping.queryActivityFieldNum() < numKeyedActivity)
  538. {
  539. assertex(mapping.queryActivityFieldSize() == size);
  540. monitor.set(in.item(mapping.queryActivityFieldNum()));
  541. assertex(monitor->getSize() == size);
  542. if(monitor->getOffset() != diskOffset)
  543. {
  544. monitor.setown(monitor->clone());
  545. if(!monitor || !monitor->setOffset(diskOffset))
  546. {
  547. failure.setown(makeFailure(Failure::UnsupportedFilter)->append("Unable to change offset of filter (segment monitor) for field ")->append(mapping.queryDiskFieldName()));
  548. return;
  549. }
  550. }
  551. break;
  552. }
  553. //fall through
  554. case FieldMapping::None:
  555. monitor.setown(createWildKeySegmentMonitor(diskOffset, size));
  556. break;
  557. case FieldMapping::ChildDataset:
  558. default:
  559. throwUnexpected();
  560. }
  561. out.append(monitor.getLink());
  562. diskOffset += size;
  563. }
  564. }
  565. void CRecordLayoutTranslator::checkSizes(char const * filename, size32_t activitySize, size32_t diskSize) const
  566. {
  567. if(activityMetaSize != activitySize)
  568. throw MakeStringException(0, "Key size mismatch during translation of index %s: ECL indicates size %u, ECL record meta has size %u", filename, activitySize, activityMetaSize);
  569. if(diskMetaSize != diskSize)
  570. throw MakeStringException(0, "Key size mismatch during translation of index %s: index indicates size %u, index record meta has size %u", filename, diskSize, diskMetaSize);
  571. }
  572. IRecordLayoutTranslator::RowTransformContext * CRecordLayoutTranslator::getRowTransformContext()
  573. {
  574. Owned<IRecordLayoutTranslator::RowTransformContext> ctx = new RowTransformContext(numTransformers);
  575. transformer.createRowTransformContext(ctx);
  576. return ctx.getClear();
  577. }
  578. size32_t CRecordLayoutTranslator::transformRow(RowTransformContext * ctx, byte const * in, size32_t inSize, byte * out, size32_t outBuffSize, offset_t & fpos) const
  579. {
  580. size32_t inOffset = 0;
  581. size32_t outOffset = 0;
  582. ctx->setFposIn(fpos);
  583. transformer.transform(ctx, in, inSize, inOffset, out, outBuffSize, outOffset);
  584. transformer.getFposOut(ctx, fpos);
  585. return outOffset;
  586. }
  587. void ExpandedSegmentMonitorList::append(IKeySegmentMonitor * monitor)
  588. {
  589. if(owner->failure) return;
  590. while(monitor->getSize() > owner->activityKeySizes[monitors.ordinality()])
  591. {
  592. Owned<IKeySegmentMonitor> split = monitor->split(owner->activityKeySizes[monitors.ordinality()]);
  593. if(!split)
  594. {
  595. owner->failure.setown(makeFailure(IRecordLayoutTranslator::Failure::UnsupportedFilter)->append("Unsupported filter (segment monitor) type (was larger than keyed field and unsplittable)"));
  596. return;
  597. }
  598. monitors.append(*split.getLink());
  599. }
  600. if(monitor->getSize() < owner->activityKeySizes[monitors.ordinality()])
  601. {
  602. owner->failure.setown(makeFailure(IRecordLayoutTranslator::Failure::UnsupportedFilter)->append("Unsupported filter (segment monitor) type (was smaller than keyed field)"));
  603. return;
  604. }
  605. monitors.append(*monitor);
  606. }
  607. void ExpandedSegmentMonitorList::setMergeBarrier(unsigned offset)
  608. {
  609. // MORE - It's possible that I need to do something here??
  610. }
  611. IRecordLayoutTranslator * createRecordLayoutTranslator(IDefRecordMeta const * diskMeta, IDefRecordMeta const * activityMeta)
  612. {
  613. Owned<IRecordLayoutTranslator> layoutTrans = new CRecordLayoutTranslator(diskMeta, activityMeta);
  614. if(!layoutTrans->querySuccess())
  615. {
  616. StringBuffer cause;
  617. layoutTrans->queryFailure().getDetail(cause);
  618. throw MakeStringException(0, "Unable to recover from record layout mismatch (%s)", cause.str());
  619. }
  620. return layoutTrans.getClear();
  621. };
  622. extern THORHELPER_API IRecordLayoutTranslator * createRecordLayoutTranslator(size32_t diskMetaSize, const void *diskMetaData, size32_t activityMetaSize, const void *activityMetaData)
  623. {
  624. MemoryBuffer activityMetaSerialized;
  625. activityMetaSerialized.setBuffer(activityMetaSize, (void *) activityMetaData, false);
  626. Owned<IDefRecordMeta> activityMeta = deserializeRecordMeta(activityMetaSerialized, true);
  627. MemoryBuffer diskMetaSerialized;
  628. diskMetaSerialized.setBuffer(diskMetaSize, (void *) diskMetaData, false);
  629. Owned<IDefRecordMeta> diskMeta = deserializeRecordMeta(diskMetaSerialized, true);
  630. return createRecordLayoutTranslator(diskMeta, activityMeta);
  631. }
  632. #ifdef DEBUG_HELPERS_REQUIRED
  633. IPropertyTree * convertFieldMappingsToPTree(FieldMapping::List const & mappings)
  634. {
  635. Owned<IPropertyTree> tree = createPTree("Record");
  636. ForEachItemIn(mappingIdx, mappings)
  637. {
  638. FieldMapping const & m = mappings.item(mappingIdx);
  639. Owned<IPropertyTree> branch = createPTree();
  640. branch->setPropInt("@diskFieldNum", m.queryDiskFieldNum());
  641. branch->setProp("@diskFieldName", m.queryDiskFieldName());
  642. switch(m.queryType())
  643. {
  644. case FieldMapping::None:
  645. branch->setProp("@type", "None");
  646. break;
  647. case FieldMapping::Simple:
  648. branch->setProp("@type", "Simple");
  649. branch->setPropInt("@activityFieldNum", m.queryActivityFieldNum());
  650. branch->setProp("@activityFieldName", m.queryActivityFieldName());
  651. break;
  652. case FieldMapping::ChildDataset:
  653. branch->setProp("@type", "ChildDataset");
  654. branch->setPropInt("@activityFieldNum", m.queryActivityFieldNum());
  655. branch->setProp("@activityFieldName", m.queryActivityFieldName());
  656. branch->setPropTree("Record", convertFieldMappingsToPTree(m.queryChildMappings()));
  657. break;
  658. default:
  659. throwUnexpected();
  660. }
  661. tree->addPropTree("Mapping", branch.getClear());
  662. }
  663. return tree.getClear();
  664. }
  665. StringBuffer & CRecordLayoutTranslator::getMappingsAsString(StringBuffer & out) const
  666. {
  667. Owned<IPropertyTree> tree = convertFieldMappingsToPTree(mappings);
  668. toXML(tree, out);
  669. return out;
  670. }
  671. #endif
  672. CacheKey::CacheKey(size32_t _s1, void const * _d1, size32_t _s2, void const * _d2)
  673. : s1(_s1), d1(static_cast<byte const *>(_d1)), s2(_s2), d2(static_cast<byte const *>(_d2))
  674. {
  675. hashval = hashc(d1, s1, 0);
  676. hashval = hashc(d2, s2, hashval);
  677. }
  678. CacheValue::CacheValue(size32_t s1, void const * d1, size32_t s2, void const * d2, IRecordLayoutTranslator * _trans)
  679. : b1(s1, d1), b2(s2, d2), key(b1.length(), b1.get(), b2.length(), b2.get()), trans(_trans)
  680. {
  681. }
  682. IRecordLayoutTranslator * CRecordLayoutTranslatorCache::get(size32_t diskMetaSize, void const * diskMetaData, size32_t activityMetaSize, void const * activityMetaData, IDefRecordMeta const * activityMeta)
  683. {
  684. CacheKey key(diskMetaSize, diskMetaData, activityMetaSize, activityMetaData);
  685. CacheValue * value = find(&key);
  686. if(!value)
  687. {
  688. Owned<IDefRecordMeta> activityMetaDeserialized;
  689. if(!activityMeta)
  690. {
  691. MemoryBuffer activityMetaSerialized;
  692. activityMetaSerialized.setBuffer(activityMetaSize, (void *) activityMetaData, false);
  693. activityMetaDeserialized.setown(deserializeRecordMeta(activityMetaSerialized, true));
  694. activityMeta = activityMetaDeserialized.get();
  695. }
  696. MemoryBuffer diskMetaSerialized;
  697. diskMetaSerialized.setBuffer(diskMetaSize, (void *) diskMetaData, false);
  698. Owned<IDefRecordMeta> diskMeta = deserializeRecordMeta(diskMetaSerialized, true);
  699. Owned<IRecordLayoutTranslator> trans = createRecordLayoutTranslator(diskMeta, activityMeta);
  700. value = new CacheValue(diskMetaSize, diskMetaData, activityMetaSize, activityMetaData, trans.getLink());
  701. addNew(value);
  702. }
  703. return value->getTranslator();
  704. }
  705. extern THORHELPER_API IRecordLayoutTranslatorCache * createRecordLayoutTranslatorCache()
  706. {
  707. return new CRecordLayoutTranslatorCache();
  708. }
  709. #ifdef _USE_CPPUNIT
  710. #include <cppunit/extensions/HelperMacros.h>
  711. //MORE: This does not test translation with blobs or child datasets. Also, it only creates translators --- testing they actually work would require a lot more framework...
  712. class RecordLayoutTranslatorTest : public CppUnit::TestFixture
  713. {
  714. CPPUNIT_TEST_SUITE(RecordLayoutTranslatorTest);
  715. CPPUNIT_TEST(testCount);
  716. CPPUNIT_TEST(testKeyedSwap);
  717. CPPUNIT_TEST(testUnkey);
  718. CPPUNIT_TEST(testKeyFail);
  719. CPPUNIT_TEST(testSwapKeyFail);
  720. CPPUNIT_TEST(testCache);
  721. CPPUNIT_TEST(testDropKeyed);
  722. CPPUNIT_TEST(testDropUnkeyed);
  723. CPPUNIT_TEST(testNewFieldFail);
  724. CPPUNIT_TEST(testRenamedFieldFail);
  725. CPPUNIT_TEST(testChangeTypeFail);
  726. CPPUNIT_TEST_SUITE_END();
  727. public:
  728. void setUp()
  729. {
  730. bool done = false;
  731. for(unsigned m=0; !done; ++m)
  732. {
  733. Owned<IDefRecordBuilder> builder = createDErecord(4096);
  734. Owned<ITypeInfo> type;
  735. _ATOM name;
  736. size32_t size;
  737. unsigned keyed;
  738. unsigned f;
  739. for(f = 0; getFieldData(m, f, type, name, size, keyed); ++f)
  740. {
  741. Owned<IDefRecordElement> field = createDEfield(name, type, NULL, size);
  742. builder->addChild(field);
  743. }
  744. Owned<IDefRecordElement> record = builder->close();
  745. if(f)
  746. meta.append(*createDefRecordMeta(record, keyed));
  747. else
  748. done = true;
  749. }
  750. }
  751. void testCount()
  752. {
  753. CPPUNIT_ASSERT(meta.ordinality() == 10);
  754. }
  755. void testKeyedSwap()
  756. {
  757. doTranslate(0, 1);
  758. }
  759. void testUnkey()
  760. {
  761. doTranslate(0, 2);
  762. }
  763. void testKeyFail()
  764. {
  765. doTranslateFail(0, 3, IRecordLayoutTranslator::Failure::UnkeyedDiskField);
  766. }
  767. void testSwapKeyFail()
  768. {
  769. doTranslateFail(0, 4, IRecordLayoutTranslator::Failure::UnkeyedDiskField);
  770. }
  771. void testDropKeyed()
  772. {
  773. doTranslate(0, 5);
  774. }
  775. void testDropUnkeyed()
  776. {
  777. doTranslate(0, 6);
  778. }
  779. void testNewFieldFail()
  780. {
  781. doTranslateFail(0, 7, IRecordLayoutTranslator::Failure::MissingDiskField);
  782. }
  783. void testRenamedFieldFail()
  784. {
  785. doTranslateFail(0, 8, IRecordLayoutTranslator::Failure::MissingDiskField);
  786. }
  787. void testChangeTypeFail()
  788. {
  789. doTranslateFail(0, 9, IRecordLayoutTranslator::Failure::UntranslatableField);
  790. }
  791. void testCache()
  792. {
  793. MemoryBuffer buff[3];
  794. for(unsigned m=0; m<3; ++m)
  795. serializeRecordMeta(buff[m], &meta.item(m), true);
  796. Owned<IRecordLayoutTranslatorCache> cache = createRecordLayoutTranslatorCache();
  797. CPPUNIT_ASSERT(cache.get() != 0);
  798. CPPUNIT_ASSERT(cache->count() == 0);
  799. Owned<IRecordLayoutTranslator> t1 = cache->get(buff[0].length(), buff[0].bufferBase(), buff[1].length(), buff[1].bufferBase(), NULL);
  800. CPPUNIT_ASSERT(cache->count() == 1);
  801. Owned<IRecordLayoutTranslator> t2 = cache->get(buff[0].length(), buff[0].bufferBase(), buff[1].length(), buff[1].bufferBase(), NULL);
  802. CPPUNIT_ASSERT(cache->count() == 1);
  803. Owned<IRecordLayoutTranslator> t3 = cache->get(buff[0].length(), buff[0].bufferBase(), buff[2].length(), buff[2].bufferBase(), NULL);
  804. CPPUNIT_ASSERT(cache->count() == 2);
  805. CPPUNIT_ASSERT(t1.get() == t2.get());
  806. CPPUNIT_ASSERT(t1.get() != t3.get());
  807. }
  808. private:
  809. bool getFieldData(unsigned m, unsigned f, Owned<ITypeInfo> & type, _ATOM & name, size32_t & size, unsigned & keyed)
  810. {
  811. switch(m)
  812. {
  813. case 0:
  814. //disk
  815. keyed = 2;
  816. switch(f)
  817. {
  818. case 0:
  819. type.setown(makeIntType(1, false));
  820. name = createAtom("i");
  821. size = 1;
  822. return true;
  823. case 1:
  824. type.setown(makeIntType(4, false));
  825. name = createAtom("j");
  826. size = 4;
  827. return true;
  828. case 2:
  829. type.setown(makeStringType(8));
  830. name = createAtom("str");
  831. size = 8;
  832. return true;
  833. }
  834. return false;
  835. case 1:
  836. //swap i,j
  837. keyed = 2;
  838. switch(f)
  839. {
  840. case 0:
  841. type.setown(makeIntType(4, false));
  842. name = createAtom("j");
  843. size = 4;
  844. return true;
  845. case 1:
  846. type.setown(makeIntType(1, false));
  847. name = createAtom("i");
  848. size = 1;
  849. return true;
  850. case 2:
  851. type.setown(makeStringType(8));
  852. name = createAtom("str");
  853. size = 8;
  854. return true;
  855. }
  856. return false;
  857. case 2:
  858. //unkey j
  859. keyed = 1;
  860. switch(f)
  861. {
  862. case 0:
  863. type.setown(makeIntType(1, false));
  864. name = createAtom("i");
  865. size = 1;
  866. return true;
  867. case 1:
  868. type.setown(makeIntType(4, false));
  869. name = createAtom("j");
  870. size = 4;
  871. return true;
  872. case 2:
  873. type.setown(makeStringType(8));
  874. name = createAtom("str");
  875. size = 8;
  876. return true;
  877. }
  878. return false;
  879. case 3:
  880. //key str (fails)
  881. keyed = 3;
  882. switch(f)
  883. {
  884. case 0:
  885. type.setown(makeIntType(1, false));
  886. name = createAtom("i");
  887. size = 1;
  888. return true;
  889. case 1:
  890. type.setown(makeIntType(4, false));
  891. name = createAtom("j");
  892. size = 4;
  893. return true;
  894. case 2:
  895. type.setown(makeStringType(8));
  896. name = createAtom("str");
  897. size = 8;
  898. return true;
  899. }
  900. return false;
  901. case 4:
  902. //move str into key (fails)
  903. keyed = 2;
  904. switch(f)
  905. {
  906. case 0:
  907. type.setown(makeIntType(1, false));
  908. name = createAtom("i");
  909. size = 1;
  910. return true;
  911. case 1:
  912. type.setown(makeStringType(8));
  913. name = createAtom("str");
  914. size = 8;
  915. return true;
  916. case 2:
  917. type.setown(makeIntType(4, false));
  918. name = createAtom("j");
  919. size = 4;
  920. return true;
  921. }
  922. return false;
  923. case 5:
  924. //drop j
  925. keyed = 1;
  926. switch(f)
  927. {
  928. case 0:
  929. type.setown(makeIntType(1, false));
  930. name = createAtom("i");
  931. size = 1;
  932. return true;
  933. case 1:
  934. type.setown(makeStringType(8));
  935. name = createAtom("str");
  936. size = 8;
  937. return true;
  938. }
  939. return false;
  940. case 6:
  941. //drop str
  942. keyed = 2;
  943. switch(f)
  944. {
  945. case 0:
  946. type.setown(makeIntType(1, false));
  947. name = createAtom("i");
  948. size = 1;
  949. return true;
  950. case 1:
  951. type.setown(makeIntType(4, false));
  952. name = createAtom("j");
  953. size = 4;
  954. return true;
  955. }
  956. return false;
  957. case 7:
  958. //add new field
  959. keyed = 2;
  960. switch(f)
  961. {
  962. case 0:
  963. type.setown(makeIntType(1, false));
  964. name = createAtom("i");
  965. size = 1;
  966. return true;
  967. case 1:
  968. type.setown(makeIntType(4, false));
  969. name = createAtom("j");
  970. size = 4;
  971. return true;
  972. case 2:
  973. type.setown(makeStringType(8));
  974. name = createAtom("str");
  975. size = 8;
  976. return true;
  977. case 3:
  978. type.setown(makeStringType(8));
  979. name = createAtom("other");
  980. size = 8;
  981. return true;
  982. }
  983. return false;
  984. case 8:
  985. //rename field
  986. keyed = 2;
  987. switch(f)
  988. {
  989. case 0:
  990. type.setown(makeIntType(1, false));
  991. name = createAtom("i");
  992. size = 1;
  993. return true;
  994. case 1:
  995. type.setown(makeIntType(4, false));
  996. name = createAtom("j");
  997. size = 4;
  998. return true;
  999. case 2:
  1000. type.setown(makeStringType(8));
  1001. name = createAtom("other");
  1002. size = 8;
  1003. return true;
  1004. }
  1005. return false;
  1006. case 9:
  1007. //change type
  1008. keyed = 2;
  1009. switch(f)
  1010. {
  1011. case 0:
  1012. type.setown(makeIntType(1, false));
  1013. name = createAtom("i");
  1014. size = 1;
  1015. return true;
  1016. case 1:
  1017. type.setown(makeIntType(4, false));
  1018. name = createAtom("j");
  1019. size = 4;
  1020. return true;
  1021. case 2:
  1022. type.setown(makeStringType(9));
  1023. name = createAtom("str");
  1024. size = 9;
  1025. return true;
  1026. }
  1027. return false;
  1028. }
  1029. return false;
  1030. }
  1031. void doTranslate(unsigned disk, unsigned activity)
  1032. {
  1033. Owned<IRecordLayoutTranslator> trans = new CRecordLayoutTranslator(&meta.item(disk), &meta.item(activity));
  1034. CPPUNIT_ASSERT(trans.get() != NULL);
  1035. CPPUNIT_ASSERT(trans->querySuccess());
  1036. }
  1037. void doTranslateFail(unsigned disk, unsigned activity, unsigned code)
  1038. {
  1039. Owned<IRecordLayoutTranslator> trans = new CRecordLayoutTranslator(&meta.item(disk), &meta.item(activity));
  1040. CPPUNIT_ASSERT(trans.get() != 0);
  1041. CPPUNIT_ASSERT(!trans->querySuccess());
  1042. CPPUNIT_ASSERT(trans->queryFailure().queryCode() == code);
  1043. }
  1044. private:
  1045. IArrayOf<IDefRecordMeta> meta;
  1046. MemoryBuffer * buff;
  1047. Owned<IRecordLayoutTranslatorCache> cache;
  1048. };
  1049. CPPUNIT_TEST_SUITE_REGISTRATION(RecordLayoutTranslatorTest);
  1050. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RecordLayoutTranslatorTest, "RecordLayoutTranslatorTest");
  1051. #endif