1 /************ JMgoConn C++ Functions Source Code File (.CPP) ***********/
2 /*  Name: JMgoConn.CPP  Version 1.2                                    */
3 /*                                                                     */
4 /*  (C) Copyright to the author Olivier BERTRAND          2017 - 2021  */
5 /*                                                                     */
6 /*  This file contains the MongoDB Java 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 "jmgoconn.h"
24 
25 #define nullptr 0
26 
27 bool IsArray(PSZ s);
28 bool MakeSelector(PGLOBAL g, PFIL fp, PSTRG s);
29 
30 /* --------------------------- Class JNCOL --------------------------- */
31 
32 /***********************************************************************/
33 /*  Add a column in the column list.                                   */
34 /***********************************************************************/
AddCol(PGLOBAL g,PCOL colp,PSZ jp)35 void JNCOL::AddCol(PGLOBAL g, PCOL colp, PSZ jp)
36 {
37 	char *p;
38 	PJKC  kp, kcp;
39 
40 	if ((p = strchr(jp, '.'))) {
41 		PJNCOL icp;
42 
43 		*p++ = 0;
44 
45 		for (kp = Klist; kp; kp = kp->Next)
46 			if (kp->Jncolp && ((kp->Key && !strcmp(jp, kp->Key))
47 				             || (!kp->Key && IsArray(jp) && kp->N == atoi(jp))))
48 				break;
49 
50 		if (!kp) {
51 			icp = new(g) JNCOL();
52 			kcp = (PJKC)PlugSubAlloc(g, NULL, sizeof(JKCOL));
53 			kcp->Next = NULL;
54 			kcp->Jncolp = icp;
55 			kcp->Colp = NULL;
56 			kcp->Array = IsArray(jp);
57 
58 			if (kcp->Array) {
59 				kcp->Key = NULL;
60 				kcp->N = atoi(jp);
61 			} else {
62 				kcp->Key = PlugDup(g, jp);
63 				kcp->N = 0;
64 			} // endif Array
65 
66 			if (Klist) {
67 				for (kp = Klist; kp->Next; kp = kp->Next);
68 
69 				kp->Next = kcp;
70 			} else
71 				Klist = kcp;
72 
73 		} else
74 			icp = kp->Jncolp;
75 
76 		*(p - 1) = '.';
77 		icp->AddCol(g, colp, p);
78 	} else {
79 		kcp = (PJKC)PlugSubAlloc(g, NULL, sizeof(JKCOL));
80 		kcp->Next = NULL;
81 		kcp->Jncolp = NULL;
82 		kcp->Colp = colp;
83 		kcp->Array = IsArray(jp);
84 
85 		if (kcp->Array) {
86 			kcp->Key = NULL;
87 			kcp->N = atoi(jp);
88 		} else {
89 			kcp->Key = jp;
90 			kcp->N = 0;
91 		} // endif Array
92 
93 		if (Klist) {
94 			for (kp = Klist; kp->Next; kp = kp->Next);
95 
96 			kp->Next = kcp;
97 		} else
98 			Klist = kcp;
99 
100 	} // endif jp
101 
102 }	// end of AddCol
103 
104 /***********************************************************************/
105 /*  JMgoConn construction/destruction.                                 */
106 /***********************************************************************/
JMgoConn(PGLOBAL g,PCSZ collname,PCSZ wrapper)107 JMgoConn::JMgoConn(PGLOBAL g, PCSZ collname, PCSZ wrapper)
108 	       : JAVAConn(g, wrapper)
109 {
110 	CollName = collname;
111 	readid = fetchid = getdocid = objfldid = fcollid = acollid =
112 	mkdocid = docaddid = mkarid = araddid = insertid = updateid =
113 	deleteid = gcollid =	countid =	rewindid = mkbsonid = nullptr;
114 	DiscFunc = "MongoDisconnect";
115 	Fpc = NULL;
116 	m_Fetch = 0;
117 	m_Ncol = 0;
118 	m_Version = 0;
119 } // end of JMgoConn
120 
121 /***********************************************************************/
122 /*  AddJars: add some jar file to the Class path.                      */
123 /***********************************************************************/
AddJars(PSTRG jpop,char sep)124 void JMgoConn::AddJars(PSTRG jpop, char sep)
125 {
126 #if defined(DEVELOPMENT)
127 	if (m_Version == 2) {
128 		jpop->Append(sep);
129 //	jpop->Append("C:/Eclipse/workspace/MongoWrap2/bin");
130 //	jpop->Append(sep);
131 		jpop->Append("C:/mongo-java-driver/mongo-java-driver-2.13.3.jar");
132 	} else {
133 		jpop->Append(sep);
134 //	jpop->Append("C:/Eclipse/workspace/MongoWrap3/bin");
135 //	jpop->Append(sep);
136 //	jpop->Append("C:/Program Files/MariaDB 10.1/lib/plugin/JavaWrappers.jar");
137 //	jpop->Append(sep);
138 		jpop->Append("C:/mongo-java-driver/mongo-java-driver-3.4.2.jar");
139 	} // endif m_Version
140 #endif   // DEVELOPMENT
141 } // end of AddJars
142 
143 /***********************************************************************/
144 /*  Connect: connect to a data source.                                 */
145 /***********************************************************************/
Connect(PJPARM sop)146 bool JMgoConn::Connect(PJPARM sop)
147 {
148 	bool		 err = false;
149 	jint     rc;
150 	jboolean brc;
151 	jstring  cln;
152 	PGLOBAL& g = m_G;
153 
154 	m_Version = sop->Version;
155 
156 	/*******************************************************************/
157 	/*  Create or attach a JVM. 																			 */
158 	/*******************************************************************/
159 	if (Open(g))
160 		return true;
161 
162 	/*******************************************************************/
163 	/*  Connect to MongoDB.      																			 */
164 	/*******************************************************************/
165 	jmethodID cid = nullptr;
166 
167 	if (gmID(g, cid, "MongoConnect", "([Ljava/lang/String;)I"))
168 		return true;
169 
170 	// Build the java string array
171 	jobjectArray parms = env->NewObjectArray(4,    // constructs java array of 4
172 		env->FindClass("java/lang/String"), NULL);   // Strings
173 
174 	//m_Scrollable = sop->Scrollable;
175 	//m_RowsetSize = sop->Fsize;
176 
177 	// change some elements
178 	if (sop->Driver)
179 		env->SetObjectArrayElement(parms, 0, env->NewStringUTF(sop->Url));
180 
181 	if (sop->Url)
182 		env->SetObjectArrayElement(parms, 1, env->NewStringUTF(sop->Driver));
183 
184 	if (sop->User)
185 		env->SetObjectArrayElement(parms, 2, env->NewStringUTF(sop->User));
186 
187 	if (sop->Pwd)
188 		env->SetObjectArrayElement(parms, 3, env->NewStringUTF(sop->Pwd));
189 
190 	// call method
191 	rc = env->CallIntMethod(job, cid, parms);
192 	err = Check(rc);
193 	env->DeleteLocalRef(parms);				 	// Not used anymore
194 
195 	if (err) {
196 		sprintf(g->Message, "Connecting: %s rc=%d", Msg, (int)rc);
197 		return true;
198 	}	// endif Msg
199 
200 	/*********************************************************************/
201 	/*  Get the collection.                                              */
202 	/*********************************************************************/
203 	if (gmID(g, gcollid, "GetCollection", "(Ljava/lang/String;)Z"))
204 		return true;
205 
206 	cln = env->NewStringUTF(CollName);
207 	brc = env->CallBooleanMethod(job, gcollid, cln);
208 	env->DeleteLocalRef(cln);
209 
210 	if (Check(brc ? -1 : 0)) {
211 		sprintf(g->Message, "GetCollection: %s", Msg);
212 		return true;
213 	}	// endif Msg
214 
215 	m_Connected = true;
216 	return false;
217 } // end of Connect
218 
219 /***********************************************************************/
220 /*  CollSize: returns the number of documents in the collection.       */
221 /***********************************************************************/
CollSize(PGLOBAL g)222 int JMgoConn::CollSize(PGLOBAL g)
223 {
224 	if (!gmID(g, countid, "GetCollSize", "()J")) {
225 		jlong card = env->CallLongMethod(job, countid);
226 
227 		return (int)card;
228 	} else
229 		return 2;				 // Make MariaDB happy
230 
231 } // end of CollSize
232 
233 /***********************************************************************/
234 /*  OpenDB: Data Base open routine for MONGO access method.            */
235 /***********************************************************************/
MakeCursor(PGLOBAL g,PTDB tdbp,PCSZ options,PCSZ filter,bool pipe)236 bool JMgoConn::MakeCursor(PGLOBAL g, PTDB tdbp, PCSZ options,
237 	                                               PCSZ filter, bool pipe)
238 {
239 	const char *p;
240 	bool  id, b = false, all = false;
241 	uint  len;
242 	PCOL  cp;
243 	PSZ   jp;
244 	PCSZ  op = NULL, sf = NULL, Options = options;
245 	PSTRG s = NULL;
246 	PFIL  filp = tdbp->GetFilter();
247 
248 	if (Options && !stricmp(Options, "all")) {
249 		Options = NULL;
250 		all = true;
251 	} else
252 		id = (tdbp->GetMode() == MODE_UPDATE || tdbp->GetMode() == MODE_DELETE);
253 
254 	for (cp = tdbp->GetColumns(); cp && !all; cp = cp->GetNext())
255 		if (cp->GetFmt() && !strcmp(cp->GetFmt(), "*") && (!Options || pipe))
256 			all = true;
257 		else if (!id)
258 			id = !strcmp(cp->GetJpath(g, false), "_id");
259 
260 	if (pipe && Options) {
261 		if (trace(1))
262 			htrc("Pipeline: %s\n", Options);
263 
264 		p = strrchr(Options, ']');
265 
266 		if (!p) {
267 			strcpy(g->Message, "Missing ] in pipeline");
268 			return true;
269 		} else
270 			*(char*)p = 0;
271 
272 		s = new(g) STRING(g, 1023, (PSZ)Options);
273 
274 		if (filp) {
275 			s->Append(",{\"$match\":");
276 
277 			if (MakeSelector(g, filp, s)) {
278 				strcpy(g->Message, "Failed making selector");
279 				return true;
280 			} else
281 				s->Append('}');
282 
283 			tdbp->SetFilter(NULL);     // Not needed anymore
284 		} // endif To_Filter
285 
286 		if (!all && tdbp->GetColumns()) {
287 			// Project list
288 			len = s->GetLength();
289 			s->Append(",{\"$project\":{\"");
290 
291 			if (!id)
292 				s->Append("_id\":0,\"");
293 
294 			for (PCOL cp = tdbp->GetColumns(); cp; cp = cp->GetNext()) {
295 				if (b)
296 					s->Append(",\"");
297 				else
298 					b = true;
299 
300 				if ((jp = cp->GetJpath(g, true)))
301 					s->Append(jp);
302 				else {
303 					s->Truncate(len);
304 					goto nop;
305 				}	// endif Jpath
306 
307 				s->Append("\":1");
308 			} // endfor cp
309 
310 			s->Append("}}");
311 		} // endif all
312 
313 	nop:
314 		s->Append("]}");
315 		s->Resize(s->GetLength() + 1);
316 		*(char*)p = ']';		 // Restore Colist for discovery
317 		p = s->GetStr();
318 
319 		if (trace(33))
320 			htrc("New Pipeline: %s\n", p);
321 
322 		return AggregateCollection(p);
323 	} else {
324 		if (filter || filp) {
325 			if (trace(1)) {
326 				if (filter)
327 					htrc("Filter: %s\n", filter);
328 
329 				if (filp) {
330 					char buf[512];
331 
332 					filp->Prints(g, buf, 511);
333 					htrc("To_Filter: %s\n", buf);
334 				} // endif To_Filter
335 
336 			}	// endif trace
337 
338 			s = new(g) STRING(g, 1023, (PSZ)filter);
339 			len = s->GetLength();
340 
341 			if (filp) {
342 				if (filter)
343 					s->Append(',');
344 
345 				if (MakeSelector(g, filp, s)) {
346 					strcpy(g->Message, "Failed making selector");
347 					return true;
348 				}	// endif Selector
349 
350 				tdbp->SetFilter(NULL);     // Not needed anymore
351 			} // endif To_Filter
352 
353 			if (trace(33))
354 				htrc("selector: %s\n", s->GetStr());
355 
356 			s->Resize(s->GetLength() + 1);
357 			sf = PlugDup(g, s->GetStr());
358 		} // endif Filter
359 
360 		if (!all) {
361 			if (Options && *Options) {
362 				if (trace(1))
363 					htrc("options=%s\n", Options);
364 
365 				op = Options;
366 			} else if (tdbp->GetColumns()) {
367 				// Projection list
368 				if (s)
369 					s->Set("{\"");
370 				else
371 					s = new(g) STRING(g, 511, "{\"");
372 
373 				if (!id)
374 					s->Append("_id\":0,\"");
375 
376 				for (PCOL cp = tdbp->GetColumns(); cp; cp = cp->GetNext()) {
377 					if (b)
378 						s->Append(",\"");
379 					else
380 						b = true;
381 
382 					if ((jp = cp->GetJpath(g, true)))
383 						s->Append(jp);
384 					else {
385 						// Can this happen?
386 						htrc("Fail getting projection path of %s\n", cp->GetName());
387 						goto nope;
388 					}	// endif Jpath
389 
390 					s->Append("\":1");
391 				} // endfor cp
392 
393 				s->Append("}");
394 				s->Resize(s->GetLength() + 1);
395 				op = s->GetStr();
396 			} else {
397 				// count(*)	?
398 				op = "{\"_id\":1}";
399 			} // endif Options
400 
401 		} // endif all
402 
403 	nope:
404 		return FindCollection(sf, op);
405 	} // endif Pipe
406 
407 } // end of MakeCursor
408 
409 /***********************************************************************/
410 /*  Find a collection and make cursor.                                 */
411 /***********************************************************************/
FindCollection(PCSZ query,PCSZ proj)412 bool JMgoConn::FindCollection(PCSZ query, PCSZ proj)
413 {
414 	bool		 rc = true;
415 	jboolean brc;
416 	jstring  qry = nullptr, prj = nullptr;
417 	PGLOBAL& g = m_G;
418 
419 	// Get the methods used to execute a query and get the result
420 	if (!gmID(g, fcollid, "FindColl",	"(Ljava/lang/String;Ljava/lang/String;)Z")) {
421 		if (query)
422 			qry = env->NewStringUTF(query);
423 
424 		if (proj)
425 			prj = env->NewStringUTF(proj);
426 
427 		brc = env->CallBooleanMethod(job, fcollid, qry, prj);
428 
429 		if (!Check(brc ? -1 : 0)) {
430 			rc = false;
431 		} else
432 			sprintf(g->Message, "FindColl: %s", Msg);
433 
434 		if (query)
435 			env->DeleteLocalRef(qry);
436 
437 		if (proj)
438 			env->DeleteLocalRef(prj);
439 
440 	} // endif xqid
441 
442 	return rc;
443 } // end of FindCollection
444 
445 /***********************************************************************/
446 /*  Find a collection and make cursor.                                 */
447 /***********************************************************************/
AggregateCollection(PCSZ pipeline)448 bool JMgoConn::AggregateCollection(PCSZ pipeline)
449 {
450 	bool		 rc = true;
451 	jboolean brc;
452 	jstring  pip = nullptr;
453 	PGLOBAL& g = m_G;
454 
455 	// Get the methods used to execute a query and get the result
456 	if (!gmID(g, acollid, "AggregateColl", "(Ljava/lang/String;)Z")) {
457 		pip = env->NewStringUTF(pipeline);
458 
459 		brc = env->CallBooleanMethod(job, acollid, pip);
460 
461 		if (!Check(brc ? -1 : 0)) {
462 			rc = false;
463 		} else
464 			sprintf(g->Message, "AggregateColl: %s", Msg);
465 
466 		env->DeleteLocalRef(pip);
467 	} // endif acollid
468 
469 	return rc;
470 } // end of AggregateCollection
471 
472 /***********************************************************************/
473 /*  Fetch next row.                                                    */
474 /***********************************************************************/
Fetch(int pos)475 int JMgoConn::Fetch(int pos)
476 {
477 	jint     rc = JNI_ERR;
478 	PGLOBAL& g = m_G;
479 
480 	//if (m_Full)						// Result set has one row
481 	//	return 1;
482 
483 	//if (pos) {
484 	//	if (!m_Scrollable) {
485 	//		strcpy(g->Message, "Cannot fetch(pos) if FORWARD ONLY");
486 	//		return rc;
487 	//	} else if (gmID(m_G, fetchid, "Fetch", "(I)Z"))
488 	//		return rc;
489 
490 	//	if (env->CallBooleanMethod(job, fetchid, pos))
491 	//		rc = m_Rows;
492 
493 	//} else {
494 		if (gmID(g, readid, "ReadNext", "()I"))
495 			return (int)rc;
496 
497 		rc = env->CallIntMethod(job, readid);
498 
499 		if (!Check(rc)) {
500 			//if (rc == 0)
501 			//	m_Full = (m_Fetch == 1);
502 			//else
503 			//	m_Fetch++;
504 
505 			m_Ncol = (int)rc;
506 			rc = MY_MIN(rc, 1);
507 			m_Rows += rc;
508 		} else
509 			sprintf(g->Message, "Fetch: %s", Msg);
510 
511 	//} // endif pos
512 
513 	return rc;
514 } // end of Fetch
515 
516 /***********************************************************************/
517 /*  Get the Json string of the current document.                       */
518 /***********************************************************************/
GetDocument(void)519 PSZ JMgoConn::GetDocument(void)
520 {
521 	PGLOBAL& g = m_G;
522 	PSZ      doc = NULL;
523 	jstring  jdc;
524 
525 	if (!gmID(g, getdocid, "GetDoc", "()Ljava/lang/String;")) {
526 		jdc = (jstring)env->CallObjectMethod(job, getdocid);
527 
528 		if (jdc)
529 			doc = (PSZ)GetUTFString(jdc);
530 
531 	} // endif getdocid
532 
533 	return doc;
534 	} // end of GetDocument
535 
536 /***********************************************************************/
537 /*  Group columns for inserting or updating.                           */
538 /***********************************************************************/
MakeColumnGroups(PGLOBAL g,PTDB tdbp)539 void JMgoConn::MakeColumnGroups(PGLOBAL g, PTDB tdbp)
540 {
541 	Fpc = new(g) JNCOL();
542 
543 	for (PCOL colp = tdbp->GetColumns(); colp; colp = colp->GetNext())
544 		if (!colp->IsSpecial())
545 			Fpc->AddCol(g, colp, colp->GetJpath(g, false));
546 
547 } // end of MakeColumnGroups
548 
549 /***********************************************************************/
550 /*  Get additional method ID.                                          */
551 /***********************************************************************/
GetMethodId(PGLOBAL g,MODE mode)552 bool JMgoConn::GetMethodId(PGLOBAL g, MODE mode)
553 {
554 	if (mode == MODE_UPDATE) {
555 		if (gmID(g, mkdocid, "MakeDocument", "()Ljava/lang/Object;"))
556 			return true;
557 
558 		if (gmID(g, docaddid, "DocAdd",
559 			"(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;I)Z"))
560 			return true;
561 
562 		if (gmID(g, updateid, "CollUpdate", "(Ljava/lang/Object;)J"))
563 			return true;
564 
565 	} else if (mode == MODE_INSERT) {
566 		if (gmID(g, mkdocid, "MakeDocument", "()Ljava/lang/Object;"))
567 			return true;
568 
569 		if (gmID(g, mkbsonid, "MakeBson",
570 			"(Ljava/lang/String;I)Ljava/lang/Object;"))
571 			return true;
572 
573 		if (gmID(g, docaddid, "DocAdd",
574 			"(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;I)Z"))
575 			return true;
576 
577 		if (gmID(g, mkarid, "MakeArray", "()Ljava/lang/Object;"))
578 			return true;
579 
580 		if (gmID(g, araddid, "ArrayAdd",
581 			"(Ljava/lang/Object;ILjava/lang/Object;I)Z"))
582 			return true;
583 
584 		if (gmID(g, insertid, "CollInsert", "(Ljava/lang/Object;)Z"))
585 			return true;
586 
587 	} else if (mode == MODE_DELETE)
588 		if (gmID(g, deleteid, "CollDelete", "(Z)J"))
589 			return true;
590 
591 	return gmID(g, rewindid, "Rewind", "()Z");
592 } // end of GetMethodId
593 
594 /***********************************************************************/
595 /*  MakeObject.                                                        */
596 /***********************************************************************/
MakeObject(PGLOBAL g,PCOL colp,bool & error)597 jobject JMgoConn::MakeObject(PGLOBAL g, PCOL colp, bool&error )
598 {
599 	jclass    cls;
600 	jmethodID cns = nullptr;								// Constructor
601 	jobject   val = nullptr;
602 	PVAL      valp = colp->GetValue();
603 
604 	error = false;
605 
606 	if (valp->IsNull())
607 		return NULL;
608 
609 	try {
610 		switch (valp->GetType()) {
611 			case TYPE_STRING:
612 				val = env->NewStringUTF(valp->GetCharValue());
613 				break;
614 			case TYPE_INT:
615 			case TYPE_SHORT:
616 				cls = env->FindClass("java/lang/Integer");
617 				cns = env->GetMethodID(cls, "<init>", "(I)V");
618 				val = env->NewObject(cls, cns, valp->GetIntValue());
619 				break;
620 			case TYPE_TINY:
621 				cls = env->FindClass("java/lang/Boolean");
622 				cns = env->GetMethodID(cls, "<init>", "(Z)V");
623 				val = env->NewObject(cls, cns, (valp->GetIntValue() != 0));
624 				break;
625 			case TYPE_BIGINT:
626 				cls = env->FindClass("java/lang/Long");
627 				cns = env->GetMethodID(cls, "<init>", "(J)V");
628 				val = env->NewObject(cls, cns, valp->GetBigintValue());
629 				break;
630 			case TYPE_DOUBLE:
631 				cls = env->FindClass("java/lang/Double");
632 				cns = env->GetMethodID(cls, "<init>", "(D)V");
633 				val = env->NewObject(cls, cns, valp->GetFloatValue());
634 				break;
635 			default:
636 				sprintf(g->Message, "Cannot make object from %d type", valp->GetType());
637 				error = true;
638 				break;
639 		}	// endswitch Type
640 
641 	} catch (...) {
642 		sprintf(g->Message, "Cannot make object from %s value", colp->GetName());
643 		error = true;
644 	}	// end try/catch
645 
646 	return val;
647 }	// end of MakeObject
648 
649 /***********************************************************************/
650 /*  Stringify.                                                            */
651 /***********************************************************************/
Stringify(PCOL colp)652 bool JMgoConn::Stringify(PCOL colp)
653 {
654 	bool b = false;
655 
656 	if (colp)
657 		b = (colp->Stringify() && colp->GetResultType() == TYPE_STRING);
658 
659 	return b;
660 }	// end of Stringify
661 
662 /***********************************************************************/
663 /*  MakeDoc.                                                           */
664 /***********************************************************************/
MakeDoc(PGLOBAL g,PJNCOL jcp)665 jobject JMgoConn::MakeDoc(PGLOBAL g, PJNCOL jcp)
666 {
667 	int     j;
668 	bool    b, error = false;
669 	jobject parent, child, val;
670 	jstring jkey;
671 	PJKC    kp = jcp->Klist;
672 
673 	if (kp->Array)
674 		parent = env->CallObjectMethod(job, mkarid);
675 	else
676 		parent = env->CallObjectMethod(job, mkdocid);
677 
678 	for (j = 0; kp; j = 0, kp = kp->Next) {
679 		if (Stringify(kp->Colp)) {
680 			switch (*kp->Colp->GetCharValue()) {
681 				case '{': j = 1; break;
682 				case '[': j = 2; break;
683 				default: break;
684 			} // endswitch
685 
686 			b = (!kp->Key || !*kp->Key || *kp->Key == '*');
687 		} else
688 			b = false;
689 
690 		if (kp->Jncolp) {
691 			if (!(child = MakeDoc(g, kp->Jncolp)))
692 				return NULL;
693 
694 			if (!kp->Array) {
695 				jkey = env->NewStringUTF(kp->Key);
696 
697 				if (env->CallBooleanMethod(job, docaddid, parent, jkey, child, j))
698 					return NULL;
699 
700 				env->DeleteLocalRef(jkey);
701 			} else
702 				if (env->CallBooleanMethod(job, araddid, parent, kp->N, child, j))
703 					return NULL;
704 
705 			env->DeleteLocalRef(child);
706 		} else {
707 			if (!(val = MakeObject(g, kp->Colp, error))) {
708 				if (error)
709 					return NULL;
710 
711 			} else if (!kp->Array) {
712 				if (!b) {
713 					jkey = env->NewStringUTF(kp->Key);
714 
715 					if (env->CallBooleanMethod(job, docaddid, parent, jkey, val, j))
716 						return NULL;
717 
718 					env->DeleteLocalRef(jkey);
719 				}	else {
720 					env->DeleteLocalRef(parent);
721 					parent = env->CallObjectMethod(job, mkbsonid, val, j);
722 				}	// endif b
723 
724 			} else if (env->CallBooleanMethod(job, araddid, parent, kp->N, val, j)) {
725 				if (Check(-1))
726 					sprintf(g->Message, "ArrayAdd: %s", Msg);
727 				else
728 					sprintf(g->Message, "ArrayAdd: unknown error");
729 
730 				return NULL;
731 			}	// endif ArrayAdd
732 
733 			env->DeleteLocalRef(val);
734 		} // endif Jncolp
735 
736 	} // endfor kp
737 
738 	return parent;
739 } // end of MakeDoc
740 
741 /***********************************************************************/
742 /*  Insert a new document in the collation.                            */
743 /***********************************************************************/
DocWrite(PGLOBAL g,PCSZ line)744 int JMgoConn::DocWrite(PGLOBAL g, PCSZ line)
745 {
746 	int     rc = RC_OK;
747 	jobject doc = nullptr;
748 
749 	if (line) {
750 		int     j;
751 		jobject val = env->NewStringUTF(line);
752 
753 		switch (*line) {
754 			case '{': j = 1; break;
755 			case '[': j = 2; break;
756 			default:  j = 0; break;
757 		} // endswitch line
758 
759 		doc =	env->CallObjectMethod(job, mkbsonid, val, j);
760 		env->DeleteLocalRef(val);
761 	} else if (Fpc)
762 		doc = MakeDoc(g, Fpc);
763 
764 	if (!doc)
765 		return RC_FX;
766 
767 	if (env->CallBooleanMethod(job, insertid, doc)) {
768 		if (Check(-1))
769 			sprintf(g->Message, "CollInsert: %s", Msg);
770 		else
771 			sprintf(g->Message, "CollInsert: unknown error");
772 
773 		rc = RC_FX;
774 	} // endif Insert
775 
776 	env->DeleteLocalRef(doc);
777 	return rc;
778 } // end of DocWrite
779 
780 /***********************************************************************/
781 /*  Update the current document from the collection.                   */
782 /***********************************************************************/
DocUpdate(PGLOBAL g,PTDB tdbp)783 int JMgoConn::DocUpdate(PGLOBAL g, PTDB tdbp)
784 {
785 	int     j = 0, rc = RC_OK;
786 	bool    error;
787 	PCOL    colp;
788 	jstring jkey;
789 	jobject val, upd, updlist = env->CallObjectMethod(job, mkdocid);
790 
791 	// Make the list of changes to do
792 	for (colp = tdbp->GetSetCols(); colp; colp = colp->GetNext()) {
793 		jkey = env->NewStringUTF(colp->GetJpath(g, false));
794 		val = MakeObject(g, colp, error);
795 
796 		if (error)
797 			return RC_FX;
798 		else if (Stringify(colp))
799 			switch (*colp->GetCharValue()) {
800 				case '{': j = 1; break;
801 				case '[': j = 2; break;
802 				default: break;
803 			} // endswitch
804 
805 		if (env->CallBooleanMethod(job, docaddid, updlist, jkey, val, j))
806 			return RC_OK;
807 
808 		env->DeleteLocalRef(jkey);
809 	}	// endfor colp
810 
811 	// Make the update parameter
812 	upd = env->CallObjectMethod(job, mkdocid);
813 	jkey = env->NewStringUTF("$set");
814 
815 	if (env->CallBooleanMethod(job, docaddid, upd, jkey, updlist, 0))
816 		return RC_OK;
817 
818 	env->DeleteLocalRef(jkey);
819 
820 	jlong ar = env->CallLongMethod(job, updateid, upd);
821 
822 	if (trace(1))
823 		htrc("DocUpdate: ar = %ld\n", ar);
824 
825 	if (Check((int)ar)) {
826 		sprintf(g->Message, "CollUpdate: %s", Msg);
827 		rc = RC_FX;
828 	} // endif ar
829 
830 	return rc;
831 } // end of DocUpdate
832 
833 /***********************************************************************/
834 /*  Remove all or only the current document from the collection.       */
835 /***********************************************************************/
DocDelete(PGLOBAL g,bool all)836 int JMgoConn::DocDelete(PGLOBAL g, bool all)
837 {
838 	int   rc = RC_OK;
839 	jlong ar = env->CallLongMethod(job, deleteid, all);
840 
841 	if (trace(1))
842 		htrc("DocDelete: ar = %ld\n", ar);
843 
844 	if (Check((int)ar)) {
845 		sprintf(g->Message, "CollDelete: %s", Msg);
846 		rc = RC_FX;
847 	} // endif ar
848 
849 	return rc;
850 } // end of DocDelete
851 
852 /***********************************************************************/
853 /*  Rewind the collection.                                             */
854 /***********************************************************************/
Rewind(void)855 bool JMgoConn::Rewind(void)
856 {
857 	return env->CallBooleanMethod(job, rewindid);
858 } // end of Rewind
859 
860 /***********************************************************************/
861 /*  Retrieve the column string value from the document.                */
862 /***********************************************************************/
GetColumnValue(PSZ path)863 PSZ JMgoConn::GetColumnValue(PSZ path)
864 {
865 	PGLOBAL& g = m_G;
866 	PSZ      fld = NULL;
867 	jstring  fn, jn = nullptr;
868 
869 	if (!path || (jn = env->NewStringUTF(path)) == nullptr) {
870 		sprintf(g->Message, "Fail to allocate jstring %s", SVP(path));
871 		throw (int)TYPE_AM_MGO;
872 	}	// endif name
873 
874 	if (!gmID(g, objfldid, "GetField", "(Ljava/lang/String;)Ljava/lang/String;")) {
875 		fn = (jstring)env->CallObjectMethod(job, objfldid, jn);
876 
877 		if (fn)
878 			fld = (PSZ)GetUTFString(fn);
879 
880 	}	// endif objfldid
881 
882 	return fld;
883 } // end of GetColumnValue
884