fvrelate.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jliball.hpp"
  14. #include "eclrtl.hpp"
  15. #include "fileview.hpp"
  16. #include "fverror.hpp"
  17. #include "fvrelate.ipp"
  18. #include "deftype.hpp"
  19. #include "fvresultset.ipp"
  20. #include "dasess.hpp"
  21. //---------------------------------------------------------------------------
  22. void ViewFile::addRelation(ViewRelation & _relation)
  23. {
  24. relations.append(_relation);
  25. }
  26. void ViewFile::addSuperFile(ViewFile & _superFile)
  27. {
  28. if (!superFiles.contains(_superFile))
  29. superFiles.append(_superFile);
  30. }
  31. void ViewFile::addSubFile(ViewFile & _subFile)
  32. {
  33. throwUnexpected();
  34. }
  35. const char * ViewFile::queryFilename() const
  36. {
  37. return definition->queryLogicalName();
  38. }
  39. INewResultSet * ViewFile::queryResultSet()
  40. {
  41. if (!cachedResultSet)
  42. {
  43. //This used to protect against exceptions - but complicated indexes now complain when the cursor is created, so no need to protect this.
  44. cachedResultSet.setown(options.resultSetFactory->createNewFileResultSet(queryFilename(), options.cluster));
  45. }
  46. return cachedResultSet;
  47. }
  48. void ViewSuperFile::addSubFile(ViewFile & _subFile)
  49. {
  50. if (!subFiles.contains(_subFile))
  51. subFiles.append(_subFile);
  52. }
  53. //---------------------------------------------------------------------------
  54. void ViewJoinColumnMapping::addFilterToPrimary(IFilteredResultSet * primaryResultSet, IResultSetCursor * secondaryCursor)
  55. {
  56. primary->clearFilter(primaryResultSet);
  57. if (secondaryIteratorColumn != NotFound)
  58. {
  59. Owned<IResultSetCursor> childCursor = secondaryCursor->getChildren(secondaryIteratorColumn);
  60. if (childCursor->first())
  61. {
  62. do
  63. {
  64. doAddFilterToPrimary(primaryResultSet, childCursor);
  65. } while (childCursor->next());
  66. }
  67. }
  68. else
  69. doAddFilterToPrimary(primaryResultSet, secondaryCursor);
  70. }
  71. void ViewJoinColumnMapping::addFilterToSecondary(IFilteredResultSet * secondaryResultSet, IResultSetCursor * primaryCursor)
  72. {
  73. assertex(secondaryIteratorColumn == NotFound);
  74. MemoryAttr value;
  75. primary->getValue(value, primaryCursor);
  76. secondary->clearFilter(secondaryResultSet);
  77. secondary->addFilter(secondaryResultSet, value);
  78. }
  79. void ViewJoinColumnMapping::doAddFilterToPrimary(IFilteredResultSet * primaryResultSet, IResultSetCursor * secondaryCursor)
  80. {
  81. MemoryAttr value;
  82. secondary->getValue(value, secondaryCursor);
  83. primary->addFilter(primaryResultSet, value);
  84. }
  85. bool ViewJoinColumnMapping::canMapPrimaryToSecondary() const
  86. {
  87. return (secondaryIteratorColumn == NotFound) && primary->canGet() && secondary->canSet();
  88. }
  89. bool ViewJoinColumnMapping::canMapSecondaryToPrimary() const
  90. {
  91. return primary->canSet() && secondary->canGet();
  92. }
  93. //---------------------------------------------------------------------------
  94. inline bool isSameString(const char * left, const char * right)
  95. {
  96. if (left == right)
  97. return true;
  98. if (left && right)
  99. return strcmp(left, right) == 0;
  100. return false;
  101. }
  102. void ViewRelation::addFilterToPrimary(IFilteredResultSet * primaryResultSet, IResultSetCursor * secondaryCursor)
  103. {
  104. ForEachItemIn(i, columnMappings)
  105. columnMappings.item(i).addFilterToPrimary(primaryResultSet, secondaryCursor);
  106. }
  107. void ViewRelation::addFilterToSecondary(IFilteredResultSet * secondaryResultSet, IResultSetCursor * primaryCursor)
  108. {
  109. ForEachItemIn(i, columnMappings)
  110. columnMappings.item(i).addFilterToSecondary(secondaryResultSet, primaryCursor);
  111. }
  112. unsigned ViewRelation::queryMappingField(unsigned whichMapping, bool needPrimary) const
  113. {
  114. if (whichMapping >= columnMappings.ordinality())
  115. return NotFound;
  116. ViewJoinColumnMapping & mapping = columnMappings.item(whichMapping);
  117. if (needPrimary)
  118. return mapping.primary->queryBaseColumn();
  119. return mapping.secondary->queryBaseColumn();
  120. }
  121. //Not in the constructor so exceptions can be handled cleanly.
  122. void ViewRelation::init()
  123. {
  124. INewResultSet * primaryResultSet = primaryFile->queryResultSet();
  125. INewResultSet * secondaryResultSet = secondaryFile->queryResultSet();
  126. if (primaryResultSet && secondaryResultSet)
  127. {
  128. FieldTransformInfoArray primaryFields;
  129. FieldTransformInfoArray secondaryFields;
  130. parseColumnMappingList(primaryFields, primaryResultSet->getMetaData(), false, definition->queryPrimaryFields());
  131. parseColumnMappingList(secondaryFields, secondaryResultSet->getMetaData(), true, definition->querySecondaryFields());
  132. //MORE: Check that if a secondary dataset column is specified that there is only one of them.
  133. if (primaryFields.ordinality() == secondaryFields.ordinality())
  134. {
  135. ForEachItemIn(i, primaryFields)
  136. {
  137. FieldTransformInfo & curPrimary = primaryFields.item(i);
  138. FieldTransformInfo & curSecondary = secondaryFields.item(i);
  139. Owned<ViewJoinColumn> primaryColumn = new ViewJoinColumn(curPrimary.column, curPrimary.getTransforms, curPrimary.setTransforms);
  140. Owned<ViewJoinColumn> secondaryColumn = new ViewJoinColumn(curSecondary.column, curSecondary.getTransforms, curSecondary.setTransforms);
  141. columnMappings.append(* new ViewJoinColumnMapping(primaryColumn, secondaryColumn, curSecondary.datasetColumn));
  142. }
  143. }
  144. else
  145. throwError4(FVERR_NumJoinFieldsMismatchXY, primaryFields.ordinality(), secondaryFields.ordinality(), primaryFile->queryFilename(), secondaryFile->queryFilename());
  146. }
  147. }
  148. bool ViewRelation::matches(IFileRelationship * searchDefinition, ViewFile * searchPrimaryFile, ViewFile * searchSecondaryFile) const
  149. {
  150. if ((searchPrimaryFile != primaryFile) || (searchSecondaryFile != secondaryFile))
  151. return false;
  152. if (!isSameString(definition->queryPrimaryFields(), searchDefinition->queryPrimaryFields()))
  153. return false;
  154. if (!isSameString(definition->querySecondaryFields(), searchDefinition->querySecondaryFields()))
  155. return false;
  156. if (!isSameString(definition->queryKind(), searchDefinition->queryKind()))
  157. return false;
  158. if (definition->isPayload() != searchDefinition->isPayload())
  159. return false;
  160. return true;
  161. }
  162. bool ViewRelation::canMapPrimaryToSecondary(bool isEfficient) const
  163. {
  164. //MORE: Implement isEfficient!
  165. ForEachItemIn(i, columnMappings)
  166. if (!columnMappings.item(i).canMapPrimaryToSecondary())
  167. return false;
  168. return true;
  169. }
  170. bool ViewRelation::canMapSecondaryToPrimary(bool isEfficient) const
  171. {
  172. ForEachItemIn(i, columnMappings)
  173. if (!columnMappings.item(i).canMapSecondaryToPrimary())
  174. return false;
  175. return true;
  176. }
  177. void ViewRelation::queryPrimaryColumns(UnsignedArray & columns)
  178. {
  179. ForEachItemIn(i, columnMappings)
  180. {
  181. ViewJoinColumnMapping & mapping = columnMappings.item(i);
  182. columns.append(mapping.primary->queryBaseColumn());
  183. }
  184. }
  185. void ViewRelation::querySecondaryColumns(UnsignedArray & columns)
  186. {
  187. ForEachItemIn(i, columnMappings)
  188. {
  189. ViewJoinColumnMapping & mapping = columnMappings.item(i);
  190. if (mapping.secondaryIteratorColumn != NotFound)
  191. columns.append(mapping.secondaryIteratorColumn);
  192. else
  193. columns.append(mapping.secondary->queryBaseColumn());
  194. }
  195. }
  196. //---------------------------------------------------------------------------
  197. unsigned BrowseCreateInfo::getNestingCount(const ViewFile * search)
  198. {
  199. unsigned count = 0;
  200. ForEachItemIn(i, nestedFiles)
  201. {
  202. if (&nestedFiles.item(i) == search)
  203. count++;
  204. }
  205. return count;
  206. }
  207. //---------------------------------------------------------------------------
  208. ViewFileWeb::ViewFileWeb(IResultSetFactory & resultSetFactory, const char * cluster, IUserDescriptor *user)
  209. : viewOptions(resultSetFactory, cluster), directory(queryDistributedFileDirectory()), udesc(user)
  210. {
  211. nextId = 0;
  212. }
  213. ViewFile * ViewFileWeb::addFile(IDistributedFile * definition)
  214. {
  215. ViewFile * file;
  216. if (definition->querySuperFile())
  217. file = new ViewSuperFile(++nextId, viewOptions, definition);
  218. else
  219. file = new ViewFile(++nextId, viewOptions, definition);
  220. files.append(*file);
  221. return file;
  222. }
  223. ViewRelation * ViewFileWeb::addRelation(IFileRelationship * definition, ViewFile * primaryFile, ViewFile * secondaryFile)
  224. {
  225. ForEachItemIn(i, relations)
  226. {
  227. ViewRelation & cur = relations.item(i);
  228. if (cur.matches(definition, primaryFile, secondaryFile))
  229. return &cur;
  230. }
  231. Owned<ViewRelation> relation = new ViewRelation(++nextId, definition, primaryFile, secondaryFile);
  232. relation->init();
  233. relations.append(*LINK(relation));
  234. primaryFile->addRelation(*relation);
  235. secondaryFile->addRelation(*relation);
  236. return relation;
  237. }
  238. IFileTreeBrowser * ViewFileWeb::createBrowseTree(const char * browseRootFilename, bool isEfficient, unsigned maxRecursion)
  239. {
  240. ViewFile * root = findFile(browseRootFilename);
  241. if (!root)
  242. throwError1(FVERR_CouldNotResolveX, browseRootFilename);
  243. CFileTreeBrowser * browser = new CFileTreeBrowser(this);
  244. BrowseCreateInfo info(browser, isEfficient, maxRecursion);
  245. gatherBrowseFiles(info, root, NULL, NULL);
  246. return browser;
  247. }
  248. ViewFile * ViewFileWeb::findFile(const char * filename)
  249. {
  250. ForEachItemIn(i, files)
  251. {
  252. ViewFile & cur = files.item(i);
  253. if (cur.matches(filename))
  254. return &cur;
  255. }
  256. return NULL;
  257. }
  258. void ViewFileWeb::gatherBrowseFiles(BrowseCreateInfo & info, ViewFile * file, CRelatedBrowseFile * parentFile, ViewRelation * parentRelation)
  259. {
  260. if (info.getNestingCount(file) >= info.maxRecursion)
  261. return;
  262. CRelatedBrowseFile * browseFile = info.browser->addFile(file, parentFile, parentRelation);
  263. info.nestedFiles.append(*file);
  264. ForEachItemIn(i2, file->relations)
  265. {
  266. ViewRelation & cur = file->relations.item(i2);
  267. if (&cur != parentRelation)
  268. {
  269. if (stricmp(cur.queryDefinition()->queryKind(), S_LINK_RELATIONSHIP_KIND) != 0)
  270. continue;
  271. //Check for secondary first - so that recursive definitions default to secondary->primary
  272. if (file == cur.querySecondary())
  273. {
  274. if (cur.canMapSecondaryToPrimary(info.isEfficient))
  275. gatherBrowseFiles(info, cur.queryPrimary(), browseFile, &cur);
  276. }
  277. else if (file == cur.queryPrimary())
  278. {
  279. if (cur.canMapPrimaryToSecondary(info.isEfficient))
  280. gatherBrowseFiles(info, cur.querySecondary(), browseFile, &cur);
  281. }
  282. }
  283. }
  284. info.nestedFiles.pop();
  285. }
  286. void ViewFileWeb::gatherWeb(const char * rootFilename, const ViewGatherOptions & options)
  287. {
  288. gatherWeb(rootFilename, NULL, options);
  289. }
  290. void ViewFileWeb::gatherWeb(const char * rootFilename, IDistributedFile * alreadyResolved, const ViewGatherOptions & options)
  291. {
  292. ViewWalkOptions localOptions(options);
  293. if (!localOptions.kind)
  294. localOptions.kind = S_LINK_RELATIONSHIP_KIND;
  295. localOptions.isExplicitFile = true;
  296. if (!walkFile(rootFilename, alreadyResolved, localOptions))
  297. throwError1(FVERR_CouldNotResolveX, rootFilename);
  298. //MORE: Should possibly percolate relations between superfiles down to files they contain.
  299. }
  300. void ViewFileWeb::gatherWebFromPattern(const char * filenamePattern, const ViewGatherOptions & options)
  301. {
  302. ViewWalkOptions localOptions(options);
  303. if (!localOptions.kind)
  304. localOptions.kind = S_LINK_RELATIONSHIP_KIND;
  305. Owned<IDistributedFileIterator> iter = queryDistributedFileDirectory().getIterator(filenamePattern, false, udesc);
  306. if (iter->first())
  307. {
  308. do
  309. {
  310. IDistributedFile & cur = iter->query();
  311. localOptions.isExplicitFile = true;
  312. walkFile(cur.queryLogicalName(), &cur, localOptions);
  313. } while (iter->next());
  314. }
  315. else
  316. throwError1(FVERR_CouldNotResolveX, filenamePattern);
  317. //MORE: Should possibly percolate relations between superfiles down to files they contain.
  318. }
  319. IViewRelatedFileIterator * ViewFileWeb::getFileIterator()
  320. {
  321. return new CViewRelatedFileIterator(files, 0, this);
  322. }
  323. IViewRelatedFile * ViewFileWeb::queryFile(unsigned i)
  324. {
  325. if (files.isItem(i))
  326. return &files.item(i);
  327. return NULL;
  328. }
  329. void ViewFileWeb::walk(IViewFileWebVisitor & visitor)
  330. {
  331. ForEachItemIn(i1, files)
  332. visitor.visit(&files.item(i1));
  333. ForEachItemIn(i2, relations)
  334. visitor.visit(&relations.item(i2));
  335. }
  336. ViewFile * ViewFileWeb::walkFile(const char * filename, IDistributedFile * alreadyResolved, ViewWalkOptions & options)
  337. {
  338. ViewFile * thisFile = findFile(filename);
  339. if (thisFile)
  340. return thisFile;
  341. if (options.explicitFilesOnly)
  342. {
  343. if (!options.isExplicitFile)
  344. return NULL;
  345. options.isExplicitFile = false;
  346. }
  347. Owned<IDistributedFile> resolved = alreadyResolved ? LINK(alreadyResolved) : directory.lookup(filename,udesc,false,false,true); // lock super-owners
  348. if (!resolved)
  349. return NULL;
  350. thisFile = addFile(resolved);
  351. if (options.maximumDepth > 0)
  352. {
  353. options.maximumDepth--;
  354. if (options.primaryDepth > 0)
  355. {
  356. options.primaryDepth--;
  357. Owned<IFileRelationshipIterator> iter = directory.lookupFileRelationships(NULL, filename, NULL, NULL, options.kind, NULL, options.payload);
  358. ForEach(*iter)
  359. {
  360. IFileRelationship & cur = iter->query();
  361. ViewFile * otherFile = walkFile(cur.queryPrimaryFilename(), NULL, options);
  362. if (otherFile)
  363. {
  364. addRelation(&cur, otherFile, thisFile);
  365. }
  366. }
  367. options.primaryDepth++;
  368. }
  369. if (options.secondaryDepth > 0)
  370. {
  371. options.secondaryDepth--;
  372. Owned<IFileRelationshipIterator> iter = directory.lookupFileRelationships(filename, NULL, NULL, NULL, options.kind, NULL, options.payload);
  373. ForEach(*iter)
  374. {
  375. IFileRelationship & cur = iter->query();
  376. ViewFile * otherFile = walkFile(cur.querySecondaryFilename(), NULL, options);
  377. if (otherFile)
  378. {
  379. addRelation(&cur, thisFile, otherFile);
  380. }
  381. }
  382. options.primaryDepth++;
  383. }
  384. if ((options.superDepth > 0))
  385. {
  386. options.superDepth--;
  387. Owned<IDistributedSuperFileIterator> iter = resolved->getOwningSuperFiles();
  388. ForEach(*iter)
  389. {
  390. IDistributedSuperFile & cur = iter->query();
  391. //ViewFile * otherFile = walkFile(cur.queryLogicalName(), &cur, options): // could pass in to save lookup
  392. ViewFile * otherFile = walkFile(cur.queryLogicalName(), &cur, options);
  393. if (otherFile)
  394. {
  395. thisFile->addSuperFile(*otherFile);
  396. otherFile->addSubFile(*thisFile);
  397. }
  398. }
  399. options.superDepth++;
  400. }
  401. IDistributedSuperFile * super = resolved->querySuperFile();
  402. if ((options.subDepth > 0) && super)
  403. {
  404. options.subDepth--;
  405. Owned<IDistributedFileIterator> iter = super->getSubFileIterator(false);
  406. ForEach(*iter)
  407. {
  408. IDistributedFile & cur = iter->query();
  409. ViewFile * otherFile = walkFile(cur.queryLogicalName(), &cur, options);
  410. if (otherFile)
  411. {
  412. thisFile->addSubFile(*otherFile);
  413. otherFile->addSuperFile(*thisFile);
  414. }
  415. }
  416. options.subDepth++;
  417. }
  418. options.maximumDepth--;
  419. }
  420. return thisFile;
  421. }
  422. //---------------------------------------------------------------------------
  423. FilteredSecondaryResultSetCursor::FilteredSecondaryResultSetCursor(INewResultSet * _resultSet, IResultSetCursor * _primary, ViewRelation * _relation)
  424. : DelayedFilteredResultSetCursor(_resultSet)
  425. {
  426. primary = _primary;
  427. relation = _relation;
  428. }
  429. void FilteredSecondaryResultSetCursor::noteRelatedFileChanged()
  430. {
  431. DelayedFilteredResultSetCursor::noteRelatedFileChanged();
  432. clearFilters();
  433. }
  434. void FilteredSecondaryResultSetCursor::ensureFiltered()
  435. {
  436. relation->addFilterToSecondary(filtered, primary);
  437. }
  438. //---------------------------------------------------------------------------
  439. FilteredPrimaryResultSetCursor::FilteredPrimaryResultSetCursor(INewResultSet * _resultSet, IResultSetCursor * _secondary, ViewRelation * _relation)
  440. : DelayedFilteredResultSetCursor(_resultSet)
  441. {
  442. secondary = _secondary;
  443. relation = _relation;
  444. }
  445. void FilteredPrimaryResultSetCursor::noteRelatedFileChanged()
  446. {
  447. DelayedFilteredResultSetCursor::noteRelatedFileChanged();
  448. clearFilters();
  449. }
  450. void FilteredPrimaryResultSetCursor::ensureFiltered()
  451. {
  452. relation->addFilterToPrimary(filtered, secondary);
  453. }
  454. //---------------------------------------------------------------------------
  455. CRelatedBrowseFile::CRelatedBrowseFile(ViewFile * _file, CRelatedBrowseFile * _parent, ViewRelation * _relation)
  456. {
  457. file = _file;
  458. parent = _parent;
  459. relation = _relation;
  460. INewResultSet * resultSet = file->queryResultSet();
  461. if (!resultSet)
  462. throwError1(FVERR_CannotBrowseFile, file->queryFilename());
  463. if (!parent)
  464. {
  465. filtered.setown(new DelayedFilteredResultSetCursor(resultSet));
  466. }
  467. else
  468. {
  469. if (file == relation->querySecondary())
  470. {
  471. filtered.setown(new FilteredSecondaryResultSetCursor(resultSet, parent->queryCursor(), relation));
  472. }
  473. else
  474. {
  475. filtered.setown(new FilteredPrimaryResultSetCursor(resultSet, parent->queryCursor(), relation));
  476. }
  477. }
  478. notifier.setown(new NotifyingResultSetCursor(filtered));
  479. }
  480. void CRelatedBrowseFile::addChild(CRelatedBrowseFile * child)
  481. {
  482. children.append(*child);
  483. notifier->addDependent(*child->notifier);
  484. }
  485. IViewRelatedFile * CRelatedBrowseFile::queryDefinition() const
  486. {
  487. return file;
  488. }
  489. IRelatedBrowseFile * CRelatedBrowseFile::queryParent() const
  490. {
  491. return parent;
  492. }
  493. IViewRelation * CRelatedBrowseFile::queryParentRelation() const
  494. {
  495. return relation;
  496. }
  497. IRelatedBrowseFile * CRelatedBrowseFile::queryChild(unsigned i) const
  498. {
  499. if (children.isItem(i))
  500. return &children.item(i);
  501. return NULL;
  502. }
  503. IResultSetCursor * CRelatedBrowseFile::queryCursor()
  504. {
  505. return notifier;
  506. }
  507. //---------------------------------------------------------------------------
  508. CFileTreeBrowser::CFileTreeBrowser(ViewFileWeb * _web) : web(_web)
  509. {
  510. }
  511. CRelatedBrowseFile * CFileTreeBrowser::addFile(ViewFile * file, CRelatedBrowseFile * parent, ViewRelation * relation)
  512. {
  513. CRelatedBrowseFile * next = new CRelatedBrowseFile(file, parent, relation);
  514. files.append(*next);
  515. if (parent)
  516. parent->addChild(next);
  517. return next;
  518. }
  519. IRelatedBrowseFile * CFileTreeBrowser::queryRootFile()
  520. {
  521. return &files.item(0);
  522. }
  523. IResultSetFilter * CFileTreeBrowser::queryRootFilter()
  524. {
  525. return &files.item(0);
  526. }
  527. //---------------------------------------------------------------------------
  528. void ViewERdiagramVisitor::visit(ViewFile * file)
  529. {
  530. activeFileId.clear().append(file->queryId());
  531. activeFieldCount = 0;
  532. IDistributedFile * definition = file->queryDefinition();
  533. builder.beginFile(activeFileId, file->queryFilename(), definition);
  534. activeFieldPrefix.clear().append(activeFileId).append("_");
  535. INewResultSet * resultSet = file->queryResultSet();
  536. if (resultSet)
  537. {
  538. const IResultSetMetaData & imeta = resultSet->getMetaData();
  539. //Cheat and use an existing internal function
  540. const CResultSetMetaData & meta = static_cast<const CResultSetMetaData &>(imeta);
  541. meta.getXmlSchema(*this, false);
  542. }
  543. else
  544. {
  545. StringBuffer dummyFieldId;
  546. dummyFieldId.append(activeFieldPrefix).append(0);
  547. builder.noteField(dummyFieldId, "**field-information-unavailable**", "");
  548. }
  549. builder.endFile();
  550. }
  551. void ViewERdiagramVisitor::visit(ViewRelation * relation)
  552. {
  553. UnsignedArray primaryColumns;
  554. UnsignedArray secondaryColumns;
  555. relation->queryPrimaryColumns(primaryColumns);
  556. relation->querySecondaryColumns(secondaryColumns);
  557. unsigned sourceFieldId;
  558. unsigned targetFieldId;
  559. if (primaryColumns.ordinality())
  560. {
  561. sourceFieldId = primaryColumns.item(0);
  562. targetFieldId = secondaryColumns.item(0);
  563. }
  564. else
  565. {
  566. sourceFieldId = relation->queryPrimary()->queryFirstFieldId();
  567. targetFieldId = relation->querySecondary()->queryFirstFieldId();
  568. }
  569. StringBuffer id, sourceId, targetId;
  570. id.append(relation->queryId());
  571. sourceId.append(relation->queryPrimary()->queryId()).append("_").append(sourceFieldId);
  572. targetId.append(relation->querySecondary()->queryId()).append("_").append(targetFieldId);
  573. builder.noteRelation(id, sourceId, targetId, relation->queryDefinition());
  574. }
  575. void ViewERdiagramVisitor::addField(const char * name, ITypeInfo & type, bool keyed)
  576. {
  577. StringBuffer eclTypeName;
  578. noteNextField();
  579. type.getECLType(eclTypeName);
  580. if (name && (stricmp(name, "__fileposition__") == 0))
  581. builder.noteField(activeFieldId.str(), "{virtual(fileposition)}", eclTypeName);
  582. else
  583. builder.noteField(activeFieldId.str(), name, eclTypeName);
  584. }
  585. void ViewERdiagramVisitor::addSetField(const char * name, const char * itemname, ITypeInfo & type)
  586. {
  587. addField(name, type, false);
  588. }
  589. void ViewERdiagramVisitor::beginIfBlock()
  590. {
  591. activeFieldCount++;
  592. }
  593. bool ViewERdiagramVisitor::beginDataset(const char * name, const char * childname, bool mixed, unsigned *updateMixed)
  594. {
  595. noteNextField();
  596. builder.noteField(activeFieldId.str(), name, "dataset");
  597. savedFieldCounts.append(activeFieldCount);
  598. savedFieldCounts.append(activeFieldPrefix.length());
  599. activeFieldPrefix.append(activeFieldCount).append("_");
  600. activeFieldCount = 0;
  601. return false; // don't expand contents
  602. }
  603. void ViewERdiagramVisitor::beginRecord(const char * name, bool mixedContent, unsigned *updateMixed)
  604. {
  605. noteNextField();
  606. builder.noteField(activeFieldId.str(), name, "record");
  607. builder.beginCompound();
  608. }
  609. void ViewERdiagramVisitor::endIfBlock()
  610. {
  611. activeFieldCount++;
  612. }
  613. void ViewERdiagramVisitor::endDataset(const char * name, const char * childname)
  614. {
  615. activeFieldPrefix.setLength(savedFieldCounts.popGet());
  616. activeFieldCount = savedFieldCounts.popGet();
  617. }
  618. void ViewERdiagramVisitor::endRecord(const char * name)
  619. {
  620. builder.endCompound();
  621. activeFieldCount++;
  622. }
  623. void ViewERdiagramVisitor::noteNextField()
  624. {
  625. activeFieldId.clear().append(activeFieldPrefix).append(activeFieldCount++);
  626. }
  627. //---------------------------------------------------------------------------
  628. ViewXgmmlERdiagramVisitor & ViewXgmmlERdiagramVisitor::addAttr(const char * name, const char * value)
  629. {
  630. xgmml.append(" ").append(name).append("=\"");
  631. encodeXML(value, xgmml, ENCODE_NEWLINES, (unsigned)-1, false);
  632. xgmml.append("\"");
  633. return *this;
  634. }
  635. ViewXgmmlERdiagramVisitor & ViewXgmmlERdiagramVisitor::addAttrTag(const char * name, const char * value)
  636. {
  637. if (value && *value)
  638. {
  639. xgmml.append("<attr");
  640. addAttr("name", name).addAttr("value", value);
  641. xgmml.append("/>").newline();
  642. }
  643. return *this;
  644. }
  645. void ViewXgmmlERdiagramVisitor::beginFile(const char * id, const char * name, IDistributedFile * definition)
  646. {
  647. xgmml.append(" <node");
  648. addAttr("id", id).addAttr("label", name);
  649. xgmml.append("><att>").newline();
  650. xgmml.append(" <graph>").newline();
  651. }
  652. void ViewXgmmlERdiagramVisitor::noteField(const char * id, const char * name, const char * type)
  653. {
  654. xgmml.append(" <node id=\"").append(id).append("\" label=\"").append(name).append("\">").newline();
  655. addAttrTag("type", type);
  656. xgmml.append(" </node>").newline();
  657. }
  658. void ViewXgmmlERdiagramVisitor::endFile()
  659. {
  660. xgmml.append(" </graph>").newline();
  661. xgmml.append(" </att></node>").newline();
  662. }
  663. void ViewXgmmlERdiagramVisitor::noteRelation(const char * id, const char * primaryId, const char * secondaryId, IFileRelationship * definition)
  664. {
  665. StringBuffer temp;
  666. //Note we want the direction from the secondary to the primary, so need to reverse some things
  667. xgmml.append(" <edge");
  668. addAttr("id", id).addAttr("source", secondaryId).addAttr("target", primaryId);
  669. const char * kind = definition->queryKind();
  670. if (kind && (stricmp(kind, S_VIEW_RELATIONSHIP_KIND) == 0))
  671. addAttr("label", "view of");
  672. xgmml.append(">").newline();
  673. getInvertedCardinality(temp.clear(), definition->queryCardinality());
  674. addAttrTag("cardinality", temp.str());
  675. xgmml.append(" </edge>").newline();
  676. }
  677. void ViewXgmmlERdiagramVisitor::beginDiagram()
  678. {
  679. xgmml.append("<xgmml>").newline();
  680. xgmml.append("<graph>").newline();
  681. }
  682. void ViewXgmmlERdiagramVisitor::endDiagram()
  683. {
  684. xgmml.append("</graph>").newline();
  685. xgmml.append("</xgmml>").newline();
  686. }
  687. void ViewXgmmlERdiagramVisitor::beginCompound()
  688. {
  689. depth++;
  690. }
  691. void ViewXgmmlERdiagramVisitor::endCompound()
  692. {
  693. depth--;
  694. }
  695. IViewFileWeb * createViewFileWeb(IResultSetFactory & resultSetFactory, const char * cluster, IUserDescriptor *user)
  696. {
  697. return new ViewFileWeb(resultSetFactory, cluster, user);
  698. }
  699. void createERdiagram(StringBuffer & xgmml, IViewFileWeb & _web)
  700. {
  701. ViewFileWeb & web = static_cast<ViewFileWeb &>(_web);
  702. ViewXgmmlERdiagramVisitor builder(xgmml);
  703. ViewERdiagramVisitor visitor(builder);
  704. builder.beginDiagram();
  705. web.walk(visitor);
  706. builder.endDiagram();
  707. }