1 /************ CMgoConn C++ Functions Source Code File (.CPP) ***********/
2 /* Name: CMgoConn.CPP Version 1.1 */
3 /* */
4 /* (C) Copyright to the author Olivier BERTRAND 2017 - 2021 */
5 /* */
6 /* This file contains the MongoDB C connection classes functions. */
7 /***********************************************************************/
8
9 /***********************************************************************/
10 /* Include relevant MariaDB header file. */
11 /***********************************************************************/
12 #include <my_global.h>
13
14 /***********************************************************************/
15 /* Required objects includes. */
16 /***********************************************************************/
17 #include "global.h"
18 #include "plgdbsem.h"
19 #include "colblk.h"
20 #include "xobject.h"
21 #include "xtable.h"
22 #include "filter.h"
23 #include "cmgoconn.h"
24
25 bool CMgoConn::IsInit = false;
26
27 bool IsArray(PSZ s);
28 bool MakeSelector(PGLOBAL g, PFIL fp, PSTRG s);
29 int GetDefaultPrec(void);
30
31 /* --------------------------- Class INCOL --------------------------- */
32
33 /***********************************************************************/
34 /* Add a column in the column list. */
35 /***********************************************************************/
AddCol(PGLOBAL g,PCOL colp,char * jp)36 void INCOL::AddCol(PGLOBAL g, PCOL colp, char *jp)
37 {
38 char *p;
39 PKC kp, kcp;
40
41 if ((p = strchr(jp, '.'))) {
42 PINCOL icp;
43
44 *p++ = 0;
45
46 for (kp = Klist; kp; kp = kp->Next)
47 if (kp->Incolp && !strcmp(jp, kp->Key))
48 break;
49
50 if (!kp) {
51 icp = new(g) INCOL();
52 kcp = (PKC)PlugSubAlloc(g, NULL, sizeof(KEYCOL));
53 kcp->Next = NULL;
54 kcp->Incolp = icp;
55 kcp->Colp = NULL;
56 kcp->Key = PlugDup(g, jp);
57 kcp->Array = IsArray(p);
58
59 if (Klist) {
60 for (kp = Klist; kp->Next; kp = kp->Next);
61
62 kp->Next = kcp;
63 } else
64 Klist = kcp;
65
66 } else
67 icp = kp->Incolp;
68
69 *(p - 1) = '.';
70 icp->AddCol(g, colp, p);
71 } else {
72 kcp = (PKC)PlugSubAlloc(g, NULL, sizeof(KEYCOL));
73
74 kcp->Next = NULL;
75 kcp->Incolp = NULL;
76 kcp->Colp = colp;
77 kcp->Key = jp;
78 kcp->Array = IsArray(jp);
79
80 if (Klist) {
81 for (kp = Klist; kp->Next; kp = kp->Next);
82
83 kp->Next = kcp;
84 } else
85 Klist = kcp;
86
87 } // endif jp
88
89 } // end of AddCol
90
91 /***********************************************************************/
92 /* Clear. */
93 /***********************************************************************/
Init(void)94 void INCOL::Init(void)
95 {
96 bson_init(Child);
97
98 for (PKC kp = Klist; kp; kp = kp->Next)
99 if (kp->Incolp)
100 kp->Incolp->Init();
101
102 } // end of init
103
104 /***********************************************************************/
105 /* Destroy. */
106 /***********************************************************************/
Destroy(void)107 void INCOL::Destroy(void)
108 {
109 bson_destroy(Child);
110
111 for (PKC kp = Klist; kp; kp = kp->Next)
112 if (kp->Incolp)
113 kp->Incolp->Destroy();
114
115 } // end of Destroy
116
117 /* -------------------------- Class CMgoConn ------------------------- */
118
119 /***********************************************************************/
120 /* Implementation of the CMgoConn class. */
121 /***********************************************************************/
CMgoConn(PGLOBAL g,PCPARM pcg)122 CMgoConn::CMgoConn(PGLOBAL g, PCPARM pcg)
123 {
124 Pcg = pcg;
125 Uri = NULL;
126 //Pool = NULL;
127 Client = NULL;
128 Database = NULL;
129 Collection = NULL;
130 Cursor = NULL;
131 Document = NULL;
132 Query = NULL;
133 Opts = NULL;
134 Fpc = NULL;
135 fp = NULL;
136 m_Connected = false;
137 } // end of CMgoConn standard constructor
138
139 /***********************************************************************/
140 /* Required to initialize libmongoc's internals. */
141 /***********************************************************************/
mongo_init(bool init)142 void CMgoConn::mongo_init(bool init)
143 {
144 if (init)
145 mongoc_init();
146 else if (IsInit)
147 mongoc_cleanup();
148
149 IsInit = init;
150 } // end of mongo_init
151
152 /***********************************************************************/
153 /* Connect to the MongoDB server and get the collection. */
154 /***********************************************************************/
Connect(PGLOBAL g)155 bool CMgoConn::Connect(PGLOBAL g)
156 {
157 if (!Pcg->Db_name || !Pcg->Coll_name) {
158 // This would crash in mongoc_client_get_collection
159 strcpy(g->Message, "Missing DB or collection name");
160 return true;
161 } // endif name
162
163 if (!IsInit)
164 #if defined(_WIN32)
165 __try {
166 mongo_init(true);
167 } __except (EXCEPTION_EXECUTE_HANDLER) {
168 strcpy(g->Message, "Cannot load MongoDB C driver");
169 return true;
170 } // end try/except
171 #else // !_WIN32
172 mongo_init(true);
173 #endif // !_WIN32
174
175 Uri = mongoc_uri_new_with_error(Pcg->Uristr, &Error);
176
177 if (!Uri) {
178 sprintf(g->Message, "Failed to parse URI: \"%s\" Msg: %s",
179 Pcg->Uristr, Error.message);
180 return true;
181 } // endif Uri
182
183 #if 0
184 // Create a new client pool instance
185 Pool = mongoc_client_pool_new(Uri);
186 mongoc_client_pool_set_error_api(Pool, 2);
187
188 // Register the application name so we can track it in the profile logs
189 // on the server. This can also be done from the URI.
190 mongoc_client_pool_set_appname(Pool, "Connect");
191
192 // Create a new client instance
193 Client = mongoc_client_pool_pop(Pool);
194 #else
195 // Create a new client instance
196 Client = mongoc_client_new_from_uri (Uri);
197
198 if (!Client) {
199 sprintf(g->Message, "Failed to get Client");
200 return true;
201 } // endif Client
202
203 // Register the application name so we can track it in the profile logs
204 // on the server. This can also be done from the URI (see other examples).
205 mongoc_client_set_appname (Client, "Connect");
206
207 // Get a handle on the database
208 // Database = mongoc_client_get_database (Client, Pcg->Db_name);
209 #endif // 0
210
211 // Get a handle on the collection
212 Collection = mongoc_client_get_collection(Client, Pcg->Db_name, Pcg->Coll_name);
213
214 if (!Collection) {
215 sprintf(g->Message, "Failed to get Collection %s.%s",
216 Pcg->Db_name, Pcg->Coll_name);
217 return true;
218 } // endif Collection
219
220 /*********************************************************************/
221 /* Link a Fblock. This make possible to automatically close it */
222 /* in case of error (throw). */
223 /*********************************************************************/
224 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
225
226 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
227 fp->Type = TYPE_FB_MONGO;
228 fp->Fname = NULL;
229 fp->Next = dbuserp->Openlist;
230 dbuserp->Openlist = fp;
231 fp->Count = 1;
232 fp->Length = 0;
233 fp->Memory = NULL;
234 fp->Mode = MODE_ANY;
235 fp->File = this;
236 fp->Handle = 0;
237
238 m_Connected = true;
239 return false;
240 } // end of Connect
241
242 /***********************************************************************/
243 /* CollSize: returns the number of documents in the collection. */
244 /***********************************************************************/
CollSize(PGLOBAL g)245 int CMgoConn::CollSize(PGLOBAL g)
246 {
247 int cnt;
248 bson_t* query;
249 const char* jf = NULL;
250
251 if (Pcg->Pipe)
252 return 10;
253 else if (Pcg->Filter)
254 jf = Pcg->Filter;
255
256 if (jf) {
257 query = bson_new_from_json((const uint8_t*)jf, -1, &Error);
258
259 if (!query) {
260 htrc("Wrong filter: %s", Error.message);
261 return 10;
262 } // endif Query
263
264 } else
265 query = bson_new();
266
267 #if defined(DEVELOPMENT)
268 if (jf)
269 cnt = (int)mongoc_collection_count_documents(Collection,
270 query, NULL, NULL, NULL, &Error);
271 else
272 cnt = (int)mongoc_collection_estimated_document_count(
273 Collection, NULL, NULL, NULL, &Error);
274 #else
275 cnt = (int)mongoc_collection_count(Collection,
276 MONGOC_QUERY_NONE, query, 0, 0, NULL, &Error);
277 #endif
278
279 if (cnt < 0) {
280 htrc("Collection count: %s", Error.message);
281 cnt = 2;
282 } // endif Cardinal
283
284 bson_destroy(query);
285 return cnt;
286 } // end of CollSize
287
288 /***********************************************************************/
289 /* Project: make the projection avoid path collision. */
290 /***********************************************************************/
Project(PGLOBAL g,PSTRG s)291 void CMgoConn::Project(PGLOBAL g, PSTRG s)
292 {
293 bool m, b = false;
294 size_t n;
295 PSZ path;
296 PCOL cp;
297 PTDB tp = Pcg->Tdbp;
298 PTHP hp, php = NULL, * nphp = &php;
299
300 for (cp = tp->GetColumns(); cp; cp = cp->GetNext()) {
301 path = cp->GetJpath(g, true);
302
303 // Resolve path collision
304 for (hp = php; hp; hp = hp->Next) {
305 if (strlen(path) < strlen(hp->Path)) {
306 n = strlen(path);
307 m = true;
308 } else {
309 n = strlen(hp->Path);
310 m = false;
311 } // endif path
312
313 if (!strncmp(path, hp->Path, n))
314 break;
315
316 } // endfor hp
317
318 if (!hp) {
319 // New path
320 hp = (PTHP)PlugSubAlloc(g, NULL, sizeof(PTH));
321 hp->Path = path;
322 hp->Name = cp->GetName();
323 hp->Next = NULL;
324 *nphp = hp;
325 nphp = &hp->Next;
326 } else if (m) // Smaller path must replace longer one
327 hp->Path = path;
328
329 } // endfor cp
330
331 for (hp = php; hp; hp = hp->Next) {
332 if (b)
333 s->Append(",\"");
334 else
335 b = true;
336
337 if (*hp->Path == '{') {
338 // This is a Mongo defined column
339 s->Append(hp->Name);
340 s->Append("\":");
341 s->Append(hp->Path);
342 } else {
343 s->Append(hp->Path);
344 s->Append("\":1");
345 } // endif Path
346
347 } // endfor hp
348
349 } // end of Project
350
351 /***********************************************************************/
352 /* MakeCursor: make the cursor used to retrieve documents. */
353 /***********************************************************************/
MakeCursor(PGLOBAL g)354 bool CMgoConn::MakeCursor(PGLOBAL g)
355 {
356 const char *p;
357 bool id, all = false;
358 PCSZ options = Pcg->Options;
359 PTDB tp = Pcg->Tdbp;
360 PCOL cp;
361 PSTRG s = NULL;
362 PFIL filp = tp->GetFilter();
363
364 id = (tp->GetMode() == MODE_UPDATE || tp->GetMode() == MODE_DELETE);
365
366 if (options && !stricmp(options, "all")) {
367 options = NULL;
368 all = true;
369 } else for (cp = tp->GetColumns(); cp && !all; cp = cp->GetNext())
370 if (cp->GetFmt() && !strcmp(cp->GetFmt(), "*") && !options)
371 all = true;
372 else if (!id)
373 id = !strcmp(cp->GetFmt() ? cp->GetFmt() : cp->GetName(), "_id");
374
375 if (Pcg->Pipe) {
376 if (trace(1))
377 htrc("Pipeline: %s\n", options);
378
379 p = strrchr(options, ']');
380
381 if (!p) {
382 strcpy(g->Message, "Missing ] in pipeline");
383 return true;
384 } else
385 *(char*)p = 0;
386
387 s = new(g) STRING(g, 1023, (PSZ)options);
388
389 if (filp) {
390 s->Append(",{\"$match\":");
391
392 if (MakeSelector(g, filp, s)) {
393 strcpy(g->Message, "Failed making selector");
394 return true;
395 } else
396 s->Append('}');
397
398 tp->SetFilter(NULL); // Not needed anymore
399 } // endif To_Filter
400
401 if (tp->GetColumns() && !strstr(s->GetStr(), "$project")) {
402 // Project list
403 s->Append(",{\"$project\":{\"");
404
405 if (!id)
406 s->Append("_id\":0,\"");
407
408 Project(g, s);
409 s->Append("}}");
410 } // endif all
411
412 s->Append("]}");
413 s->Resize(s->GetLength() + 1);
414 *(char*)p = ']'; // Restore Colist for discovery
415 p = s->GetStr();
416
417 if (trace(33))
418 htrc("New Pipeline: %s\n", p);
419
420 Query = bson_new_from_json((const uint8_t *)p, -1, &Error);
421
422 if (!Query) {
423 sprintf(g->Message, "Wrong pipeline: %s", Error.message);
424 return true;
425 } // endif Query
426
427 Cursor = mongoc_collection_aggregate(Collection, MONGOC_QUERY_NONE,
428 Query, NULL, NULL);
429
430 if (mongoc_cursor_error(Cursor, &Error)) {
431 sprintf(g->Message, "Mongo aggregate Failure: %s", Error.message);
432 return true;
433 } // endif error
434
435 } else {
436 if (Pcg->Filter || filp) {
437 if (trace(1)) {
438 if (Pcg->Filter)
439 htrc("Filter: %s\n", Pcg->Filter);
440
441 if (filp) {
442 char buf[512];
443
444 filp->Prints(g, buf, 511);
445 htrc("To_Filter: %s\n", buf);
446 } // endif To_Filter
447
448 } // endif trace
449
450 s = new(g) STRING(g, 1023, (PSZ)Pcg->Filter);
451
452 if (filp) {
453 if (Pcg->Filter)
454 s->Append(',');
455
456 if (MakeSelector(g, filp, s)) {
457 strcpy(g->Message, "Failed making selector");
458 return true;
459 } // endif Selector
460
461 tp->SetFilter(NULL); // Not needed anymore
462 } // endif To_Filter
463
464 if (trace(33))
465 htrc("selector: %s\n", s->GetStr());
466
467 s->Resize(s->GetLength() + 1);
468 Query = bson_new_from_json((const uint8_t *)s->GetStr(), -1, &Error);
469
470 if (!Query) {
471 sprintf(g->Message, "Wrong filter: %s", Error.message);
472 return true;
473 } // endif Query
474
475 } else
476 Query = bson_new();
477
478 if (!all) {
479 if (options && *options) {
480 if (trace(1))
481 htrc("options=%s\n", options);
482
483 p = options;
484 } else if (tp->GetColumns()) {
485 // Projection list
486 if (s)
487 s->Set("{\"projection\":{\"");
488 else
489 s = new(g) STRING(g, 511, "{\"projection\":{\"");
490
491 if (!id)
492 s->Append("_id\":0,\"");
493
494 Project(g, s);
495 s->Append("}}");
496 s->Resize(s->GetLength() + 1);
497 p = s->GetStr();
498 } else {
499 // count(*) ?
500 p = "{\"projection\":{\"_id\":1}}";
501 } // endif Options
502
503 Opts = bson_new_from_json((const uint8_t *)p, -1, &Error);
504
505 if (!Opts) {
506 sprintf(g->Message, "Wrong options: %s", Error.message);
507 return true;
508 } // endif Opts
509
510 } // endif all
511
512 Cursor = mongoc_collection_find_with_opts(Collection, Query, Opts, NULL);
513 } // endif Pipe
514
515 return false;
516 } // end of MakeCursor
517
518 /***********************************************************************/
519 /* Fetch next document. */
520 /***********************************************************************/
ReadNext(PGLOBAL g)521 int CMgoConn::ReadNext(PGLOBAL g)
522 {
523 int rc = RC_OK;
524
525 if (!Cursor && MakeCursor(g)) {
526 rc = RC_FX;
527 } else if (mongoc_cursor_next(Cursor, &Document)) {
528 if (trace(512)) {
529 bson_iter_t iter;
530 ShowDocument(&iter, Document, "");
531 } else if (trace(1))
532 htrc("%s\n", GetDocument(g));
533
534 } else if (mongoc_cursor_error(Cursor, &Error)) {
535 sprintf(g->Message, "Mongo Cursor Failure: %s", Error.message);
536 rc = RC_FX;
537 } else
538 rc = RC_EF;
539
540 return rc;
541 } // end of Fetch
542
543 /***********************************************************************/
544 /* Get the Json string of the current document. */
545 /***********************************************************************/
GetDocument(PGLOBAL g)546 PSZ CMgoConn::GetDocument(PGLOBAL g)
547 {
548 char *str = bson_as_json(Document, NULL);
549 PSZ doc = PlugDup(g, str);
550
551 bson_free(str);
552 return doc;
553 } // end of GetDocument
554
555 /***********************************************************************/
556 /* Use to trace restaurants document contains. */
557 /***********************************************************************/
ShowDocument(bson_iter_t * iter,const bson_t * doc,const char * k)558 void CMgoConn::ShowDocument(bson_iter_t *iter, const bson_t *doc, const char *k)
559 {
560 if (!doc || bson_iter_init(iter, doc)) {
561 const char *key;
562
563 while (bson_iter_next(iter)) {
564 key = bson_iter_key(iter);
565 htrc("Found element key: \"%s\"\n", key);
566
567 switch (bson_iter_type(iter)) {
568 case BSON_TYPE_UTF8:
569 htrc("%s.%s=\"%s\"\n", k, key, bson_iter_utf8(iter, NULL));
570 break;
571 case BSON_TYPE_INT32:
572 htrc("%s.%s=%d\n", k, key, bson_iter_int32(iter));
573 break;
574 case BSON_TYPE_INT64:
575 htrc("%s.%s=%lld\n", k, key, bson_iter_int64(iter));
576 break;
577 case BSON_TYPE_DOUBLE:
578 htrc("%s.%s=%g\n", k, key, bson_iter_double(iter));
579 break;
580 case BSON_TYPE_DATE_TIME:
581 htrc("%s.%s=date(%lld)\n", k, key, bson_iter_date_time(iter));
582 break;
583 case BSON_TYPE_OID: {
584 char str[25];
585
586 bson_oid_to_string(bson_iter_oid(iter), str);
587 htrc("%s.%s=%s\n", k, key, str);
588 } break;
589 case BSON_TYPE_DECIMAL128: {
590 char str[BSON_DECIMAL128_STRING];
591 bson_decimal128_t dec;
592
593 bson_iter_decimal128(iter, &dec);
594 bson_decimal128_to_string(&dec, str);
595 htrc("%s.%s=%s\n", k, key, str);
596 } break;
597 case BSON_TYPE_DOCUMENT: {
598 bson_iter_t child;
599
600 if (bson_iter_recurse(iter, &child))
601 ShowDocument(&child, NULL, key);
602
603 } break;
604 case BSON_TYPE_ARRAY: {
605 bson_t* arr;
606 bson_iter_t itar;
607 const uint8_t* data = NULL;
608 uint32_t len = 0;
609
610 bson_iter_array(iter, &len, &data);
611 arr = bson_new_from_data(data, len);
612 ShowDocument(&itar, arr, key);
613 } break;
614 } // endswitch iter
615
616 } // endwhile bson_iter_next
617
618 } // endif bson_iter_init
619
620 } // end of ShowDocument
621
622 /***********************************************************************/
623 /* Group columns for inserting or updating. */
624 /***********************************************************************/
MakeColumnGroups(PGLOBAL g)625 void CMgoConn::MakeColumnGroups(PGLOBAL g)
626 {
627 Fpc = new(g) INCOL();
628
629 for (PCOL colp = Pcg->Tdbp->GetColumns(); colp; colp = colp->GetNext())
630 if (!colp->IsSpecial())
631 Fpc->AddCol(g, colp, colp->GetJpath(g, false));
632
633 } // end of MakeColumnGroups
634
635 /***********************************************************************/
636 /* DocWrite. */
637 /***********************************************************************/
DocWrite(PGLOBAL g,PINCOL icp)638 bool CMgoConn::DocWrite(PGLOBAL g, PINCOL icp)
639 {
640 for (PKC kp = icp->Klist; kp; kp = kp->Next)
641 if (kp->Incolp) {
642 bool isdoc = !kp->Array;
643
644 if (isdoc)
645 BSON_APPEND_DOCUMENT_BEGIN(icp->Child, kp->Key, kp->Incolp->Child);
646 else
647 BSON_APPEND_ARRAY_BEGIN(icp->Child, kp->Key, kp->Incolp->Child);
648
649 if (DocWrite(g, kp->Incolp))
650 return true;
651
652 if (isdoc)
653 bson_append_document_end(icp->Child, kp->Incolp->Child);
654 else
655 bson_append_array_end(icp->Child, kp->Incolp->Child);
656
657 } else if (AddValue(g, kp->Colp, icp->Child, kp->Key, false))
658 return true;
659
660 return false;
661 } // end of DocWrite
662
663 /***********************************************************************/
664 /* WriteDB: Data Base write routine for CMGO access method. */
665 /***********************************************************************/
Write(PGLOBAL g)666 int CMgoConn::Write(PGLOBAL g)
667 {
668 int rc = RC_OK;
669 PTDB tp = Pcg->Tdbp;
670
671 if (tp->GetMode() == MODE_INSERT) {
672 if (!Pcg->Line) {
673 Fpc->Init();
674
675 if (DocWrite(g, Fpc))
676 return RC_FX;
677
678 if (trace(2)) {
679 char* str = bson_as_json(Fpc->Child, NULL);
680 htrc("Inserting: %s\n", str);
681 bson_free(str);
682 } // endif trace
683
684 if (!mongoc_collection_insert(Collection, MONGOC_INSERT_NONE,
685 Fpc->Child, NULL, &Error)) {
686 sprintf(g->Message, "Mongo insert: %s", Error.message);
687 rc = RC_FX;
688 } // endif insert
689
690 } else {
691 const uint8_t* val = (const uint8_t*)Pcg->Line;
692 bson_t* doc = bson_new_from_json(val, -1, &Error);
693
694 if (doc && trace(2)) {
695 char* str = bson_as_json(doc, NULL);
696 htrc("Inserting: %s\n", str);
697 bson_free(str);
698 } // endif trace
699
700 if (!doc) {
701 sprintf(g->Message, "bson_new_from_json: %s", Error.message);
702 rc = RC_FX;
703 } else if (!mongoc_collection_insert(Collection,
704 MONGOC_INSERT_NONE, doc, NULL, &Error)) {
705 sprintf(g->Message, "Mongo insert: %s", Error.message);
706 bson_destroy(doc);
707 rc = RC_FX;
708 } // endif insert
709
710 } // endif Line
711
712 } else {
713 bool b = false;
714 bson_iter_t iter;
715 bson_t *query = bson_new();
716
717 bson_iter_init(&iter, Document);
718
719 if (bson_iter_find(&iter, "_id"))
720 switch (bson_iter_type(&iter)) {
721 case BSON_TYPE_OID:
722 b = BSON_APPEND_OID(query, "_id", bson_iter_oid(&iter));
723 break;
724 case BSON_TYPE_UTF8:
725 b = BSON_APPEND_UTF8(query, "_id", bson_iter_utf8(&iter, NULL));
726 break;
727 case BSON_TYPE_INT32:
728 b = BSON_APPEND_INT32(query, "_id", bson_iter_int32(&iter));
729 break;
730 case BSON_TYPE_INT64:
731 b = BSON_APPEND_INT64(query, "_id", bson_iter_int64(&iter));
732 break;
733 case BSON_TYPE_DOUBLE:
734 b = BSON_APPEND_DOUBLE(query, "_id", bson_iter_double(&iter));
735 break;
736 default:
737 break;
738 } // endswitch iter
739
740 if (b) {
741 if (trace(2)) {
742 char *str = bson_as_json(query, NULL);
743 htrc("update query: %s\n", str);
744 bson_free(str);
745 } // endif trace
746
747 if (tp->GetMode() == MODE_UPDATE) {
748 bson_t child;
749 bson_t *update = bson_new();
750
751 BSON_APPEND_DOCUMENT_BEGIN(update, "$set", &child);
752
753 for (PCOL colp = tp->GetSetCols(); colp; colp = colp->GetNext())
754 if (AddValue(g, colp, &child, colp->GetJpath(g, false), true))
755 rc = RC_FX;
756
757 bson_append_document_end(update, &child);
758
759 if (rc == RC_OK)
760 if (!mongoc_collection_update(Collection, MONGOC_UPDATE_NONE,
761 query, update, NULL, &Error)) {
762 sprintf(g->Message, "Mongo update: %s", Error.message);
763 rc = RC_FX;
764 } // endif update
765
766 bson_destroy(update);
767 } else if (!mongoc_collection_remove(Collection,
768 MONGOC_REMOVE_SINGLE_REMOVE, query, NULL, &Error)) {
769 sprintf(g->Message, "Mongo delete: %s", Error.message);
770 rc = RC_FX;
771 } // endif remove
772
773 } else {
774 strcpy(g->Message, "Mongo update: cannot find _id");
775 rc = RC_FX;
776 } // endif b
777
778 bson_destroy(query);
779 } // endif Mode
780
781 return rc;
782 } // end of Write
783
784 /***********************************************************************/
785 /* Remove all documents from the collection. */
786 /***********************************************************************/
DocDelete(PGLOBAL g)787 bool CMgoConn::DocDelete(PGLOBAL g)
788 {
789 Query = bson_new();
790
791 if (!mongoc_collection_remove(Collection, MONGOC_REMOVE_NONE,
792 Query, NULL, &Error)) {
793 sprintf(g->Message, "Mongo remove all: %s", Error.message);
794 return true;
795 } // endif remove
796
797 return false;
798 } // end of DocDelete
799
800 /***********************************************************************/
801 /* Rewind the collection. */
802 /***********************************************************************/
Rewind(void)803 void CMgoConn::Rewind(void)
804 {
805 mongoc_cursor_t *cursor = mongoc_cursor_clone(Cursor);
806
807 mongoc_cursor_destroy(Cursor);
808 Cursor = cursor;
809 } // end of Rewind
810
811 /***********************************************************************/
812 /* Table close routine for MONGO tables. */
813 /***********************************************************************/
Close(void)814 void CMgoConn::Close(void)
815 {
816 if (Query) bson_destroy(Query);
817 if (Opts) bson_destroy(Opts);
818 if (Cursor) mongoc_cursor_destroy(Cursor);
819 if (Collection) mongoc_collection_destroy(Collection);
820 //if (Client) mongoc_client_pool_push(Pool, Client);
821 //if (Pool) mongoc_client_pool_destroy(Pool);
822 if (Client) mongoc_client_destroy(Client);
823 if (Uri) mongoc_uri_destroy(Uri);
824 if (Fpc) Fpc->Destroy();
825 if (fp) fp->Count = 0;
826 } // end of Close
827
828 /***********************************************************************/
829 /* Mini: used to suppress blanks to json strings. */
830 /***********************************************************************/
Mini(PGLOBAL g,PCOL colp,const bson_t * bson,bool b)831 char *CMgoConn::Mini(PGLOBAL g, PCOL colp, const bson_t *bson, bool b)
832 {
833 char *s, *str = NULL;
834 char *Mbuf = (char*)PlugSubAlloc(g, NULL, (size_t)colp->GetLength() + 1);
835 int i, j = 0, k = 0, n = 0, m = GetDefaultPrec();
836 bool ok = true, dbl = false;
837 double d;
838 size_t len;
839
840 if (b)
841 s = str = bson_array_as_json(bson, &len);
842 else
843 s = str = bson_as_json(bson, &len);
844
845 if (len > (size_t)colp->GetLength()) {
846 sprintf(g->Message, "Value too long for column %s", colp->GetName());
847 bson_free(str);
848 throw (int)TYPE_AM_MGO;
849 } // endif len
850
851 for (i = 0; i < colp->GetLength() && s[i]; i++) {
852 switch (s[i]) {
853 case ' ':
854 if (ok) continue;
855 break;
856 case '"':
857 ok = !ok;
858 break;
859 case '.':
860 if (j) dbl = true;
861 break;
862 default:
863 if (ok) {
864 if (isdigit(s[i])) {
865 if (!j) j = k;
866 if (dbl) n++;
867 } else if (dbl && n > m) {
868 Mbuf[k] = 0;
869 d = atof(Mbuf + j);
870 n = sprintf(Mbuf + j, "%.*f", m, d);
871 k = j + n;
872 j = n = 0;
873 } else if (j)
874 j = n = 0;
875
876 } // endif ok
877
878 break;
879 } // endswitch s[i]
880
881 Mbuf[k++] = s[i];
882 } // endfor i
883
884 bson_free(str);
885
886 Mbuf[k] = 0;
887 return Mbuf;
888 } // end of Mini
889
890 /***********************************************************************/
891 /* Retrieve the column value from the document. */
892 /***********************************************************************/
GetColumnValue(PGLOBAL g,PCOL colp)893 void CMgoConn::GetColumnValue(PGLOBAL g, PCOL colp)
894 {
895 char *jpath = colp->GetJpath(g, false);
896 bool b = false;
897 PVAL value = colp->GetValue();
898 bson_iter_t Iter; // Used to retrieve column value
899 bson_iter_t Desc; // Descendant iter
900
901 if (*jpath == '{')
902 jpath = colp->GetName(); // This is a Mongo defined column
903
904 if (!*jpath || !strcmp(jpath, "*")) {
905 value->SetValue_psz(Mini(g, colp, Document, false));
906 } else if (bson_iter_init(&Iter, Document) &&
907 bson_iter_find_descendant(&Iter, jpath, &Desc)) {
908 switch (bson_iter_type(&Desc)) {
909 case BSON_TYPE_UTF8:
910 value->SetValue_psz((PSZ)bson_iter_utf8(&Desc, NULL));
911 break;
912 case BSON_TYPE_INT32:
913 value->SetValue(bson_iter_int32(&Desc));
914 break;
915 case BSON_TYPE_INT64:
916 value->SetValue(bson_iter_int64(&Desc));
917 break;
918 case BSON_TYPE_DOUBLE:
919 value->SetValue(bson_iter_double(&Desc));
920 break;
921 case BSON_TYPE_DATE_TIME:
922 value->SetValue(bson_iter_date_time(&Desc) / 1000);
923 break;
924 case BSON_TYPE_BOOL:
925 b = bson_iter_bool(&Desc);
926
927 if (value->IsTypeNum())
928 value->SetValue(b ? 1 : 0);
929 else
930 value->SetValue_psz(b ? "true" : "false");
931
932 break;
933 case BSON_TYPE_OID: {
934 char str[25];
935
936 bson_oid_to_string(bson_iter_oid(&Desc), str);
937 value->SetValue_psz(str);
938 } break;
939 case BSON_TYPE_ARRAY:
940 b = true;
941 // passthru
942 case BSON_TYPE_DOCUMENT:
943 { // All this because MongoDB can return the wrong type
944 int i = 0;
945 const uint8_t *data = NULL;
946 uint32_t len = 0;
947
948 for (; i < 2; i++) {
949 if (b) // Try array first
950 bson_iter_array(&Desc, &len, &data);
951 else
952 bson_iter_document(&Desc, &len, &data);
953
954 if (!data) {
955 len = 0;
956 b = !b;
957 } else
958 break;
959
960 } // endfor i
961
962 if (data) {
963 bson_t *doc = bson_new_from_data(data, len);
964
965 value->SetValue_psz(Mini(g, colp, doc, b));
966 bson_destroy(doc);
967 } else {
968 // ... or we can also come here in case of NULL!
969 value->Reset();
970 value->SetNull(true);
971 } // endif data
972
973 } break;
974 case BSON_TYPE_NULL:
975 // Apparently this does not work...
976 value->Reset();
977 value->SetNull(true);
978 break;
979 case BSON_TYPE_DECIMAL128: {
980 char str[BSON_DECIMAL128_STRING];
981 bson_decimal128_t dec;
982
983 bson_iter_decimal128(&Desc, &dec);
984 bson_decimal128_to_string(&dec, str);
985 value->SetValue_psz(str);
986 // bson_free(str);
987 } break;
988 default:
989 value->Reset();
990 break;
991 } // endswitch Desc
992
993 } else {
994 // Field does not exist
995 value->Reset();
996 value->SetNull(true);
997 } // endif Iter
998
999 } // end of GetColumnValue
1000
1001 /***********************************************************************/
1002 /* AddValue: Add column value to the document to insert or update. */
1003 /***********************************************************************/
AddValue(PGLOBAL g,PCOL colp,bson_t * doc,char * key,bool upd)1004 bool CMgoConn::AddValue(PGLOBAL g, PCOL colp, bson_t *doc, char *key, bool upd)
1005 {
1006 bool rc = false;
1007 PVAL value = colp->GetValue();
1008
1009 if (value->IsNull()) {
1010 // if (upd)
1011 rc = BSON_APPEND_NULL(doc, key);
1012 // else
1013 // return false;
1014
1015 } else switch (colp->GetResultType()) {
1016 case TYPE_STRING:
1017 if (colp->Stringify()) {
1018 const uint8_t *val = (const uint8_t*)value->GetCharValue();
1019 bson_t *bsn = bson_new_from_json(val, -1, &Error);
1020
1021 if (!bsn) {
1022 sprintf (g->Message, "AddValue: %s", Error.message);
1023 return true;
1024 } else if (*key) {
1025 if (*val == '[')
1026 rc = BSON_APPEND_ARRAY(doc, key, bsn);
1027 else
1028 rc = BSON_APPEND_DOCUMENT(doc, key, bsn);
1029
1030 } else {
1031 bson_copy_to (bsn, doc);
1032 rc = true;
1033 } // endif's
1034
1035 bson_free(bsn);
1036 } else
1037 rc = BSON_APPEND_UTF8(doc, key, value->GetCharValue());
1038
1039 break;
1040 case TYPE_INT:
1041 case TYPE_SHORT:
1042 rc = BSON_APPEND_INT32(doc, key, value->GetIntValue());
1043 break;
1044 case TYPE_TINY:
1045 rc = BSON_APPEND_BOOL(doc, key, value->GetIntValue());
1046 break;
1047 case TYPE_BIGINT:
1048 rc = BSON_APPEND_INT64(doc, key, value->GetBigintValue());
1049 break;
1050 case TYPE_DOUBLE:
1051 rc = BSON_APPEND_DOUBLE(doc, key, value->GetFloatValue());
1052 break;
1053 case TYPE_DECIM:
1054 {bson_decimal128_t dec;
1055
1056 if (bson_decimal128_from_string(value->GetCharValue(), &dec))
1057 rc = BSON_APPEND_DECIMAL128(doc, key, &dec);
1058
1059 } break;
1060 case TYPE_DATE:
1061 rc = BSON_APPEND_DATE_TIME(doc, key, value->GetBigintValue() * 1000);
1062 break;
1063 default:
1064 sprintf(g->Message, "Type %d not supported yet", colp->GetResultType());
1065 return true;
1066 } // endswitch Buf_Type
1067
1068 if (!rc) {
1069 strcpy(g->Message, "Adding value failed");
1070 return true;
1071 } else
1072 return false;
1073
1074 } // end of AddValue
1075
1076 #if 0
1077 void *CMgoConn::mgo_alloc(size_t n)
1078 {
1079 char *mst = (char*)PlgDBSubAlloc(G, NULL, n + sizeof(size_t));
1080
1081 if (mst) {
1082 *(size_t*)mst = n;
1083 return mst + sizeof(size_t);
1084 } // endif mst
1085
1086 return NULL;
1087 } // end of mgo_alloc
1088
1089 void *CMgoConn::mgo_calloc(size_t n, size_t sz)
1090 {
1091 void *m = mgo_alloc(n * sz);
1092
1093 if (m)
1094 memset(m, 0, n * sz);
1095
1096 return m;
1097 } // end of mgo_calloc
1098
1099 void *CMgoConn::mgo_realloc(void *m, size_t n)
1100 {
1101 if (!m)
1102 return n ? mgo_alloc(n) : NULL;
1103
1104 size_t *osz = (size_t*)((char*)m - sizeof(size_t));
1105
1106 if (n > *osz) {
1107 void *nwm = mgo_alloc(n);
1108
1109 if (nwm)
1110 memcpy(nwm, m, *osz);
1111
1112 return nwm;
1113 } else {
1114 *osz = n;
1115 return m;
1116 } // endif n
1117
1118 } // end of mgo_realloc
1119 #endif // 0
1120
1121