couchbaseembed.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "couchbaseembed.hpp"
  14. #include "platform.h"
  15. #include "jexcept.hpp"
  16. #include "jlog.hpp"
  17. #include "hqlplugins.hpp"
  18. #include "deftype.hpp"
  19. #include "eclhelper.hpp"
  20. #include "eclrtl.hpp"
  21. #include "eclrtl_imp.hpp"
  22. #include <map>
  23. #include <mutex>
  24. #include <thread>
  25. static const char *g_moduleName = "couchbase";
  26. static const char *g_moduleDescription = "Couchbase Embed Helper";
  27. static const char *g_version = "Couchbase Embed Helper 1.0.0";
  28. static const char *g_compatibleVersions[] = { g_version, nullptr };
  29. static const NullFieldProcessor NULLFIELD(NULL);
  30. extern "C" COUCHBASEEMBED_PLUGIN_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  31. {
  32. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  33. {
  34. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  35. pbx->compatibleVersions = g_compatibleVersions;
  36. }
  37. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  38. return false;
  39. pb->magicVersion = PLUGIN_VERSION;
  40. pb->version = g_version;
  41. pb->moduleName = g_moduleName;
  42. pb->ECL = nullptr;
  43. pb->flags = PLUGIN_IMPLICIT_MODULE;
  44. pb->description = g_moduleDescription;
  45. return true;
  46. }
  47. namespace couchbaseembed
  48. {
  49. const time_t OBJECT_EXPIRE_TIMEOUT_SECONDS = 60 * 2; // Two minutes
  50. static std::once_flag connectionCacheInitFlag;
  51. //--------------------------------------------------------------------------
  52. // Plugin Classes
  53. //--------------------------------------------------------------------------
  54. void reportIfQueryFailure(Couchbase::Query * query)
  55. {
  56. auto status = query->meta().status();
  57. if (status.errcode())
  58. {
  59. if (status.isNetworkError())
  60. failx("NetworkErr: %s", status.description());
  61. else if (status.isDataError())
  62. failx("DataErr: %s", status.description());
  63. else if (status.isInputError())
  64. failx("InputErr: %s", status.description());
  65. else if (status.isTemporary())
  66. failx("TempErr: %s", status.description());
  67. else
  68. failx("Couchbase err: %s (%d)", status.description(), status.errcode());
  69. }
  70. //consider parsing json result
  71. if (strstr(query->meta().body().to_string().c_str(), "\"status\": \"errors\""))
  72. failx("Err: %s", query->meta().body().to_string().c_str());
  73. }
  74. CouchbaseRowStream::CouchbaseRowStream(IEngineRowAllocator* resultAllocator, Couchbase::Query * cbaseQuery)
  75. : m_resultAllocator(resultAllocator)
  76. {
  77. m_currentRow = 0;
  78. m_shouldRead = true;
  79. //iterating over result rows and copying them to stringarray
  80. //is there a way to independently step through original result rows?
  81. for (auto cbrow : *cbaseQuery)
  82. m_Rows.append(cbrow.json().to_string().c_str());
  83. reportIfQueryFailure(cbaseQuery);
  84. }
  85. CouchbaseRowStream::~CouchbaseRowStream() {}
  86. const void * CouchbaseRowStream::nextRow()
  87. {
  88. const void * result = nullptr;
  89. if (m_shouldRead && m_currentRow < m_Rows.length())
  90. {
  91. auto json = m_Rows.item(m_currentRow++);
  92. Owned<IPropertyTree> contentTree = createPTreeFromJSONString(json,ipt_caseInsensitive);
  93. if (contentTree)
  94. {
  95. CouchbaseRowBuilder cbRowBuilder(contentTree);
  96. RtlDynamicRowBuilder rowBuilder(m_resultAllocator);
  97. const RtlTypeInfo *typeInfo = m_resultAllocator->queryOutputMeta()->queryTypeInfo();
  98. assertex(typeInfo);
  99. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  100. size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, cbRowBuilder);
  101. return rowBuilder.finalizeRowClear(len);
  102. }
  103. else
  104. failx("Error processing result row");
  105. }
  106. return result;
  107. }
  108. void CouchbaseRowStream::stop()
  109. {
  110. m_resultAllocator.clear();
  111. m_shouldRead = false;
  112. }
  113. Couchbase::Query * CouchbaseConnection::query(Couchbase::QueryCommand * qcommand)
  114. {
  115. Couchbase::Status queryStatus;
  116. Couchbase::Query * pQuery = new Couchbase::Query(*m_pCouchbaseClient, *qcommand, queryStatus); // will be owned by method caller
  117. if (!queryStatus)
  118. failx("Couldn't issue query: %s", queryStatus.description());
  119. if (!pQuery->status())
  120. failx("Couldn't execute query, reason: %s\nBody is: ", pQuery->meta().body().data());
  121. if (pQuery->meta().status().errcode() != LCB_SUCCESS )//rows.length() == 0)
  122. failx("Query execution error: %s", pQuery->meta().body().data());
  123. return pQuery;
  124. }
  125. extern void UNSUPPORTED(const char *feature)
  126. {
  127. throw MakeStringException(-1, "UNSUPPORTED feature: %s not supported in %s", feature, g_version);
  128. }
  129. extern void failx(const char *message, ...)
  130. {
  131. va_list args;
  132. va_start(args,message);
  133. StringBuffer msg;
  134. msg.appendf("%s: ", g_moduleName).valist_appendf(message,args);
  135. va_end(args);
  136. rtlFail(0, msg.str());
  137. }
  138. extern void fail(const char *message)
  139. {
  140. StringBuffer msg;
  141. msg.appendf("%s: ", g_moduleName).append(message);
  142. rtlFail(0, msg.str());
  143. }
  144. void bindStringParam(unsigned len, const char *value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  145. {
  146. VStringBuffer cbPlaceholder("$%s", field->name);
  147. if (pQcmd)
  148. {
  149. size32_t utf8chars;
  150. char *utf8;
  151. rtlStrToUtf8X(utf8chars, utf8, len, value);
  152. auto status = pQcmd->named_param(cbPlaceholder.str(), utf8);
  153. if (!status.success())
  154. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), utf8);
  155. }
  156. else
  157. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  158. }
  159. void bindBoolParam(bool value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  160. {
  161. VStringBuffer cbPlaceholder("$%s", field->name);
  162. if (pQcmd)
  163. {
  164. StringBuffer serialized;
  165. TokenSerializer tokenSerializer;
  166. tokenSerializer.serialize(value, serialized);
  167. auto status = pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  168. if (!status.success())
  169. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  170. }
  171. else
  172. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  173. }
  174. void bindDataParam(unsigned len, const void *value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  175. {
  176. VStringBuffer cbPlaceholder("$%s", field->name);
  177. if (pQcmd)
  178. {
  179. size32_t bytes;
  180. void *data;
  181. rtlStrToDataX(bytes, data, len, value);
  182. auto status = pQcmd->named_param(cbPlaceholder.str(), (char *)data);
  183. if (!status.success())
  184. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), (char *)data);
  185. }
  186. else
  187. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  188. }
  189. void bindIntParam(__int64 value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  190. {
  191. VStringBuffer cbPlaceholder("$%s", field->name);
  192. if (pQcmd)
  193. {
  194. StringBuffer serialized;
  195. TokenSerializer tokenSerializer;
  196. tokenSerializer.serialize(value, serialized);
  197. auto status = pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  198. if (!status.success())
  199. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  200. }
  201. else
  202. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  203. }
  204. void bindUIntParam(unsigned __int64 value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  205. {
  206. VStringBuffer cbPlaceholder("$%s", field->name);
  207. if (pQcmd)
  208. {
  209. StringBuffer serialized;
  210. TokenSerializer tokenSerializer;
  211. tokenSerializer.serialize(value, serialized);
  212. auto status = pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  213. if (!status.success())
  214. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  215. }
  216. else
  217. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  218. }
  219. void bindRealParam(double value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  220. {
  221. VStringBuffer cbPlaceholder("$%s", field->name);
  222. if (pQcmd)
  223. {
  224. StringBuffer serialized;
  225. TokenSerializer tokenSerializer;
  226. tokenSerializer.serialize(value, serialized);
  227. auto status = pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  228. if (!status.success())
  229. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  230. }
  231. else
  232. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  233. }
  234. void bindUnicodeParam(unsigned chars, const UChar *value, const RtlFieldInfo * field, Couchbase::QueryCommand * pQcmd)
  235. {
  236. VStringBuffer cbPlaceholder("$%s", field->name);
  237. if (pQcmd)
  238. {
  239. size32_t utf8chars;
  240. char *utf8;
  241. rtlUnicodeToUtf8X(utf8chars, utf8, chars, value);
  242. auto status = pQcmd->named_param(cbPlaceholder.str(), utf8);
  243. if (!status.success())
  244. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), utf8);
  245. }
  246. else
  247. failx("Internal error: detected invalid CouchbaseQueryCommand while attempting to bind to field: %s", cbPlaceholder.str());
  248. }
  249. int CouchbaseRecordBinder::numFields()
  250. {
  251. int count = 0;
  252. const RtlFieldInfo * const *fields = typeInfo->queryFields();
  253. assertex(fields);
  254. while (*fields++)
  255. count++;
  256. return count;
  257. }
  258. void CouchbaseRecordBinder::processRow(const byte *row)
  259. {
  260. thisParam = firstParam;
  261. typeInfo->process(row, row, &dummyField, *this); // Bind the variables for the current row
  262. }
  263. void CouchbaseRecordBinder::processString(unsigned len, const char *value, const RtlFieldInfo * field)
  264. {
  265. checkNextParam(field);
  266. bindStringParam(len, value, field, m_pQcmd);
  267. }
  268. void CouchbaseRecordBinder::processBool(bool value, const RtlFieldInfo * field)
  269. {
  270. bindBoolParam(value, field, m_pQcmd);
  271. }
  272. void CouchbaseRecordBinder::processData(unsigned len, const void *value, const RtlFieldInfo * field)
  273. {
  274. bindDataParam(len, value, field, m_pQcmd);
  275. }
  276. void CouchbaseRecordBinder::processInt(__int64 value, const RtlFieldInfo * field)
  277. {
  278. bindIntParam(value, field, m_pQcmd);
  279. }
  280. void CouchbaseRecordBinder::processUInt(unsigned __int64 value, const RtlFieldInfo * field)
  281. {
  282. bindUIntParam(value, field,m_pQcmd);
  283. }
  284. void CouchbaseRecordBinder::processReal(double value, const RtlFieldInfo * field)
  285. {
  286. bindRealParam(value, field, m_pQcmd);
  287. }
  288. void CouchbaseRecordBinder::processDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  289. {
  290. Decimal val;
  291. size32_t bytes;
  292. rtlDataAttr decText;
  293. val.setDecimal(digits, precision, value);
  294. val.getStringX(bytes, decText.refstr());
  295. processUtf8(bytes, decText.getstr(), field);
  296. }
  297. void CouchbaseRecordBinder::processUnicode(unsigned chars, const UChar *value, const RtlFieldInfo * field)
  298. {
  299. bindUnicodeParam(chars, value, field, m_pQcmd);
  300. }
  301. void CouchbaseRecordBinder::processQString(unsigned len, const char *value, const RtlFieldInfo * field)
  302. {
  303. size32_t charCount;
  304. rtlDataAttr text;
  305. rtlQStrToStrX(charCount, text.refstr(), len, value);
  306. processUtf8(charCount, text.getstr(), field);
  307. }
  308. void CouchbaseRecordBinder::processUtf8(unsigned chars, const char *value, const RtlFieldInfo * field)
  309. {
  310. bindStringParam(strlen(value), value, field, m_pQcmd);
  311. }
  312. unsigned CouchbaseRecordBinder::checkNextParam(const RtlFieldInfo * field)
  313. {
  314. if (logctx.queryTraceLevel() > 4)
  315. logctx.CTXLOG("Binding %s to %d", field->name, thisParam);
  316. return thisParam++;
  317. }
  318. static class ConnectionCacheObj
  319. {
  320. private:
  321. typedef std::vector<CouchbaseConnection*> ConnectionList;
  322. typedef std::map<hash64_t, ConnectionList> ObjMap;
  323. public:
  324. ConnectionCacheObj(int _traceLevel)
  325. : traceLevel(_traceLevel)
  326. {
  327. }
  328. ~ConnectionCacheObj()
  329. {
  330. deleteAll();
  331. }
  332. void deleteAll()
  333. {
  334. CriticalBlock block(cacheLock);
  335. // Delete all idle connection objects
  336. for (ObjMap::iterator keyIter = idleConnections.begin(); keyIter != idleConnections.end(); keyIter++)
  337. {
  338. for (ConnectionList::iterator connectionIter = keyIter->second.begin(); connectionIter != keyIter->second.end(); connectionIter++)
  339. {
  340. if (*connectionIter)
  341. {
  342. delete(*connectionIter);
  343. }
  344. }
  345. }
  346. idleConnections.clear();
  347. // Delete all active connection objects
  348. for (ObjMap::iterator keyIter = activeConnections.begin(); keyIter != activeConnections.end(); keyIter++)
  349. {
  350. for (ConnectionList::iterator connectionIter = keyIter->second.begin(); connectionIter != keyIter->second.end(); connectionIter++)
  351. {
  352. if (*connectionIter)
  353. {
  354. delete(*connectionIter);
  355. }
  356. }
  357. }
  358. activeConnections.clear();
  359. }
  360. void releaseActive(CouchbaseConnection* connectionPtr)
  361. {
  362. CriticalBlock block(cacheLock);
  363. // Find given connection in our active list and move it to our
  364. // idle list
  365. for (ObjMap::iterator keyIter = activeConnections.begin(); keyIter != activeConnections.end(); keyIter++)
  366. {
  367. for (ConnectionList::iterator connectionIter = keyIter->second.begin(); connectionIter != keyIter->second.end(); connectionIter++)
  368. {
  369. if (*connectionIter == connectionPtr)
  370. {
  371. connectionPtr->updateTimeTouched();
  372. keyIter->second.erase(connectionIter);
  373. idleConnections[keyIter->first].push_back(connectionPtr);
  374. if (traceLevel > 4)
  375. {
  376. DBGLOG("Couchbase: Released connection object %p", connectionPtr);
  377. }
  378. return;
  379. }
  380. }
  381. }
  382. }
  383. void expire()
  384. {
  385. if (!idleConnections.empty())
  386. {
  387. CriticalBlock block(cacheLock);
  388. time_t oldestAllowedTime = time(NULL) - OBJECT_EXPIRE_TIMEOUT_SECONDS;
  389. __int32 expireCount = 0;
  390. for (ObjMap::iterator keyIter = idleConnections.begin(); keyIter != idleConnections.end(); keyIter++)
  391. {
  392. ConnectionList::iterator connectionIter = keyIter->second.begin();
  393. while (connectionIter != keyIter->second.end())
  394. {
  395. if (*connectionIter)
  396. {
  397. if ((*connectionIter)->getTimeTouched() < oldestAllowedTime)
  398. {
  399. delete(*connectionIter);
  400. connectionIter = keyIter->second.erase(connectionIter);
  401. ++expireCount;
  402. }
  403. else
  404. {
  405. ++connectionIter;
  406. }
  407. }
  408. else
  409. {
  410. connectionIter = keyIter->second.erase(connectionIter);
  411. }
  412. }
  413. }
  414. if (traceLevel > 4 && expireCount > 0)
  415. {
  416. DBGLOG("Couchbase: Expired %d cached connection%s", expireCount, (expireCount == 1 ? "" : "s"));
  417. }
  418. }
  419. }
  420. CouchbaseConnection* getConnection(bool useSSL, const char * host, unsigned port, const char * bucketname, const char * password, const char * connOptions, unsigned int maxConnections)
  421. {
  422. CouchbaseConnection* connectionObjPtr = nullptr;
  423. StringBuffer connectionString;
  424. CouchbaseConnection::makeConnectionString(useSSL, host, port, bucketname, connOptions, connectionString);
  425. // Use a hash of the connection string as the key to finding
  426. // any idle connection objects
  427. hash64_t key = rtlHash64VStr(connectionString.str(), 0);
  428. while (true)
  429. {
  430. {
  431. CriticalBlock block(cacheLock);
  432. ConnectionList& idleConnectionList = idleConnections[key];
  433. if (!idleConnectionList.empty())
  434. {
  435. // We have at least one idle connection; use that
  436. connectionObjPtr = idleConnectionList.back();
  437. idleConnectionList.pop_back();
  438. connectionObjPtr->updateTimeTouched();
  439. // Push the connection object onto our active list
  440. activeConnections[key].push_back(connectionObjPtr);
  441. if (traceLevel > 4)
  442. {
  443. DBGLOG("Couchbase: Using cached connection object %p: %s", connectionObjPtr, connectionString.str());
  444. }
  445. break;
  446. }
  447. else if (maxConnections == 0 || activeConnections[key].size() < maxConnections)
  448. {
  449. // No idle connections but we don't have to wait for
  450. // one; exit the loop and create a new connection
  451. break;
  452. }
  453. }
  454. // We can't exit the loop and allow a new connection to
  455. // be created because there are too many active
  456. // connections already; wait for a short while
  457. // and try again
  458. std::this_thread::sleep_for(std::chrono::microseconds(10));
  459. }
  460. if (!connectionObjPtr)
  461. {
  462. // An idle connection for that particular combination of
  463. // options does not exist so we need to create one;
  464. // use a small loop to retry connections if necessary
  465. unsigned int connectAttempt = 0;
  466. unsigned int MAX_ATTEMPTS = 10;
  467. useconds_t SLEEP_TIME = 100 + (rand() % 200);
  468. while (true)
  469. {
  470. connectionObjPtr = new CouchbaseConnection(connectionString, password);
  471. connectionObjPtr->connect();
  472. if (connectionObjPtr->getConnectionStatus().success())
  473. {
  474. {
  475. // Push new connection object onto our active list
  476. CriticalBlock block(cacheLock);
  477. connectionObjPtr->updateTimeTouched();
  478. ConnectionList& activeConnectionList = activeConnections[key];
  479. activeConnectionList.push_back(connectionObjPtr);
  480. }
  481. if (traceLevel > 4)
  482. {
  483. DBGLOG("Couchbase: Created and cached new connection object %p: %s", connectionObjPtr, connectionString.str());
  484. }
  485. break;
  486. }
  487. else if (connectionObjPtr->getConnectionStatus().isTemporary())
  488. {
  489. ++connectAttempt;
  490. if (connectAttempt < MAX_ATTEMPTS)
  491. {
  492. // According to libcouchbase-cxx, we need
  493. // to destroy the connection object if
  494. // there has been a failure of any kind
  495. delete(connectionObjPtr);
  496. connectionObjPtr = nullptr;
  497. std::this_thread::sleep_for(std::chrono::microseconds(SLEEP_TIME));
  498. }
  499. else
  500. {
  501. // Capture the final failure reason and
  502. // destroy the connection object before
  503. // throwing an error
  504. std::string reason = connectionObjPtr->getConnectionStatus().description();
  505. delete(connectionObjPtr);
  506. connectionObjPtr = nullptr;
  507. failx("Failed to connect to couchbase instance: %s Reason: '%s'", connectionString.str(), reason.c_str());
  508. }
  509. }
  510. else
  511. {
  512. // Capture the final failure reason and
  513. // destroy the connection object before
  514. // throwing an error
  515. std::string reason = connectionObjPtr->getConnectionStatus().description();
  516. delete(connectionObjPtr);
  517. connectionObjPtr = nullptr;
  518. failx("Failed to connect to couchbase instance: %s Reason: '%s'", connectionString.str(), reason.c_str());
  519. }
  520. }
  521. }
  522. if (!connectionObjPtr)
  523. {
  524. failx("Couchbase: Unable to create connection: %s", connectionString.str());
  525. }
  526. return connectionObjPtr;
  527. }
  528. private:
  529. ObjMap idleConnections; //!< std::map of created CouchbaseConnection object pointers
  530. ObjMap activeConnections; //!< std::map of created CouchbaseConnection object pointers
  531. CriticalSection cacheLock; //!< Mutex guarding modifications to connection pools
  532. int traceLevel; //!< The current logging level
  533. } *connectionCache;
  534. static class ConnectionCacheExpirerObj : public Thread
  535. {
  536. public:
  537. ConnectionCacheExpirerObj()
  538. : Thread("Couchbase::ConnectionCacheExpirer"),
  539. shouldRun(false)
  540. {
  541. }
  542. virtual void start()
  543. {
  544. if (!isAlive())
  545. {
  546. shouldRun = true;
  547. Thread::start();
  548. }
  549. }
  550. virtual void stop()
  551. {
  552. if (isAlive())
  553. {
  554. shouldRun = false;
  555. join();
  556. }
  557. }
  558. virtual int run()
  559. {
  560. // Periodically delete connections that have been idle too long
  561. while (shouldRun)
  562. {
  563. if (connectionCache)
  564. {
  565. connectionCache->expire();
  566. }
  567. std::this_thread::sleep_for(std::chrono::microseconds(1000));
  568. }
  569. return 0;
  570. }
  571. private:
  572. std::atomic_bool shouldRun; //!< If true, we should execute our thread's main event loop
  573. } *connectionCacheExpirer;
  574. static void setupConnectionCache(int traceLevel)
  575. {
  576. couchbaseembed::connectionCache = new couchbaseembed::ConnectionCacheObj(traceLevel);
  577. couchbaseembed::connectionCacheExpirer = new couchbaseembed::ConnectionCacheExpirerObj;
  578. couchbaseembed::connectionCacheExpirer->start();
  579. }
  580. CouchbaseEmbedFunctionContext::CouchbaseEmbedFunctionContext(const IContextLogger &_logctx, const char *options, unsigned _flags)
  581. : logctx(_logctx), m_NextRow(), m_nextParam(0), m_numParams(0), m_scriptFlags(_flags)
  582. {
  583. m_pQuery = nullptr;
  584. m_pQcmd = nullptr;
  585. const char *server = "localhost";
  586. const char *user = "";
  587. const char *password = "";
  588. const char *bucketname = "default";
  589. unsigned port = 8091;
  590. bool useSSL = false;
  591. StringBuffer connectionOptions;
  592. unsigned int maxConnections = 0;
  593. StringArray inputOptions;
  594. inputOptions.appendList(options, ",");
  595. ForEachItemIn(idx, inputOptions)
  596. {
  597. const char *opt = inputOptions.item(idx);
  598. const char *val = strchr(opt, '=');
  599. if (val)
  600. {
  601. StringBuffer optName(val-opt, opt);
  602. val++;
  603. if (stricmp(optName, "server")==0)
  604. server = val; // Note that lifetime of val is adequate for this to be safe
  605. else if (stricmp(optName, "port")==0)
  606. port = atoi(val);
  607. else if (stricmp(optName, "user")==0)
  608. user = val; // This is not used but retained for backwards-compatibility
  609. else if (stricmp(optName, "password")==0)
  610. password = val;
  611. else if (stricmp(optName, "bucket")==0)
  612. bucketname = val;
  613. else if (stricmp(optName, "useSSL")==0)
  614. useSSL = clipStrToBool(val);
  615. else if (stricmp(optName, "max_connections")==0)
  616. maxConnections = atoi(val);
  617. //Connection String options
  618. else if (stricmp(optName, "detailed_errcodes")==0
  619. || stricmp(optName, "operation_timeout")==0
  620. || stricmp(optName, "config_total_timeout")==0
  621. || stricmp(optName, "http_poolsize")==0)
  622. connectionOptions.appendf("%s%s=%s", connectionOptions.length() == 0 ? "?" : "&", optName.str(), val);
  623. else
  624. failx("Unknown option %s", optName.str());
  625. }
  626. }
  627. std::call_once(connectionCacheInitFlag, setupConnectionCache, logctx.queryTraceLevel());
  628. // Get a cached idle connection or create a new one
  629. m_oCBConnection = connectionCache->getConnection(useSSL, server, port, bucketname, password, connectionOptions.str(), maxConnections);
  630. }
  631. CouchbaseEmbedFunctionContext::~CouchbaseEmbedFunctionContext()
  632. {
  633. if (m_pQcmd)
  634. {
  635. delete m_pQcmd;
  636. m_pQcmd = nullptr;
  637. }
  638. if (m_pQuery)
  639. {
  640. delete m_pQuery;
  641. m_pQuery = nullptr;
  642. }
  643. if (m_oCBConnection)
  644. {
  645. // When the context is deleted we should return any connection
  646. // object back to idle status
  647. connectionCache->releaseActive(m_oCBConnection);
  648. m_oCBConnection = nullptr;
  649. }
  650. }
  651. IPropertyTree * CouchbaseEmbedFunctionContext::nextResultRowTree()
  652. {
  653. for (auto cbrow : *m_pQuery)
  654. {
  655. auto json = cbrow.json().to_string();
  656. Owned<IPropertyTree> contentTree = createPTreeFromJSONString(json.c_str());
  657. return contentTree.getLink();
  658. }
  659. reportIfQueryFailure(m_pQuery);
  660. return nullptr;
  661. }
  662. IPropertyTreeIterator * CouchbaseEmbedFunctionContext::nextResultRowIterator()
  663. {
  664. for (auto cbrow : *m_pQuery)
  665. {
  666. auto json = cbrow.json().to_string();
  667. Owned<IPropertyTree> contentTree = createPTreeFromJSONString(json.c_str());
  668. if (contentTree)
  669. return contentTree->getElements("./*");
  670. failx("Could not fetch next result row.");
  671. break;
  672. }
  673. reportIfQueryFailure(m_pQuery);
  674. return nullptr;
  675. }
  676. const char * CouchbaseEmbedFunctionContext::nextResultScalar()
  677. {
  678. m_resultrow.setown(nextResultRowIterator());
  679. if (m_resultrow)
  680. {
  681. m_resultrow->first();
  682. if(m_resultrow->isValid() == true)
  683. {
  684. if (m_resultrow->query().hasChildren())
  685. typeError("scalar", "");
  686. return m_resultrow->query().queryProp("");
  687. }
  688. else
  689. failx("Could not fetch next result column.");
  690. }
  691. else
  692. failx("Could not fetch next result row.");
  693. return nullptr;
  694. }
  695. bool CouchbaseEmbedFunctionContext::getBooleanResult()
  696. {
  697. bool mybool;
  698. auto scalar = nextResultScalar();
  699. handleDeserializeOutcome(m_tokenDeserializer.deserialize(scalar, mybool), "bool", scalar);
  700. return mybool;
  701. }
  702. void CouchbaseEmbedFunctionContext::getDataResult(size32_t &len, void * &result)
  703. {
  704. auto value = nextResultScalar();
  705. if (value && *value)
  706. {
  707. rtlStrToDataX(len, result, strlen(value), value); // This feels like it may not work to me - will preallocate rather larger than we want
  708. }
  709. else
  710. {
  711. rtlStrToDataX(len, result, NULLFIELD.resultChars, NULLFIELD.stringResult);
  712. }
  713. }
  714. double CouchbaseEmbedFunctionContext::getRealResult()
  715. {
  716. double mydouble = 0.0;
  717. auto value = nextResultScalar();
  718. handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, mydouble), "real", value);
  719. return mydouble;
  720. }
  721. __int64 CouchbaseEmbedFunctionContext::getSignedResult()
  722. {
  723. __int64 myint64 = 0;
  724. auto value = nextResultScalar();
  725. handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, myint64), "signed", value);
  726. return myint64;
  727. }
  728. unsigned __int64 CouchbaseEmbedFunctionContext::getUnsignedResult()
  729. {
  730. unsigned __int64 myuint64 = 0;
  731. auto value = nextResultScalar();
  732. handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, myuint64), "unsigned", value);
  733. return myuint64;
  734. }
  735. void CouchbaseEmbedFunctionContext::getStringResult(size32_t &chars, char * &result)
  736. {
  737. auto value = nextResultScalar();
  738. if (value && *value)
  739. {
  740. unsigned numchars = rtlUtf8Length(strlen(value), value);
  741. rtlUtf8ToStrX(chars, result, numchars, value);
  742. }
  743. else
  744. {
  745. rtlStrToStrX(chars, result, NULLFIELD.resultChars, NULLFIELD.stringResult);
  746. }
  747. }
  748. void CouchbaseEmbedFunctionContext::getUTF8Result(size32_t &chars, char * &result)
  749. {
  750. getStringResult(chars, result);
  751. }
  752. void CouchbaseEmbedFunctionContext::getUnicodeResult(size32_t &chars, UChar * &result)
  753. {
  754. auto value = nextResultScalar();
  755. if (value && *value)
  756. {
  757. unsigned numchars = rtlUtf8Length(strlen(value), value);
  758. rtlUtf8ToUnicodeX(chars, result, numchars, value);
  759. }
  760. else
  761. {
  762. rtlUnicodeToUnicodeX(chars, result, NULLFIELD.resultChars, NULLFIELD.unicodeResult);
  763. }
  764. }
  765. void CouchbaseEmbedFunctionContext::getDecimalResult(Decimal &value)
  766. {
  767. auto text = nextResultScalar();
  768. if (text && *text)
  769. value.setString(rtlUtf8Length(strlen(text), text), text);
  770. else
  771. value.set(NULLFIELD.decimalResult);
  772. }
  773. IRowStream * CouchbaseEmbedFunctionContext::getDatasetResult(IEngineRowAllocator * _resultAllocator)
  774. {
  775. Owned<CouchbaseRowStream> cbaseRowStream;
  776. cbaseRowStream.setown(new CouchbaseRowStream(_resultAllocator, m_pQuery));
  777. return cbaseRowStream.getLink();
  778. }
  779. byte * CouchbaseEmbedFunctionContext::getRowResult(IEngineRowAllocator * _resultAllocator)
  780. {
  781. Owned<CouchbaseRowStream> cbaseRowStream;
  782. cbaseRowStream.setown(new CouchbaseRowStream(_resultAllocator, m_pQuery));
  783. return (byte *)cbaseRowStream->nextRow();
  784. }
  785. size32_t CouchbaseEmbedFunctionContext::getTransformResult(ARowBuilder & rowBuilder)
  786. {
  787. execute();
  788. auto resultrow = nextResultRowTree();
  789. if (!resultrow)
  790. fail("Failed to read row");
  791. if (resultrow->getCount("./*") != 1)
  792. typeError("row", "");
  793. CouchbaseRowBuilder couchbaseRowBuilder(resultrow);
  794. const RtlTypeInfo *typeInfo = rowBuilder.queryAllocator()->queryOutputMeta()->queryTypeInfo();
  795. assertex(typeInfo);
  796. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  797. return typeInfo->build(rowBuilder, 0, &dummyField, couchbaseRowBuilder);
  798. }
  799. void CouchbaseEmbedFunctionContext::bindRowParam(const char *name, IOutputMetaData & metaVal, byte *val)
  800. {
  801. CouchbaseRecordBinder binder(logctx, metaVal.queryTypeInfo(), m_pQcmd, m_nextParam);
  802. binder.processRow(val);
  803. m_nextParam += binder.numFields();
  804. }
  805. void CouchbaseEmbedFunctionContext::bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
  806. {
  807. // We only support a single dataset parameter...
  808. // MORE - look into batch?
  809. if (m_oInputStream)
  810. {
  811. fail("At most one dataset parameter supported");
  812. }
  813. m_oInputStream.setown(new CouchbaseDatasetBinder(logctx, LINK(val), metaVal.queryTypeInfo(), m_pQcmd, m_nextParam));
  814. m_nextParam += m_oInputStream->numFields();
  815. }
  816. void CouchbaseEmbedFunctionContext::bindBooleanParam(const char *name, bool val)
  817. {
  818. checkNextParam(name);
  819. StringBuffer serialized;
  820. m_tokenSerializer.serialize(val, serialized);
  821. VStringBuffer cbPlaceholder("$%s", name);
  822. auto status = m_pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  823. if (!status.success())
  824. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  825. }
  826. void CouchbaseEmbedFunctionContext::bindDataParam(const char *name, size32_t len, const void *val)
  827. {
  828. checkNextParam(name);
  829. VStringBuffer cbPlaceholder("$%s", name);
  830. size32_t bytes;
  831. void *data;
  832. rtlStrToDataX(bytes, data, len, val);
  833. auto status = m_pQcmd->named_param(cbPlaceholder.str(), (char *)data);
  834. if (!status.success())
  835. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), (char *)data);
  836. }
  837. void CouchbaseEmbedFunctionContext::bindFloatParam(const char *name, float val)
  838. {
  839. checkNextParam(name);
  840. StringBuffer serialized;
  841. m_tokenSerializer.serialize(val, serialized);
  842. VStringBuffer cbPlaceholder("$%s", name);
  843. auto status = m_pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  844. if (!status.success())
  845. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  846. }
  847. void CouchbaseEmbedFunctionContext::bindRealParam(const char *name, double val)
  848. {
  849. checkNextParam(name);
  850. StringBuffer serialized;
  851. m_tokenSerializer.serialize(val, serialized);
  852. VStringBuffer cbPlaceholder("$%s", name);
  853. auto status = m_pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  854. if (!status.success())
  855. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  856. }
  857. void CouchbaseEmbedFunctionContext::bindSignedSizeParam(const char *name, int size, __int64 val)
  858. {
  859. bindSignedParam(name, val);
  860. }
  861. void CouchbaseEmbedFunctionContext::bindSignedParam(const char *name, __int64 val)
  862. {
  863. checkNextParam(name);
  864. StringBuffer serialized;
  865. m_tokenSerializer.serialize(val, serialized);
  866. VStringBuffer cbPlaceholder("$%s", name);
  867. auto status = m_pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  868. if (!status.success())
  869. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  870. }
  871. void CouchbaseEmbedFunctionContext::bindUnsignedSizeParam(const char *name, int size, unsigned __int64 val)
  872. {
  873. bindUnsignedParam(name, val);
  874. }
  875. void CouchbaseEmbedFunctionContext::bindUnsignedParam(const char *name, unsigned __int64 val)
  876. {
  877. checkNextParam(name);
  878. StringBuffer serialized;
  879. m_tokenSerializer.serialize(val, serialized);
  880. VStringBuffer cbPlaceholder("$%s", name);
  881. auto status = m_pQcmd->named_param(cbPlaceholder.str(), serialized.str());
  882. if (!status.success())
  883. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), serialized.str());
  884. }
  885. void CouchbaseEmbedFunctionContext::bindStringParam(const char *name, size32_t len, const char *val)
  886. {
  887. checkNextParam(name);
  888. VStringBuffer cbPlaceholder("$%s", name);
  889. size32_t utf8chars;
  890. char *utf8;
  891. rtlStrToUtf8X(utf8chars, utf8, len, val);
  892. auto status = m_pQcmd->named_param(cbPlaceholder.str(), utf8);
  893. if (!status.success())
  894. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), utf8);
  895. }
  896. void CouchbaseEmbedFunctionContext::bindVStringParam(const char *name, const char *val)
  897. {
  898. checkNextParam(name);
  899. bindStringParam(name, strlen(val), val);
  900. }
  901. void CouchbaseEmbedFunctionContext::bindUTF8Param(const char *name, size32_t chars, const char *val)
  902. {
  903. checkNextParam(name);
  904. bindStringParam(name, strlen(val), val);
  905. }
  906. void CouchbaseEmbedFunctionContext::bindUnicodeParam(const char *name, size32_t chars, const UChar *val)
  907. {
  908. checkNextParam(name);
  909. VStringBuffer cbPlaceholder("$%s", name);
  910. size32_t utf8chars;
  911. char *utf8;
  912. rtlUnicodeToUtf8X(utf8chars, utf8, chars, val);
  913. auto status = m_pQcmd->named_param(cbPlaceholder.str(), utf8);
  914. if (!status.success())
  915. failx("Could not bind Param: %s val: %s", cbPlaceholder.str(), utf8);
  916. }
  917. void CouchbaseEmbedFunctionContext::compileEmbeddedScript(size32_t chars, const char *script)
  918. {
  919. if (script && *script)
  920. {
  921. // Incoming script is not necessarily null terminated. Note that the chars refers to utf8 characters and not bytes.
  922. size32_t len = rtlUtf8Size(chars, script);
  923. if (len > 0)
  924. {
  925. StringAttr queryScript;
  926. queryScript.set(script, len);
  927. const char * terminatedScript = queryScript.get(); // Now null terminated
  928. if (m_pQcmd)
  929. delete m_pQcmd;
  930. m_pQcmd = new Couchbase::QueryCommand(terminatedScript);
  931. if ((m_scriptFlags & EFnoparams) == 0)
  932. m_numParams = countParameterPlaceholders(terminatedScript);
  933. else
  934. m_numParams = 0;
  935. }
  936. else
  937. failx("Empty N1QL query detected");
  938. }
  939. else
  940. failx("Empty N1QL query detected");
  941. }
  942. void CouchbaseEmbedFunctionContext::callFunction()
  943. {
  944. execute();
  945. }
  946. void CouchbaseEmbedFunctionContext::execute()
  947. {
  948. if (m_oInputStream)
  949. m_oInputStream->executeAll(m_oCBConnection);
  950. else
  951. {
  952. if (m_pQuery)
  953. delete m_pQuery;
  954. m_pQuery = m_oCBConnection->query(m_pQcmd);
  955. reportIfQueryFailure(m_pQuery);
  956. }
  957. }
  958. unsigned CouchbaseEmbedFunctionContext::checkNextParam(const char *name)
  959. {
  960. if (m_nextParam == m_numParams)
  961. failx("Too many parameters supplied: No matching $<name> placeholder for parameter %s", name);
  962. return m_nextParam++;
  963. }
  964. bool CouchbaseRowBuilder::getBooleanResult(const RtlFieldInfo *field)
  965. {
  966. const char * value = nextField(field);
  967. if (!value || !*value)
  968. {
  969. NullFieldProcessor p(field);
  970. return p.boolResult;
  971. }
  972. bool mybool;
  973. couchbaseembed::handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, mybool), "bool", value);
  974. return mybool;
  975. }
  976. void CouchbaseRowBuilder::getDataResult(const RtlFieldInfo *field, size32_t &len, void * &result)
  977. {
  978. const char * value = nextField(field);
  979. if (!value || !*value)
  980. {
  981. NullFieldProcessor p(field);
  982. rtlStrToDataX(len, result, p.resultChars, p.stringResult);
  983. return;
  984. }
  985. rtlStrToDataX(len, result, strlen(value), value); // This feels like it may not work to me - will preallocate rather larger than we want
  986. }
  987. double CouchbaseRowBuilder::getRealResult(const RtlFieldInfo *field)
  988. {
  989. const char * value = nextField(field);
  990. if (!value || !*value)
  991. {
  992. NullFieldProcessor p(field);
  993. return p.doubleResult;
  994. }
  995. double mydouble = 0.0;
  996. couchbaseembed::handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, mydouble), "real", value);
  997. return mydouble;
  998. }
  999. __int64 CouchbaseRowBuilder::getSignedResult(const RtlFieldInfo *field)
  1000. {
  1001. const char * value = nextField(field);
  1002. if (!value || !*value)
  1003. {
  1004. NullFieldProcessor p(field);
  1005. return p.uintResult;
  1006. }
  1007. __int64 myint64 = 0;
  1008. couchbaseembed::handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, myint64), "signed", value);
  1009. return myint64;
  1010. }
  1011. unsigned __int64 CouchbaseRowBuilder::getUnsignedResult(const RtlFieldInfo *field)
  1012. {
  1013. const char * value = nextField(field);
  1014. if (!value || !*value)
  1015. {
  1016. NullFieldProcessor p(field);
  1017. return p.uintResult;
  1018. }
  1019. unsigned __int64 myuint64 = 0;
  1020. couchbaseembed::handleDeserializeOutcome(m_tokenDeserializer.deserialize(value, myuint64), "unsigned", value);
  1021. return myuint64;
  1022. }
  1023. void CouchbaseRowBuilder::getStringResult(const RtlFieldInfo *field, size32_t &chars, char * &result)
  1024. {
  1025. const char * value = nextField(field);
  1026. if (!value || !*value)
  1027. {
  1028. NullFieldProcessor p(field);
  1029. rtlStrToStrX(chars, result, p.resultChars, p.stringResult);
  1030. return;
  1031. }
  1032. unsigned numchars = rtlUtf8Length(strlen(value), value); // MORE - is it a good assumption that it is utf8 ? Depends how the database is configured I think
  1033. rtlUtf8ToStrX(chars, result, numchars, value);
  1034. return;
  1035. }
  1036. void CouchbaseRowBuilder::getUTF8Result(const RtlFieldInfo *field, size32_t &chars, char * &result)
  1037. {
  1038. getStringResult(field, chars, result);
  1039. return;
  1040. }
  1041. void CouchbaseRowBuilder::getUnicodeResult(const RtlFieldInfo *field, size32_t &chars, UChar * &result)
  1042. {
  1043. const char * value = nextField(field);
  1044. if (!value || !*value)
  1045. {
  1046. NullFieldProcessor p(field);
  1047. rtlUnicodeToUnicodeX(chars, result, p.resultChars, p.unicodeResult);
  1048. return;
  1049. }
  1050. unsigned numchars = rtlUtf8Length(strlen(value), value); // MORE - is it a good assumption that it is utf8 ? Depends how the database is configured I think
  1051. rtlUtf8ToUnicodeX(chars, result, numchars, value);
  1052. return;
  1053. }
  1054. void CouchbaseRowBuilder::getDecimalResult(const RtlFieldInfo *field, Decimal &value)
  1055. {
  1056. const char * dvalue = nextField(field);
  1057. if (!dvalue || !*dvalue)
  1058. {
  1059. NullFieldProcessor p(field);
  1060. value.set(p.decimalResult);
  1061. return;
  1062. }
  1063. size32_t chars;
  1064. rtlDataAttr result;
  1065. value.setString(strlen(dvalue), dvalue);
  1066. if (field)
  1067. {
  1068. RtlDecimalTypeInfo *dtype = (RtlDecimalTypeInfo *) field->type;
  1069. value.setPrecision(dtype->getDecimalDigits(), dtype->getDecimalPrecision());
  1070. }
  1071. }
  1072. void CouchbaseRowBuilder::processBeginSet(const RtlFieldInfo * field, bool &isAll)
  1073. {
  1074. isAll = false; // ALL not supported
  1075. StringBuffer xpath;
  1076. xpathOrName(xpath, field);
  1077. if (!xpath.isEmpty())
  1078. {
  1079. PathTracker newPathNode(xpath, CPNTSet);
  1080. StringBuffer newXPath;
  1081. constructNewXPath(newXPath, xpath.str());
  1082. newPathNode.childCount = m_oResultRow->getCount(newXPath);
  1083. m_pathStack.push_back(newPathNode);
  1084. }
  1085. else
  1086. {
  1087. failx("processBeginSet: Field name or xpath missing");
  1088. }
  1089. }
  1090. bool CouchbaseRowBuilder::processNextSet(const RtlFieldInfo * field)
  1091. {
  1092. return m_pathStack.back().childrenProcessed < m_pathStack.back().childCount;
  1093. }
  1094. void CouchbaseRowBuilder::processBeginDataset(const RtlFieldInfo * field)
  1095. {
  1096. StringBuffer xpath;
  1097. xpathOrName(xpath, field);
  1098. if (!xpath.isEmpty())
  1099. {
  1100. PathTracker newPathNode(xpath, CPNTDataset);
  1101. StringBuffer newXPath;
  1102. constructNewXPath(newXPath, xpath.str());
  1103. newPathNode.childCount = m_oResultRow->getCount(newXPath);
  1104. m_pathStack.push_back(newPathNode);
  1105. }
  1106. else
  1107. {
  1108. failx("processBeginDataset: Field name or xpath missing");
  1109. }
  1110. }
  1111. void CouchbaseRowBuilder::processBeginRow(const RtlFieldInfo * field)
  1112. {
  1113. StringBuffer xpath;
  1114. xpathOrName(xpath, field);
  1115. if (!xpath.isEmpty())
  1116. {
  1117. if (strncmp(xpath.str(), "<nested row>", 12) == 0)
  1118. {
  1119. // Row within child dataset
  1120. if (m_pathStack.back().nodeType == CPNTDataset)
  1121. {
  1122. m_pathStack.back().currentChildIndex++;
  1123. }
  1124. else
  1125. {
  1126. failx("<nested row> received with no outer dataset designated");
  1127. }
  1128. }
  1129. else
  1130. {
  1131. m_pathStack.push_back(PathTracker(xpath, CPNTScalar));
  1132. }
  1133. }
  1134. else
  1135. {
  1136. failx("processBeginRow: Field name or xpath missing");
  1137. }
  1138. }
  1139. bool CouchbaseRowBuilder::processNextRow(const RtlFieldInfo * field)
  1140. {
  1141. return m_pathStack.back().childrenProcessed < m_pathStack.back().childCount;
  1142. }
  1143. void CouchbaseRowBuilder::processEndSet(const RtlFieldInfo * field)
  1144. {
  1145. StringBuffer xpath;
  1146. xpathOrName(xpath, field);
  1147. if (!xpath.isEmpty() && !m_pathStack.empty() && strcmp(xpath.str(), m_pathStack.back().nodeName.str()) == 0)
  1148. {
  1149. m_pathStack.pop_back();
  1150. }
  1151. }
  1152. void CouchbaseRowBuilder::processEndDataset(const RtlFieldInfo * field)
  1153. {
  1154. StringBuffer xpath;
  1155. xpathOrName(xpath, field);
  1156. if (!xpath.isEmpty())
  1157. {
  1158. if (!m_pathStack.empty() && strcmp(xpath.str(), m_pathStack.back().nodeName.str()) == 0)
  1159. {
  1160. m_pathStack.pop_back();
  1161. }
  1162. }
  1163. else
  1164. {
  1165. failx("processEndDataset: Field name or xpath missing");
  1166. }
  1167. }
  1168. void CouchbaseRowBuilder::processEndRow(const RtlFieldInfo * field)
  1169. {
  1170. StringBuffer xpath;
  1171. xpathOrName(xpath, field);
  1172. if (!xpath.isEmpty())
  1173. {
  1174. if (!m_pathStack.empty())
  1175. {
  1176. if (m_pathStack.back().nodeType == CPNTDataset)
  1177. {
  1178. m_pathStack.back().childrenProcessed++;
  1179. }
  1180. else if (strcmp(xpath.str(), m_pathStack.back().nodeName.str()) == 0)
  1181. {
  1182. m_pathStack.pop_back();
  1183. }
  1184. }
  1185. }
  1186. else
  1187. {
  1188. failx("processEndRow: Field name or xpath missing");
  1189. }
  1190. }
  1191. const char * CouchbaseRowBuilder::nextField(const RtlFieldInfo * field)
  1192. {
  1193. StringBuffer xpath;
  1194. xpathOrName(xpath, field);
  1195. if (xpath.isEmpty())
  1196. {
  1197. failx("nextField: Field name or xpath missing");
  1198. }
  1199. StringBuffer fullXPath;
  1200. if (!m_pathStack.empty() && m_pathStack.back().nodeType == CPNTSet && strncmp(xpath.str(), "<set element>", 13) == 0)
  1201. {
  1202. m_pathStack.back().currentChildIndex++;
  1203. constructNewXPath(fullXPath, NULL);
  1204. m_pathStack.back().childrenProcessed++;
  1205. }
  1206. else
  1207. {
  1208. constructNewXPath(fullXPath, xpath.str());
  1209. }
  1210. return m_oResultRow->queryProp(fullXPath.str());
  1211. }
  1212. void CouchbaseRowBuilder::xpathOrName(StringBuffer & outXPath, const RtlFieldInfo * field) const
  1213. {
  1214. outXPath.clear();
  1215. if (field->xpath)
  1216. {
  1217. if (field->xpath[0] == xpathCompoundSeparatorChar)
  1218. {
  1219. outXPath.append(field->xpath + 1);
  1220. }
  1221. else
  1222. {
  1223. const char * sep = strchr(field->xpath, xpathCompoundSeparatorChar);
  1224. if (!sep)
  1225. {
  1226. outXPath.append(field->xpath);
  1227. }
  1228. else
  1229. {
  1230. outXPath.append(field->xpath, 0, static_cast<size32_t>(sep - field->xpath));
  1231. }
  1232. }
  1233. }
  1234. else
  1235. {
  1236. outXPath.append(field->name);
  1237. }
  1238. }
  1239. void CouchbaseRowBuilder::constructNewXPath(StringBuffer& outXPath, const char * nextNode) const
  1240. {
  1241. bool nextNodeIsFromRoot = (nextNode && *nextNode == '/');
  1242. outXPath.clear();
  1243. if (!nextNodeIsFromRoot)
  1244. {
  1245. // Build up full parent xpath using our previous components
  1246. for (std::vector<PathTracker>::const_iterator iter = m_pathStack.begin(); iter != m_pathStack.end(); iter++)
  1247. {
  1248. if (strncmp(iter->nodeName, "<row>", 5) != 0)
  1249. {
  1250. if (!outXPath.isEmpty())
  1251. {
  1252. outXPath.append("/");
  1253. }
  1254. outXPath.append(iter->nodeName);
  1255. if (iter->nodeType == CPNTDataset || iter->nodeType == CPNTSet)
  1256. {
  1257. outXPath.appendf("[%d]", iter->currentChildIndex);
  1258. }
  1259. }
  1260. }
  1261. }
  1262. if (nextNode && *nextNode)
  1263. {
  1264. if (!outXPath.isEmpty())
  1265. {
  1266. outXPath.append("/");
  1267. }
  1268. outXPath.append(nextNode);
  1269. }
  1270. }
  1271. class CouchbaseEmbedContext : public CInterfaceOf<IEmbedContext>
  1272. {
  1273. public:
  1274. virtual IEmbedFunctionContext * createFunctionContext(unsigned flags, const char *options)
  1275. {
  1276. return createFunctionContextEx(NULL, flags, options);
  1277. }
  1278. virtual IEmbedFunctionContext * createFunctionContextEx(ICodeContext * ctx, unsigned flags, const char *options)
  1279. {
  1280. if (flags & EFimport)
  1281. {
  1282. UNSUPPORTED("IMPORT");
  1283. return nullptr;
  1284. }
  1285. else
  1286. return new CouchbaseEmbedFunctionContext(ctx ? ctx->queryContextLogger() : queryDummyContextLogger(), options, flags);
  1287. }
  1288. virtual IEmbedServiceContext * createServiceContext(const char *service, unsigned flags, const char *options)
  1289. {
  1290. throwUnexpected();
  1291. return nullptr;
  1292. }
  1293. };
  1294. extern DECL_EXPORT IEmbedContext* getEmbedContext()
  1295. {
  1296. return new CouchbaseEmbedContext();
  1297. }
  1298. extern DECL_EXPORT bool syntaxCheck(const char *script)
  1299. {
  1300. return true; // TO-DO
  1301. }
  1302. } // namespace
  1303. MODULE_INIT(INIT_PRIORITY_STANDARD)
  1304. {
  1305. couchbaseembed::connectionCache = nullptr;
  1306. couchbaseembed::connectionCacheExpirer = nullptr;
  1307. return true;
  1308. }
  1309. MODULE_EXIT()
  1310. {
  1311. // Delete the background thread expiring items from the CouchbaseConnection
  1312. // cache before deleting the connection cache
  1313. if (couchbaseembed::connectionCacheExpirer)
  1314. {
  1315. couchbaseembed::connectionCacheExpirer->stop();
  1316. delete(couchbaseembed::connectionCacheExpirer);
  1317. couchbaseembed::connectionCacheExpirer = nullptr;
  1318. }
  1319. if (couchbaseembed::connectionCache)
  1320. {
  1321. couchbaseembed::connectionCache->deleteAll();
  1322. delete(couchbaseembed::connectionCache);
  1323. couchbaseembed::connectionCache = nullptr;
  1324. }
  1325. }