fvtransform.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991
  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 "thorplugin.hpp"
  21. #include "eclrtl_imp.hpp"
  22. #include "hqlfold.hpp"
  23. #include "hqlvalid.hpp"
  24. #include "hqlcollect.hpp"
  25. #include "hqlrepository.hpp"
  26. #include "hqlerror.hpp"
  27. #include "dasess.hpp"
  28. static ViewTransformerRegistry * theTransformerRegistry;
  29. static ITypeInfo * stringType;
  30. static ITypeInfo * utf8Type;
  31. static ITypeInfo * unicodeType;
  32. static IAtom * addAtom;
  33. MODULE_INIT(INIT_PRIORITY_STANDARD)
  34. {
  35. addAtom = createLowerCaseAtom("add");
  36. stringType = makeStringType(UNKNOWN_LENGTH, NULL, NULL);
  37. utf8Type = makeUtf8Type(UNKNOWN_LENGTH, NULL);
  38. unicodeType = makeUnicodeType(UNKNOWN_LENGTH, NULL);
  39. theTransformerRegistry = new ViewTransformerRegistry;
  40. theTransformerRegistry->addTransformer(new ViewFailTransformer);
  41. theTransformerRegistry->addTransformer(new ViewAddTransformer);
  42. return true;
  43. }
  44. MODULE_EXIT()
  45. {
  46. delete theTransformerRegistry;
  47. unicodeType->Release();
  48. utf8Type->Release();
  49. stringType->Release();
  50. }
  51. //---------------------------------------------------------------------------
  52. void CardinalityElement::init(unsigned len, const char * _text)
  53. {
  54. if (len == 0)
  55. {
  56. text.set("1");
  57. min = 1;
  58. max = 1;
  59. return;
  60. }
  61. text.set(_text, len);
  62. const char * dotdot = strstr(text, "..");
  63. if (dotdot)
  64. {
  65. min = rtlStrToUInt4(dotdot-text, text);
  66. if (stricmp(dotdot+2, "M") == 0)
  67. max = Unbounded;
  68. else
  69. max = rtlVStrToUInt4(dotdot+2);
  70. }
  71. else
  72. {
  73. if (stricmp(text, "M") == 0)
  74. {
  75. min = 0;
  76. max = Unbounded;
  77. }
  78. else
  79. {
  80. min = rtlVStrToUInt4(text);
  81. max = min;
  82. }
  83. }
  84. }
  85. void CardinalityMapping::init(const char * text)
  86. {
  87. const char * colon = strchr(text, ':');
  88. if (colon)
  89. {
  90. primary.init(colon-text, text);
  91. secondary.init(strlen(colon+1), colon+1);
  92. }
  93. else
  94. {
  95. //May as well try and make some sense of it
  96. primary.init(0, "");
  97. secondary.init(strlen(text), text);
  98. }
  99. }
  100. void getInvertedCardinality(StringBuffer & out, const char * cardinality)
  101. {
  102. if (cardinality)
  103. {
  104. CardinalityMapping mapping(cardinality);
  105. out.append(mapping.secondary.text).append(":").append(mapping.primary.text);
  106. }
  107. }
  108. //---------------------------------------------------------------------------
  109. ViewFieldTransformer * ViewFieldTransformer::bind(const HqlExprArray & args)
  110. {
  111. return LINK(this);
  112. }
  113. void ViewFieldTransformer::transform(MemoryAttr & utfTarget, const MemoryAttr & utfSrc)
  114. {
  115. //NB: The system utf8 functions typically take a length, whilst MemoryAttr provide a size.
  116. unsigned lenTarget;
  117. char * target;
  118. const char * source = static_cast<const char *>(utfSrc.get());
  119. unsigned lenSource = rtlUtf8Length((size32_t)utfSrc.length(), source);
  120. transform(lenTarget, target, lenSource, source);
  121. unsigned sizeTarget = rtlUtf8Size(lenTarget, target);
  122. utfTarget.setOwn(sizeTarget, target);
  123. }
  124. ViewFailTransformer::ViewFailTransformer() : ViewFieldTransformer(failAtom)
  125. {
  126. }
  127. void ViewFailTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  128. {
  129. throwError(FVERR_FailTransformation);
  130. }
  131. ViewExceptionTransformer::ViewExceptionTransformer(IException * _e) : ViewFieldTransformer(failAtom), e(_e)
  132. {
  133. }
  134. void ViewExceptionTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  135. {
  136. throw LINK(e);
  137. }
  138. ViewAddTransformer::ViewAddTransformer() : ViewFieldTransformer(addAtom)
  139. {
  140. }
  141. ViewAddTransformer::ViewAddTransformer(const HqlExprArray & _args) : ViewFieldTransformer(addAtom)
  142. {
  143. appendArray(args, _args);
  144. }
  145. ViewFieldTransformer * ViewAddTransformer::bind(const HqlExprArray & args)
  146. {
  147. return new ViewAddTransformer(args);
  148. }
  149. void ViewAddTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  150. {
  151. unsigned __int64 value = rtlUtf8ToInt(lenSource, source);
  152. if (args.ordinality())
  153. value += args.item(0).queryValue()->getIntValue();
  154. rtlInt8ToStrX(lenTarget, target, value);
  155. }
  156. //---------------------------------------------------------------------------
  157. void ViewFieldUtf8Transformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  158. {
  159. (*function)(lenTarget, target, lenSource, source);
  160. }
  161. void ViewFieldUnicodeTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  162. {
  163. unsigned lenUnicodeSrc;
  164. unsigned lenUnicodeTarget;
  165. rtlDataAttr unicodeSrc;
  166. rtlDataAttr unicodeTarget;
  167. rtlUtf8ToUnicodeX(lenUnicodeSrc, unicodeSrc.refustr(), lenSource, source);
  168. (*function)(lenUnicodeTarget, unicodeTarget.refustr(), lenUnicodeSrc, unicodeSrc.getustr());
  169. rtlUnicodeToUtf8X(lenTarget, target, lenUnicodeTarget, unicodeTarget.getustr());
  170. }
  171. void ViewFieldStringTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  172. {
  173. unsigned lenStringSrc;
  174. unsigned lenStringTarget;
  175. rtlDataAttr stringSrc;
  176. rtlDataAttr stringTarget;
  177. rtlUtf8ToStrX(lenStringSrc, stringSrc.refstr(), lenSource, source);
  178. (*function)(lenStringTarget, stringTarget.refstr(), lenStringSrc, stringSrc.getstr());
  179. rtlStrToUtf8X(lenTarget, target, lenStringTarget, stringTarget.getstr());
  180. }
  181. //---------------------------------------------------------------------------
  182. ViewFieldTransformer * ViewFieldECLTransformer::bind(const HqlExprArray & args)
  183. {
  184. if (args.ordinality() == 0)
  185. return LINK(this);
  186. return new ViewFieldBoundECLTransformer(this, args);
  187. }
  188. void ViewFieldECLTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source, const HqlExprArray & extraArgs)
  189. {
  190. Owned<ITypeInfo> sourceType = makeUtf8Type(lenSource, 0);
  191. IValue * sourceValue = createUtf8Value(source, LINK(sourceType));
  192. OwnedHqlExpr sourceExpr = createConstant(sourceValue);
  193. HqlExprArray actuals;
  194. actuals.append(*LINK(sourceExpr));
  195. appendArray(actuals, extraArgs);
  196. Owned<IErrorReceiver> errorReporter = createThrowingErrorReceiver();
  197. OwnedHqlExpr call = createBoundFunction(errorReporter, function, actuals, NULL, true);
  198. OwnedHqlExpr castValue = ensureExprType(call, utf8Type);
  199. OwnedHqlExpr folded = quickFoldExpression(castValue, NULL, 0);
  200. IValue * foldedValue = folded->queryValue();
  201. assertex(foldedValue);
  202. unsigned len = foldedValue->queryType()->getStringLen();
  203. const char * data = static_cast<const char *>(foldedValue->queryValue());
  204. unsigned size = rtlUtf8Size(len, data);
  205. lenTarget = len;
  206. target = (char *)rtlMalloc(size);
  207. memcpy(target, data, size);
  208. }
  209. void ViewFieldECLTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  210. {
  211. HqlExprArray extraArgs;
  212. transform(lenTarget, target, lenSource, source, extraArgs);
  213. }
  214. void ViewFieldBoundECLTransformer::transform(unsigned & lenTarget, char * & target, unsigned lenSource, const char * source)
  215. {
  216. transformer->transform(lenTarget, target, lenSource, source, args);
  217. }
  218. //---------------------------------------------------------------------------
  219. void ViewTransformerRegistry::addTransformer(ViewFieldTransformer * ownedTransformer)
  220. {
  221. transformers.append(*ownedTransformer);
  222. }
  223. void ViewTransformerRegistry::addFieldUtf8Transformer(const char * name, utf8FieldTransformerFunction func)
  224. {
  225. transformers.append(* new ViewFieldUtf8Transformer(createLowerCaseAtom(name), func));
  226. }
  227. void ViewTransformerRegistry::addFieldStringTransformer(const char * name, stringFieldTransformerFunction func)
  228. {
  229. transformers.append(* new ViewFieldStringTransformer(createLowerCaseAtom(name), func));
  230. }
  231. void ViewTransformerRegistry::addFieldUnicodeTransformer(const char * name, unicodeFieldTransformerFunction func)
  232. {
  233. transformers.append(* new ViewFieldUnicodeTransformer(createLowerCaseAtom(name), func));
  234. }
  235. void ViewTransformerRegistry::addPlugins(const char * name)
  236. {
  237. loadedPlugins.setown(new SafePluginMap(&pluginCtx, true));
  238. loadedPlugins->loadFromList(name);
  239. Owned<IErrorReceiver> errorReporter = createThrowingErrorReceiver();
  240. dataServer.setown(createNewSourceFileEclRepository(errorReporter, name, ESFallowplugins, 0));
  241. HqlScopeArray scopes;
  242. HqlParseContext parseCtx(dataServer, NULL, NULL);
  243. HqlLookupContext ctx(parseCtx, errorReporter);
  244. getRootScopes(scopes, dataServer, ctx);
  245. ForEachItemIn(i, scopes)
  246. {
  247. IHqlScope * scope = &scopes.item(i);
  248. HqlExprArray symbols;
  249. try
  250. {
  251. scope->ensureSymbolsDefined(ctx);
  252. scope->getSymbols(symbols);
  253. ForEachItemIn(j, symbols)
  254. {
  255. IHqlExpression & cur = symbols.item(j);
  256. if (cur.getOperator() == no_service)
  257. addServiceDefinition(&cur);
  258. }
  259. }
  260. catch (IException * e)
  261. {
  262. const char * name = str(scope->queryName());
  263. VStringBuffer msg("Error loading plugin %s", name);
  264. EXCLOG(e, msg.str());
  265. }
  266. }
  267. }
  268. static bool matchesSimpleTransformer(IHqlExpression * definition, ITypeInfo * type)
  269. {
  270. if (definition->queryBody()->queryType() != type)
  271. return false;
  272. if (definition->numChildren() != 2)
  273. return false;
  274. if (definition->queryChild(1)->queryType() != type)
  275. return false;
  276. return true;
  277. }
  278. void * ViewTransformerRegistry::resolveExternal(IHqlExpression * funcdef)
  279. {
  280. IHqlExpression *body = funcdef->queryChild(0);
  281. StringBuffer entry;
  282. StringBuffer lib;
  283. getAttribute(body, entrypointAtom, entry);
  284. getAttribute(body, libraryAtom, lib);
  285. if (!lib.length())
  286. getAttribute(body, pluginAtom, lib);
  287. ensureFileExtension(lib, SharedObjectExtension);
  288. Owned<ILoadedDllEntry> match = loadedPlugins->getPluginDll(lib.str(), NULL, false); // MORE - shouldn't it check the version????
  289. if (!match)
  290. return NULL;
  291. return GetSharedProcedure(match->getInstance(), entry.str());
  292. }
  293. ViewFieldTransformer * ViewTransformerRegistry::createTransformer(IHqlExpression * funcdef)
  294. {
  295. IHqlExpression *body = funcdef->queryChild(0);
  296. if(!body)
  297. return NULL;
  298. StringBuffer entry;
  299. StringBuffer lib;
  300. getAttribute(body, entrypointAtom, entry);
  301. getAttribute(body, libraryAtom, lib);
  302. if (!lib.length())
  303. getAttribute(body, pluginAtom, lib);
  304. if ((entry.length() == 0) || (lib.length() == 0))
  305. return NULL;
  306. if(!body->hasAttribute(pureAtom) && !body->hasAttribute(templateAtom))
  307. return NULL;
  308. if(!body->hasAttribute(cAtom))
  309. return NULL;
  310. if(body->hasAttribute(gctxmethodAtom) || body->hasAttribute(ctxmethodAtom) || body->hasAttribute(omethodAtom))
  311. return NULL;
  312. if(body->hasAttribute(contextAtom) || body->hasAttribute(globalContextAtom))
  313. return NULL;
  314. //Special case string->string mapping (e.g., uppercase)
  315. if (matchesSimpleTransformer(funcdef, stringType))
  316. {
  317. stringFieldTransformerFunction resolved = (stringFieldTransformerFunction)resolveExternal(funcdef);
  318. if (resolved)
  319. return new ViewFieldStringTransformer(funcdef->queryName(), resolved);
  320. }
  321. //Special case string->string mapping (e.g., uppercase)
  322. if (matchesSimpleTransformer(funcdef, unicodeType))
  323. {
  324. unicodeFieldTransformerFunction resolved = (unicodeFieldTransformerFunction)resolveExternal(funcdef);
  325. if (resolved)
  326. return new ViewFieldUnicodeTransformer(funcdef->queryName(), resolved);
  327. }
  328. //MORE: special case string->string etc.
  329. return new ViewFieldECLTransformer(funcdef);
  330. }
  331. void ViewTransformerRegistry::addServiceDefinition(IHqlExpression * service)
  332. {
  333. Owned<ViewServiceEntry> entry = new ViewServiceEntry;
  334. entry->name.set(str(service->queryName()));
  335. HqlExprArray symbols;
  336. service->queryScope()->getSymbols(symbols);
  337. ForEachItemIn(i, symbols)
  338. {
  339. IHqlExpression & cur = symbols.item(i);
  340. if (cur.getOperator() == no_funcdef && cur.queryChild(0)->getOperator() == no_external)
  341. {
  342. ViewFieldTransformer * transformer = createTransformer(&cur);
  343. if (transformer)
  344. entry->transformers.append(*transformer);
  345. }
  346. }
  347. plugins.append(*entry.getClear());
  348. }
  349. ViewFieldTransformer * find(const ViewFieldTransformerArray & transformers, const char * name, const HqlExprArray & args)
  350. {
  351. if (!name)
  352. return NULL;
  353. IIdAtom * search = createIdAtom(name);
  354. ForEachItemIn(i, transformers)
  355. {
  356. ViewFieldTransformer & cur = transformers.item(i);
  357. if (cur.matches(search))
  358. return cur.bind(args);
  359. }
  360. return NULL;
  361. }
  362. ViewFieldTransformer * ViewTransformerRegistry::resolve(const char * name, const HqlExprArray & args)
  363. {
  364. return find(transformers, name, args);
  365. }
  366. ViewFieldTransformer * ViewTransformerRegistry::resolve(const char * servicename, const char * functionName, const HqlExprArray & args)
  367. {
  368. if (!servicename)
  369. return NULL;
  370. ForEachItemIn(i, plugins)
  371. {
  372. ViewServiceEntry & cur = plugins.item(i);
  373. if (stricmp(servicename, cur.name) == 0)
  374. return find(cur.transformers, functionName, args);
  375. }
  376. return NULL;
  377. }
  378. IViewTransformerRegistry & queryTransformerRegistry()
  379. {
  380. return *theTransformerRegistry;
  381. }
  382. //---------------------------------------------------------------------------
  383. bool containsFail(const ViewFieldTransformerArray & transforms)
  384. {
  385. ForEachItemIn(i, transforms)
  386. {
  387. if (transforms.item(i).matches(failId))
  388. return true;
  389. }
  390. return false;
  391. }
  392. void translateValue(MemoryAttr & result, const MemoryAttr & filterValue, const ViewFieldTransformerArray & transforms)
  393. {
  394. unsigned numTransforms = transforms.ordinality();
  395. if (numTransforms)
  396. {
  397. MemoryAttr tempValue[2];
  398. unsigned whichTarget = 0;
  399. const MemoryAttr * source = &filterValue;
  400. for (unsigned i=0; i < numTransforms-1; i++)
  401. {
  402. MemoryAttr * target = &tempValue[whichTarget];
  403. transforms.item(i).transform(*target, *source);
  404. source = target;
  405. whichTarget = 1-whichTarget;
  406. }
  407. transforms.item(numTransforms-1).transform(result, *source);
  408. }
  409. else
  410. result.set(filterValue.length(), filterValue.get());
  411. }
  412. ViewJoinColumn::ViewJoinColumn(unsigned _whichColumn, const ViewFieldTransformerArray & _getTransforms, const ViewFieldTransformerArray & _setTransforms)
  413. {
  414. whichColumn = _whichColumn;
  415. appendArray(getTransforms, _getTransforms);
  416. appendArray(setTransforms, _setTransforms);
  417. getContainsFail = containsFail(getTransforms);
  418. setContainsFail = containsFail(setTransforms);
  419. }
  420. void ViewJoinColumn::addFilter(IFilteredResultSet * resultSet, const MemoryAttr & value)
  421. {
  422. const MemoryAttr * source = &value;
  423. MemoryAttr tempValue;
  424. if (setTransforms.ordinality())
  425. {
  426. translateValue(tempValue, value, setTransforms);
  427. source = &tempValue;
  428. }
  429. resultSet->addFilter(whichColumn, (size32_t)source->length(), (const char *)source->get());
  430. }
  431. void ViewJoinColumn::clearFilter(IFilteredResultSet * resultSet)
  432. {
  433. resultSet->clearFilter(whichColumn);
  434. }
  435. void ViewJoinColumn::getValue(MemoryAttr & value, IResultSetCursor * cursor)
  436. {
  437. if (getTransforms.ordinality())
  438. {
  439. MemoryAttr rowValue;
  440. MemoryAttr2IStringVal adaptor(rowValue);
  441. cursor->getDisplayText(adaptor, whichColumn);
  442. translateValue(value, rowValue, getTransforms);
  443. }
  444. else
  445. {
  446. MemoryAttr2IStringVal adaptor(value);
  447. cursor->getDisplayText(adaptor, whichColumn);
  448. }
  449. }
  450. //---------------------------------------------------------------------------
  451. struct TextReference
  452. {
  453. TextReference() { len =0; text = NULL; }
  454. TextReference(size32_t _len, const char * _text) : len(_len), text(_text) {}
  455. inline bool eq(const char * search) const { return (len == strlen(search)) && (memicmp(text, search, len) == 0); }
  456. inline void get(StringAttr & target) const { target.set(text, len); }
  457. void set(size32_t _len, const char * _text) { len = _len; text = _text; }
  458. IHqlExpression * createIntConstant();
  459. IHqlExpression * createStringConstant();
  460. size32_t len;
  461. const char * text;
  462. };
  463. IHqlExpression * TextReference::createIntConstant()
  464. {
  465. return createConstant(rtlStrToInt8(len, text));
  466. }
  467. IHqlExpression * TextReference::createStringConstant()
  468. {
  469. return createConstant(createUnicodeValue(text+1, len-2, "", true, true));
  470. }
  471. class MappingParser
  472. {
  473. enum { TokEof=256, TokId, TokInt, TokString };
  474. public:
  475. MappingParser(const IResultSetMetaData & _fieldMeta, bool _datasetSelectorAllowed) : fieldMeta(_fieldMeta), datasetSelectorAllowed(_datasetSelectorAllowed)
  476. {
  477. tokenType = TokEof;
  478. lenInput = 0;
  479. input = NULL;
  480. offset = 0;
  481. }
  482. void parseColumnMappingList(FieldTransformInfoArray & results, unsigned len, const char * text);
  483. protected:
  484. unsigned lexToken();
  485. void assertToken(int expected);
  486. void getTokenText(StringAttr & target) { curToken.get(target); }
  487. void parseAttribute(FieldTransformInfo & output);
  488. void parseColumn(FieldTransformInfo & output);
  489. void parseColumnMapping(FieldTransformInfo & output);
  490. void parseConstantList(HqlExprArray & args);
  491. void parseTransformList(ViewFieldTransformerArray & transforms);
  492. protected:
  493. const IResultSetMetaData & fieldMeta;
  494. bool datasetSelectorAllowed;
  495. unsigned tokenType;
  496. TextReference curToken;
  497. unsigned offset;
  498. unsigned lenInput;
  499. const char * input;
  500. };
  501. inline bool isLeadingIdentChar(byte next)
  502. {
  503. return isalpha(next) || (next == '_') || (next == '$');
  504. }
  505. inline bool isTrailingIdentChar(byte next)
  506. {
  507. return isalnum(next) || (next == '_') || (next == '$');
  508. }
  509. //MORE: This should really use the hqllexer - especially if it gets any more complex!
  510. unsigned MappingParser::lexToken()
  511. {
  512. const byte * buffer = (const byte *)input;
  513. unsigned cur = offset;
  514. while ((cur < lenInput) && isspace(buffer[cur]))
  515. cur++;
  516. if (cur < lenInput)
  517. {
  518. byte next = buffer[cur];
  519. if (isLeadingIdentChar(next))
  520. {
  521. cur++;
  522. while ((cur < lenInput) && (isTrailingIdentChar(buffer[cur])))
  523. cur++;
  524. tokenType = TokId;
  525. }
  526. else if (isdigit(next) ||
  527. ((next == '-') && (cur+1 < lenInput) && isdigit(buffer[cur+1])))
  528. {
  529. cur++;
  530. while ((cur < lenInput) && (isdigit(buffer[cur])))
  531. cur++;
  532. tokenType = TokInt;
  533. }
  534. else if (next == '\'')
  535. {
  536. cur++;
  537. while (cur < lenInput)
  538. {
  539. byte next = buffer[cur];
  540. if (next == '\'')
  541. break;
  542. else if (next == '\\')
  543. {
  544. if (cur+1 < lenInput)
  545. cur++;
  546. }
  547. cur++;
  548. }
  549. if (cur == lenInput)
  550. throwError2(FVERR_BadStringTermination, cur-offset, input+offset);
  551. cur++;
  552. tokenType = TokString;
  553. }
  554. else
  555. {
  556. tokenType = next;
  557. cur++;
  558. }
  559. }
  560. else
  561. {
  562. tokenType = TokEof;
  563. }
  564. curToken.set(cur-offset, input+offset);
  565. offset = cur;
  566. return tokenType;
  567. }
  568. void MappingParser::assertToken(int expected)
  569. {
  570. if (tokenType != expected)
  571. {
  572. StringBuffer id;
  573. switch (expected)
  574. {
  575. case TokId:
  576. id.append("identifier");
  577. break;
  578. default:
  579. id.append((char)expected);
  580. break;
  581. }
  582. unsigned len = lenInput-offset;
  583. if (len>10) len = 10;
  584. throwError3(FVERR_ExpectedX, id.str(), len, input+offset);
  585. }
  586. }
  587. void MappingParser::parseColumn(FieldTransformInfo & output)
  588. {
  589. output.datasetColumn = NotFound;
  590. output.column = NotFound;
  591. unsigned firstFieldIndex = 0;
  592. const IResultSetMetaData * curMeta = &fieldMeta;
  593. for (;;)
  594. {
  595. StringAttr fieldName;
  596. assertToken(TokId);
  597. getTokenText(fieldName);
  598. lexToken();
  599. //Cheat and cast the meta so the field lookup can be done more efficiently
  600. const CResultSetMetaData & castFieldMeta = static_cast<const CResultSetMetaData &>(*curMeta);
  601. unsigned matchColumn = castFieldMeta.queryColumnIndex(firstFieldIndex, fieldName);
  602. if (matchColumn == NotFound)
  603. throwError1(FVERR_UnrecognisedFieldX, fieldName.get());
  604. DisplayType kind = fieldMeta.getColumnDisplayType(matchColumn);
  605. switch (kind)
  606. {
  607. case TypeBoolean:
  608. case TypeInteger:
  609. case TypeUnsignedInteger:
  610. case TypeReal:
  611. case TypeString:
  612. case TypeData:
  613. case TypeUnicode:
  614. case TypeUnknown:
  615. case TypeSet:
  616. output.column = matchColumn;
  617. break;
  618. case TypeBeginRecord:
  619. //Restrict the search fields to the contents of the record.
  620. firstFieldIndex = matchColumn+1;
  621. break;
  622. case TypeDataset:
  623. {
  624. if (!datasetSelectorAllowed)
  625. throwError1(FVERR_CannotSelectFromDatasetX, fieldName.get());
  626. if (output.datasetColumn != NotFound)
  627. throwError1(FVERR_CannotSelectManyFromDatasetX, fieldName.get());
  628. firstFieldIndex = 0;
  629. curMeta = curMeta->getChildMeta(matchColumn);
  630. output.datasetColumn = matchColumn;
  631. break;
  632. }
  633. default:
  634. throwUnexpected();
  635. }
  636. if (output.column != NotFound)
  637. return;
  638. assertToken('.');
  639. lexToken();
  640. }
  641. }
  642. void MappingParser::parseConstantList(HqlExprArray & args)
  643. {
  644. for (;;)
  645. {
  646. switch (tokenType)
  647. {
  648. case TokInt:
  649. args.append(*curToken.createIntConstant());
  650. break;
  651. case TokString:
  652. args.append(*curToken.createStringConstant());
  653. break;
  654. default:
  655. unsigned len = lenInput - offset > 10 ? 10 : lenInput - offset;
  656. throwError3(FVERR_ExpectedX, "int or string constant", len, input+offset);
  657. }
  658. lexToken();
  659. if (tokenType != ',')
  660. return;
  661. lexToken();
  662. }
  663. }
  664. void MappingParser::parseTransformList(ViewFieldTransformerArray & transforms)
  665. {
  666. for (;;)
  667. {
  668. assertToken(TokId);
  669. StringAttr mappingName, childName, grandName;
  670. curToken.get(mappingName);
  671. lexToken();
  672. if (tokenType == '.')
  673. {
  674. lexToken();
  675. assertToken(TokId);
  676. curToken.get(childName);
  677. lexToken();
  678. }
  679. if (tokenType == '.')
  680. {
  681. lexToken();
  682. assertToken(TokId);
  683. curToken.get(grandName);
  684. lexToken();
  685. }
  686. HqlExprArray args;
  687. if (tokenType == '(')
  688. {
  689. lexToken();
  690. if (tokenType != ')')
  691. parseConstantList(args);
  692. assertToken(')');
  693. lexToken();
  694. }
  695. ViewFieldTransformer * transform;
  696. try
  697. {
  698. if (childName)
  699. {
  700. transform = theTransformerRegistry->resolve(mappingName, childName, args);
  701. if (!transform)
  702. {
  703. //Maybe they specified the module name - should provide a 3 valued lookup
  704. transform = theTransformerRegistry->resolve(childName, grandName, args);
  705. }
  706. if (!transform)
  707. throwError2(FVERR_UnrecognisedMappingFunctionXY, mappingName.get(), childName.get());
  708. }
  709. else
  710. {
  711. transform = theTransformerRegistry->resolve(mappingName, args);
  712. if (!transform)
  713. throwError1(FVERR_UnrecognisedMappingFunctionX, mappingName.get());
  714. }
  715. }
  716. catch (IException * e)
  717. {
  718. EXCLOG(e, "Processing field mapping");
  719. transform = new ViewExceptionTransformer(e);
  720. e->Release();
  721. }
  722. transforms.append(*transform);
  723. if (tokenType != ',')
  724. break;
  725. lexToken();
  726. }
  727. }
  728. void MappingParser::parseAttribute(FieldTransformInfo & output)
  729. {
  730. assertToken(TokId);
  731. if (curToken.eq("get"))
  732. {
  733. lexToken();
  734. assertToken('(');
  735. lexToken();
  736. parseTransformList(output.getTransforms);
  737. assertToken(')');
  738. lexToken();
  739. }
  740. else if (curToken.eq("set"))
  741. {
  742. lexToken();
  743. assertToken('(');
  744. lexToken();
  745. parseTransformList(output.setTransforms);
  746. assertToken(')');
  747. lexToken();
  748. }
  749. else if (curToken.eq("displayname"))
  750. {
  751. lexToken();
  752. assertToken('(');
  753. lexToken();
  754. assertToken(TokId); // could allow a string I guess
  755. curToken.get(output.naturalName);
  756. lexToken();
  757. assertToken(')');
  758. lexToken();
  759. }
  760. else
  761. {
  762. unsigned len = lenInput-offset;
  763. if (len>10) len = 10;
  764. throwError3(FVERR_ExpectedX, "Definition name", len, input+offset);
  765. }
  766. }
  767. void MappingParser::parseColumnMapping(FieldTransformInfo & output)
  768. {
  769. parseColumn(output);
  770. int endToken = '}';
  771. //be flexible and allow () or {}?
  772. if (tokenType == '(')
  773. endToken = ')';
  774. else if (tokenType != '{')
  775. return;
  776. lexToken();
  777. if (tokenType != endToken)
  778. {
  779. for (;;)
  780. {
  781. parseAttribute(output);
  782. if (tokenType != ',')
  783. break;
  784. lexToken();
  785. }
  786. }
  787. assertToken(endToken);
  788. lexToken();
  789. }
  790. void MappingParser::parseColumnMappingList(FieldTransformInfoArray & results, unsigned len, const char * text)
  791. {
  792. lenInput = len;
  793. input = text;
  794. offset = 0;
  795. lexToken();
  796. if (tokenType == TokEof)
  797. return;
  798. for (;;)
  799. {
  800. FieldTransformInfo * next = new FieldTransformInfo;
  801. results.append(*next);
  802. parseColumnMapping(*next);
  803. if (tokenType == TokEof)
  804. break;
  805. assertToken(',');
  806. lexToken();
  807. }
  808. }
  809. void parseColumnMappingList(FieldTransformInfoArray & results,
  810. const IResultSetMetaData & fieldMeta,
  811. bool isDatasetAllowed, // if non null dataset.x is allowed, and column returned via pointer
  812. const char * text)
  813. {
  814. MappingParser parser(fieldMeta, isDatasetAllowed);
  815. parser.parseColumnMappingList(results, strlen(text), text);
  816. }
  817. void parseFileColumnMapping(FieldTransformInfoArray & results, const char * text, const IResultSetMetaData & fieldMeta)
  818. {
  819. MappingParser parser(fieldMeta, false);
  820. parser.parseColumnMappingList(results, strlen(text), text);
  821. }
  822. static void test()
  823. {
  824. HqlExprArray args;
  825. MemoryAttr source;
  826. source.set(26,"Gavin H\303\243lliday !!\316\261\316\221\307\272!!");
  827. {
  828. Owned<ViewFieldTransformer> transform = theTransformerRegistry->resolve("stringlib","StringToUpperCase",args);
  829. MemoryAttr target;
  830. transform->transform(target, source);
  831. }
  832. {
  833. Owned<ViewFieldTransformer> transform = theTransformerRegistry->resolve("unicodelib","UnicodeToUpperCase",args);
  834. MemoryAttr target;
  835. transform->transform(target, source);
  836. }
  837. source.get();
  838. }