1 /************** tabjmg C++ Program Source Code File (.CPP) *************/
2 /* PROGRAM NAME: tabjmg     Version 1.3                                */
3 /*  (C) Copyright to the author Olivier BERTRAND          2021         */
4 /*  This file contains the MongoDB classes using the Java Driver.      */
5 /***********************************************************************/
6 
7 /***********************************************************************/
8 /*  Include relevant sections of the MariaDB header file.              */
9 /***********************************************************************/
10 #include <my_global.h>
11 
12 /***********************************************************************/
13 /*  Include application header files:                                  */
14 /*  global.h    is header containing all global declarations.          */
15 /*  plgdbsem.h  is header containing the DB application declarations.  */
16 /***********************************************************************/
17 #include "global.h"
18 #include "plgdbsem.h"
19 #include "xtable.h"
20 #include "maputil.h"
21 #include "filamtxt.h"
22 #include "tabext.h"
23 #include "tabjmg.h"
24 #include "tabmul.h"
25 #include "checklvl.h"
26 #include "resource.h"
27 #include "mycat.h"                             // for FNC_COL
28 #include "filter.h"
29 
30 #define nullptr 0
31 
32 PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt, bool info);
33 bool    Stringified(PCSZ, char*);
34 
35 /* -------------------------- Class JMGDISC -------------------------- */
36 
37 /***********************************************************************/
38 /*  Constructor																												 */
39 /***********************************************************************/
JMGDISC(PGLOBAL g,int * lg)40 JMGDISC::JMGDISC(PGLOBAL g, int *lg) : MGODISC(g, lg)
41 {
42 	drv = "Java"; Jcp = NULL; columnid = nullptr; bvnameid = nullptr;
43 }	// end of JMGDISC constructor
44 
45 /***********************************************************************/
46 /*  Initialyze.                                                        */
47 /***********************************************************************/
Init(PGLOBAL g)48 bool JMGDISC::Init(PGLOBAL g)
49 {
50 	if (!(Jcp = ((TDBJMG*)tmgp)->Jcp)) {
51 		strcpy(g->Message, "Init: Jcp is NULL");
52 		return true;
53 	}	else if (Jcp->gmID(g, columnid, "ColumnDesc",
54 		                  "(Ljava/lang/Object;I[II)Ljava/lang/Object;"))
55 		return true;
56 	else if (Jcp->gmID(g, bvnameid, "ColDescName", "()Ljava/lang/String;"))
57 	  return true;
58 
59 	return false;
60 }	// end of Init
61 
62 /***********************************************************************/
63 /*  Analyse passed document.                                           */
64 /***********************************************************************/
Find(PGLOBAL g)65 bool JMGDISC::Find(PGLOBAL g)
66 {
67 	return ColDesc(g, nullptr, NULL, NULL, Jcp->m_Ncol, 0);
68 }	// end of Find
69 
70 /***********************************************************************/
71 /*  Analyse passed document.                                           */
72 /***********************************************************************/
ColDesc(PGLOBAL g,jobject obj,char * pcn,char * pfmt,int ncol,int k)73 bool JMGDISC::ColDesc(PGLOBAL g, jobject obj, char *pcn, char *pfmt,
74 											int ncol, int k)
75 {
76 	const char *key, *utf;
77 	char    colname[65];
78 	char    fmt[129];
79 	bool    rc = true;
80 	size_t  z;
81 	jint   *n = nullptr;
82 	jstring jkey;
83 	jobject jres;
84 
85 	// Build the java int array
86 	jintArray val = Jcp->env->NewIntArray(5);
87 
88 	if (val == nullptr) {
89 		strcpy(g->Message, "Cannot allocate jint array");
90 		return true;
91 	} else if (!ncol)
92 		n = Jcp->env->GetIntArrayElements(val, 0);
93 
94 	for (int i = 0; i < ncol; i++) {
95 		jres = Jcp->env->CallObjectMethod(Jcp->job, columnid, obj, i, val, lvl - k);
96 		n = Jcp->env->GetIntArrayElements(val, 0);
97 
98 		if (Jcp->Check(n[0])) {
99 			sprintf(g->Message, "ColDesc: %s", Jcp->Msg);
100 			goto err;
101 		} else if (!n[0])
102 			continue;
103 
104 		jkey = (jstring)Jcp->env->CallObjectMethod(Jcp->job, bvnameid);
105 		utf = Jcp->env->GetStringUTFChars(jkey, nullptr);
106 		key = PlugDup(g, utf);
107 		Jcp->env->ReleaseStringUTFChars(jkey, utf);
108 		Jcp->env->DeleteLocalRef(jkey);
109 
110 		if (pcn) {
111 			strncpy(colname, pcn, 64);
112 			colname[64] = 0;
113 			z = 65 - strlen(colname);
114 			strncat(strncat(colname, "_", z), key, z - 1);
115 		} else
116 			strcpy(colname, key);
117 
118 		if (pfmt) {
119 			strncpy(fmt, pfmt, 128);
120 			fmt[128] = 0;
121 			z = 129 - strlen(fmt);
122 			strncat(strncat(fmt, ".", z), key, z - 1);
123 		} else
124 			strcpy(fmt, key);
125 
126 		if (!jres) {
127 			bcol.Type = n[0];
128 			bcol.Len = n[1];
129 			bcol.Scale = n[2];
130 			bcol.Cbn = n[3];
131 			AddColumn(g, colname, fmt, k);
132 		} else {
133 			if (n[0] == 2 && !all)
134 				n[4] = MY_MIN(n[4], 1);
135 
136 			if (ColDesc(g, jres, colname, fmt, n[4], k + 1))
137 				goto err;
138 
139 		}	// endif jres
140 
141 	} // endfor i
142 
143 	rc = false;
144 
145  err:
146 	Jcp->env->ReleaseIntArrayElements(val, n, 0);
147 	return rc;
148 }	// end of ColDesc
149 
150 /* --------------------------- Class TDBJMG -------------------------- */
151 
152 /***********************************************************************/
153 /*  Implementation of the TDBJMG class.                                */
154 /***********************************************************************/
TDBJMG(PMGODEF tdp)155 TDBJMG::TDBJMG(PMGODEF tdp) : TDBEXT(tdp)
156 {
157 	Jcp = NULL;
158 //Cnp = NULL;
159 
160 	if (tdp) {
161 		Ops.Driver = tdp->Tabschema;
162 		Ops.Url = tdp->Uri;
163 		Ops.Version = tdp->Version;
164 		Uri = tdp->Uri;
165 		Db_name = tdp->Tabschema;
166 		Wrapname = tdp->Wrapname;
167 		Coll_name = tdp->Tabname;
168 		Options = tdp->Colist;
169 		Filter = tdp->Filter;
170 		Strfy = tdp->Strfy;
171 		B = tdp->Base ? 1 : 0;
172 		Pipe = tdp->Pipe && Options != NULL;
173 	} else {
174 		Ops.Driver = NULL;
175 		Ops.Url = NULL;
176 		Ops.Version = 0;
177 		Uri = NULL;
178 		Db_name = NULL;
179 		Coll_name = NULL;
180 		Options = NULL;
181 		Filter = NULL;
182 		Strfy = NULL;
183 		B = 0;
184 		Pipe = false;
185 	} // endif tdp
186 
187 	Ops.User = NULL;
188 	Ops.Pwd = NULL;
189 	Ops.Scrollable = false;
190 	Ops.Fsize = 0;
191 	Fpos = -1;
192 	N = 0;
193 	Done = false;
194 } // end of TDBJMG standard constructor
195 
TDBJMG(TDBJMG * tdbp)196 TDBJMG::TDBJMG(TDBJMG *tdbp) : TDBEXT(tdbp)
197 {
198 	Uri = tdbp->Uri;
199 	Db_name = tdbp->Db_name;;
200 	Coll_name = tdbp->Coll_name;
201 	Options = tdbp->Options;
202 	Filter = tdbp->Filter;
203 	Strfy = tdbp->Strfy;
204 	B = tdbp->B;
205 	Fpos = tdbp->Fpos;
206 	N = tdbp->N;
207 	Done = tdbp->Done;
208 	Pipe = tdbp->Pipe;
209 } // end of TDBJMG copy constructor
210 
211 // Used for update
Clone(PTABS t)212 PTDB TDBJMG::Clone(PTABS t)
213 {
214 	PTDB    tp;
215 	PJMGCOL cp1, cp2;
216 	PGLOBAL g = t->G;
217 
218 	tp = new(g) TDBJMG(this);
219 
220 	for (cp1 = (PJMGCOL)Columns; cp1; cp1 = (PJMGCOL)cp1->GetNext())
221 		if (!cp1->IsSpecial()) {
222 			cp2 = new(g) JMGCOL(cp1, tp);  // Make a copy
223 			NewPointer(t, cp1, cp2);
224 		} // endif cp1
225 
226 	return tp;
227 } // end of Clone
228 
229 /***********************************************************************/
230 /*  Allocate JSN column description block.                             */
231 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)232 PCOL TDBJMG::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
233 {
234 	return new(g) JMGCOL(g, cdp, this, cprec, n);
235 } // end of MakeCol
236 
237 /***********************************************************************/
238 /*  InsertSpecialColumn: Put a special column ahead of the column list.*/
239 /***********************************************************************/
InsertSpecialColumn(PCOL colp)240 PCOL TDBJMG::InsertSpecialColumn(PCOL colp)
241 {
242 	if (!colp->IsSpecial())
243 		return NULL;
244 
245 	colp->SetNext(Columns);
246 	Columns = colp;
247 	return colp;
248 } // end of InsertSpecialColumn
249 
250 /***********************************************************************/
251 /*  MONGO Cardinality: returns table size in number of rows.           */
252 /***********************************************************************/
Cardinality(PGLOBAL g)253 int TDBJMG::Cardinality(PGLOBAL g)
254 {
255 	if (!g)
256 		return 1;
257 	else if (Cardinal < 0)
258 		Cardinal = (!Init(g)) ? Jcp->CollSize(g) : 0;
259 
260 	return Cardinal;
261 } // end of Cardinality
262 
263 /***********************************************************************/
264 /*  MONGO GetMaxSize: returns collection size estimate.                */
265 /***********************************************************************/
GetMaxSize(PGLOBAL g)266 int TDBJMG::GetMaxSize(PGLOBAL g)
267 {
268 	if (MaxSize < 0)
269 		MaxSize = Cardinality(g);
270 
271 	return MaxSize;
272 } // end of GetMaxSize
273 
274 /***********************************************************************/
275 /*  Init: initialize MongoDB processing.                               */
276 /***********************************************************************/
Init(PGLOBAL g)277 bool TDBJMG::Init(PGLOBAL g)
278 {
279 	if (Done)
280 		return false;
281 
282 	/*********************************************************************/
283 	/*  Open an JDBC connection for this table.                          */
284 	/*  Note: this may not be the proper way to do. Perhaps it is better */
285 	/*  to test whether a connection is already open for this datasource */
286 	/*  and if so to allocate just a new result set. But this only for   */
287 	/*  drivers allowing concurency in getting results ???               */
288 	/*********************************************************************/
289 	if (!Jcp)
290 		Jcp = new(g) JMgoConn(g, Coll_name, Wrapname);
291 	else if (Jcp->IsOpen())
292 		Jcp->Close();
293 
294 	if (Jcp->Connect(&Ops))
295 		return true;
296 
297 	Done = true;
298 	return false;
299 } // end of Init
300 
301 /***********************************************************************/
302 /*  OpenDB: Data Base open routine for MONGO access method.            */
303 /***********************************************************************/
OpenDB(PGLOBAL g)304 bool TDBJMG::OpenDB(PGLOBAL g)
305 {
306 	if (Use == USE_OPEN) {
307 		/*******************************************************************/
308 		/*  Table already open replace it at its beginning.                */
309 		/*******************************************************************/
310 		if (Jcp->Rewind())
311 			return true;
312 
313 		Fpos = -1;
314 		return false;
315 	} // endif Use
316 
317 	/*********************************************************************/
318 	/*  First opening.                                                   */
319 	/*********************************************************************/
320 	if (Pipe && Mode != MODE_READ) {
321 		strcpy(g->Message, "Pipeline tables are read only");
322 		return true;
323 	}	// endif Pipe
324 
325 	Use = USE_OPEN;       // Do it now in case we are recursively called
326 
327 	if (Init(g))
328 		return true;
329 
330 	if (Jcp->GetMethodId(g, Mode))
331 		return true;
332 
333 	if (Mode == MODE_DELETE && !Next) {
334 	// Delete all documents
335 		if (!Jcp->MakeCursor(g, this, "all", Filter, false))
336 			if (Jcp->DocDelete(g, true) == RC_OK)
337 				return false;
338 
339 		return true;
340 	}	// endif Mode
341 
342 	if (Mode == MODE_INSERT)
343 		Jcp->MakeColumnGroups(g, this);
344 
345 	if (Mode != MODE_UPDATE)
346 		return Jcp->MakeCursor(g, this, Options, Filter, Pipe);
347 
348 	return false;
349 } // end of OpenDB
350 
351 /***********************************************************************/
352 /*  Data Base indexed read routine for ODBC access method.             */
353 /***********************************************************************/
ReadKey(PGLOBAL g,OPVAL op,const key_range * kr)354 bool TDBJMG::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
355 {
356 	strcpy(g->Message, "MONGO tables are not indexable");
357 	return true;
358 } // end of ReadKey
359 
360 /***********************************************************************/
361 /*  ReadDB: Get next document from a collection.                       */
362 /***********************************************************************/
ReadDB(PGLOBAL g)363 int TDBJMG::ReadDB(PGLOBAL g)
364 {
365 	int rc = RC_OK;
366 
367 	if (!N && Mode == MODE_UPDATE)
368 		if (Jcp->MakeCursor(g, this, Options, Filter, Pipe))
369 			return RC_FX;
370 
371 	if (++CurNum >= Rbuf) {
372 		Rbuf = Jcp->Fetch();
373 		Curpos = Fpos + 1;
374 		CurNum = 0;
375 		N++;
376 	} // endif CurNum
377 
378 	rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
379 
380 	return rc;
381 } // end of ReadDB
382 
383 /***********************************************************************/
384 /*  WriteDB: Data Base write routine for DOS access method.            */
385 /***********************************************************************/
WriteDB(PGLOBAL g)386 int TDBJMG::WriteDB(PGLOBAL g)
387 {
388 	int rc = RC_OK;
389 
390 	if (Mode == MODE_INSERT) {
391 		rc = Jcp->DocWrite(g, NULL);
392 	} else if (Mode == MODE_DELETE) {
393 		rc = Jcp->DocDelete(g, false);
394 	} else if (Mode == MODE_UPDATE) {
395 		rc = Jcp->DocUpdate(g, this);
396 	}	// endif Mode
397 
398 	return rc;
399 } // end of WriteDB
400 
401 /***********************************************************************/
402 /*  Data Base delete line routine for ODBC access method.              */
403 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)404 int TDBJMG::DeleteDB(PGLOBAL g, int irc)
405 {
406 	return (irc == RC_OK) ? WriteDB(g) : RC_OK;
407 } // end of DeleteDB
408 
409 /***********************************************************************/
410 /*  Table close routine for MONGO tables.                              */
411 /***********************************************************************/
CloseDB(PGLOBAL g)412 void TDBJMG::CloseDB(PGLOBAL g)
413 {
414 	Jcp->Close();
415 	Done = false;
416 } // end of CloseDB
417 
418 /* ----------------------------- JMGCOL ------------------------------ */
419 
420 /***********************************************************************/
421 /*  JMGCOL public constructor.                                         */
422 /***********************************************************************/
JMGCOL(PGLOBAL g,PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i)423 JMGCOL::JMGCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
424 	: EXTCOL(cdp, tdbp, cprec, i, "MGO")
425 {
426 	Tmgp = (PTDBJMG)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
427 	Sgfy = Stringified(Tmgp->Strfy, Name);
428 
429 	if ((Jpath = cdp->GetFmt())) {
430 		int n = strlen(Jpath);
431 
432 		if (n && Jpath[n - 1] == '*') {
433 			Jpath = PlugDup(g, cdp->GetFmt());
434 
435 			if (--n) {
436 				if (Jpath[n - 1] == '.') n--;
437 				Jpath[n] = 0;
438 			}	// endif n
439 
440 			Sgfy = true;
441 		}	// endif Jpath
442 
443 	}	else
444 		Jpath = cdp->GetName();
445 
446 } // end of JMGCOL constructor
447 
448 /***********************************************************************/
449 /*  JMGCOL constructor used for copying columns.                       */
450 /*  tdbp is the pointer to the new table descriptor.                   */
451 /***********************************************************************/
JMGCOL(JMGCOL * col1,PTDB tdbp)452 JMGCOL::JMGCOL(JMGCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
453 {
454 	Tmgp = col1->Tmgp;
455 	Jpath = col1->Jpath;
456 	Sgfy = col1->Sgfy;
457 } // end of JMGCOL copy constructor
458 
459 /***********************************************************************/
460 /*  Get path when proj is false or projection path when proj is true.  */
461 /***********************************************************************/
GetJpath(PGLOBAL g,bool proj)462 PSZ JMGCOL::GetJpath(PGLOBAL g, bool proj)
463 {
464 	if (Jpath) {
465 		if (proj) {
466 			char* p1, * p2, * projpath = PlugDup(g, Jpath);
467 			int   i = 0;
468 
469 			for (p1 = p2 = projpath; *p1; p1++)
470 				if (*p1 == '.') {
471 					if (!i)
472 						*p2++ = *p1;
473 
474 					i = 1;
475 				} else if (i) {
476 					if (!isdigit(*p1)) {
477 						*p2++ = *p1;
478 						i = 0;
479 					} // endif p1
480 
481 				} else
482 					*p2++ = *p1;
483 
484 			if (*(p2 - 1) == '.')
485 				p2--;
486 
487 			*p2 = 0;
488 			return projpath;
489 		} else
490 			return Jpath;
491 
492 	} else
493 		return Name;
494 
495 } // end of GetJpath
496 
497 #if 0
498 /***********************************************************************/
499 /*  Mini: used to suppress blanks to json strings.                     */
500 /***********************************************************************/
501 char *JMGCOL::Mini(PGLOBAL g, const bson_t *bson, bool b)
502 {
503 	char *s, *str = NULL;
504 	int   i, k = 0;
505 	bool  ok = true;
506 
507 	if (b)
508 		s = str = bson_array_as_json(bson, NULL);
509 	else
510 		s = str = bson_as_json(bson, NULL);
511 
512 	for (i = 0; i < Long && s[i]; i++) {
513 		switch (s[i]) {
514 			case ' ':
515 				if (ok) continue;
516 				break;
517 			case '"':
518 				ok = !ok;
519 			default:
520 				break;
521 		} // endswitch s[i]
522 
523 		Mbuf[k++] = s[i];
524 	} // endfor i
525 
526 	bson_free(str);
527 
528 	if (i >= Long) {
529 		sprintf(g->Message, "Value too long for column %s", Name);
530 		throw (int)TYPE_AM_MGO;
531 	}	// endif i
532 
533 	Mbuf[k] = 0;
534 	return Mbuf;
535 } // end of Mini
536 #endif // 0
537 
538 /***********************************************************************/
539 /*  ReadColumn:                                                        */
540 /***********************************************************************/
ReadColumn(PGLOBAL g)541 void JMGCOL::ReadColumn(PGLOBAL g)
542 {
543 	Value->SetValue_psz(Tmgp->Jcp->GetColumnValue(Jpath));
544 } // end of ReadColumn
545 
546 /***********************************************************************/
547 /*  WriteColumn:                                                       */
548 /***********************************************************************/
WriteColumn(PGLOBAL g)549 void JMGCOL::WriteColumn(PGLOBAL g)
550 {
551 	// Check whether this node must be written
552 	if (Value != To_Val)
553 		Value->SetValue_pval(To_Val, FALSE);    // Convert the updated value
554 
555 } // end of WriteColumn
556 
557 #if 0
558 /***********************************************************************/
559 /*  AddValue: Add column value to the document to insert or update.    */
560 /***********************************************************************/
561 bool JMGCOL::AddValue(PGLOBAL g, bson_t *doc, char *key, bool upd)
562 {
563 	bool rc = false;
564 
565 	if (Value->IsNull()) {
566 		if (upd)
567 			rc = BSON_APPEND_NULL(doc, key);
568 		else
569 			return false;
570 
571 	} else switch (Buf_Type) {
572 		case TYPE_STRING:
573 			rc = BSON_APPEND_UTF8(doc, key, Value->GetCharValue());
574 			break;
575 		case TYPE_INT:
576 		case TYPE_SHORT:
577 			rc = BSON_APPEND_INT32(doc, key, Value->GetIntValue());
578 			break;
579 		case TYPE_TINY:
580 			rc = BSON_APPEND_BOOL(doc, key, Value->GetIntValue());
581 			break;
582 		case TYPE_BIGINT:
583 			rc = BSON_APPEND_INT64(doc, key, Value->GetBigintValue());
584 			break;
585 		case TYPE_DOUBLE:
586 			rc = BSON_APPEND_DOUBLE(doc, key, Value->GetFloatValue());
587 			break;
588 		case TYPE_DECIM:
589 		{bson_decimal128_t dec;
590 
591 		if (bson_decimal128_from_string(Value->GetCharValue(), &dec))
592 			rc = BSON_APPEND_DECIMAL128(doc, key, &dec);
593 
594 		} break;
595 		case TYPE_DATE:
596 			rc = BSON_APPEND_DATE_TIME(doc, key, Value->GetBigintValue() * 1000);
597 			break;
598 		default:
599 			sprintf(g->Message, "Type %d not supported yet", Buf_Type);
600 			return true;
601 	} // endswitch Buf_Type
602 
603 	if (!rc) {
604 		strcpy(g->Message, "Adding value failed");
605 		return true;
606 	} else
607 		return false;
608 
609 } // end of AddValue
610 #endif // 0
611 
612 /* -------------------------- TDBJGL class --------------------------- */
613 
614 /***********************************************************************/
615 /*  TDBJGL class constructor.                                          */
616 /***********************************************************************/
TDBJGL(PMGODEF tdp)617 TDBJGL::TDBJGL(PMGODEF tdp) : TDBCAT(tdp)
618 {
619 	Topt = tdp->GetTopt();
620 	Uri = tdp->Uri;
621 	Db = tdp->GetTabschema();
622 } // end of TDBJCL constructor
623 
624 /***********************************************************************/
625 /*  GetResult: Get the list the MongoDB collection columns.            */
626 /***********************************************************************/
GetResult(PGLOBAL g)627 PQRYRES TDBJGL::GetResult(PGLOBAL g)
628 {
629 	return MGOColumns(g, Db, Uri, Topt, false);
630 } // end of GetResult
631 
632 /* -------------------------- End of mongo --------------------------- */
633