1 /************** mongo C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: mongo     Version 1.1                                 */
3 /*  (C) Copyright to the author Olivier BERTRAND          2021         */
4 /*  These programs are the MGODEF class execution routines.            */
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 "tabext.h"
21 #include "filter.h"
22 #if defined(CMGO_SUPPORT)
23 #include "tabcmg.h"
24 #endif   // CMGO_SUPPORT
25 #if defined(JAVA_SUPPORT)
26 #include "tabjmg.h"
27 #endif   // JAVA_SUPPORT
28 #include "resource.h"
29 
30 /***********************************************************************/
31 /*  This should be an option.                                          */
32 /***********************************************************************/
33 #define MAXCOL          200        /* Default max column nb in result  */
34 #define TYPE_UNKNOWN     12        /* Must be greater than other types */
35 
36 bool MakeSelector(PGLOBAL g, PFIL fp, PSTRG s);
37 bool IsNum(PSZ s);
38 int  GetDefaultDepth(void);
39 bool JsonAllPath(void);
40 
41 /***********************************************************************/
42 /*  Make selector json representation for Mongo tables.                */
43 /***********************************************************************/
44 bool MakeSelector(PGLOBAL g, PFIL fp, PSTRG s)
45 {
46 	OPVAL opc = fp->GetOpc();
47 
48 	s->Append('{');
49 
50 	if (opc == OP_AND || opc == OP_OR) {
51 		if (fp->GetArgType(0) != TYPE_FILTER || fp->GetArgType(1) != TYPE_FILTER)
52 			return true;
53 
54 		s->Append("\"$");
55 		s->Append(opc == OP_AND ? "and" : "or");
56 		s->Append("\":[");
57 
58 		if (MakeSelector(g, (PFIL)fp->Arg(0), s))
59 			return true;
60 
61 		s->Append(',');
62 
63 		if (MakeSelector(g, (PFIL)fp->Arg(1), s))
64 			return true;
65 
66 		s->Append(']');
67 	} else {
68 		if (fp->GetArgType(0) != TYPE_COLBLK)
69 			return true;
70 
71 		s->Append('"');
72 		s->Append(((PCOL)fp->Arg(0))->GetJpath(g, false));
73 		s->Append("\":{\"$");
74 
75 		switch (opc) {
76 			case OP_EQ:
77 				s->Append("eq");
78 				break;
79 			case OP_NE:
80 				s->Append("ne");
81 				break;
82 			case OP_GT:
83 				s->Append("gt");
84 				break;
85 			case OP_GE:
86 				s->Append("gte");
87 				break;
88 			case OP_LT:
89 				s->Append("lt");
90 				break;
91 			case OP_LE:
92 				s->Append("lte");
93 				break;
94 			case OP_NULL:
95 			case OP_LIKE:
96 			case OP_EXIST:
97 			default:
98 				return true;
99 		} // endswitch Opc
100 
101 		s->Append("\":");
102 
103 		if (fp->GetArgType(1) == TYPE_COLBLK) {
104 			s->Append("\"$");
105 			s->Append(((PEXTCOL)fp->Arg(1))->GetJpath(g, false));
106 			s->Append('"');
107 		} else {
108 			char buf[501];
109 
110 			fp->Arg(1)->Prints(g, buf, 500);
111 			s->Append(buf);
112 		} // endif Type
113 
114 		s->Append('}');
115 	} // endif opc
116 
117 	s->Append('}');
118 	return false;
119 } // end of MakeSelector
120 
121 /***********************************************************************/
122 /*  MGOColumns: construct the result blocks containing the description */
123 /*  of all the columns of a document contained inside MongoDB.         */
124 /***********************************************************************/
125 PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt, bool info)
126 {
127 	static int  buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
128 													TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
129 	static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
130 													FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
131 	unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
132 	int      ncol = sizeof(buftyp) / sizeof(int);
133 	int      i, n = 0;
134 	PCSZ     drv;
135 	PBCOL    bcp;
136 	MGODISC *cmgd = NULL;
137 	PQRYRES  qrp;
138 	PCOLRES  crp;
139 
140 	if (info) {
141 		length[0] = 128;
142 		length[7] = 256;
143 		goto skipit;
144 	} // endif info
145 
146 	/*********************************************************************/
147 	/*  Open MongoDB.                                                    */
148 	/*********************************************************************/
149 	drv = GetStringTableOption(g, topt, "Driver", NULL);
150 
151 	if (drv && toupper(*drv) == 'C') {
152 #if defined(CMGO_SUPPORT)
153 		cmgd = new(g) CMGDISC(g, (int*)length);
154 #else
155 		sprintf(g->Message, "Mongo %s Driver not available", "C");
156 		goto err;
157 #endif
158 	} else if (drv && toupper(*drv) == 'J') {
159 #if defined(JAVA_SUPPORT)
160 		cmgd = new(g) JMGDISC(g, (int*)length);
161 #else
162 		sprintf(g->Message, "Mongo %s Driver not available", "Java");
163 		goto err;
164 #endif
165 	} else {						 // Driver not specified
166 #if defined(CMGO_SUPPORT)
167 		cmgd = new(g) CMGDISC(g, (int*)length);
168 #else
169 		cmgd = new(g) JMGDISC(g, (int*)length);
170 #endif
171 	}	// endif drv
172 
173 	if ((n = cmgd->GetColumns(g, db, uri, topt)) < 0)
174 		goto err;
175 
176 skipit:
177 	if (trace(1))
178 		htrc("MGOColumns: n=%d len=%d\n", n, length[0]);
179 
180 	/*********************************************************************/
181 	/*  Allocate the structures used to refer to the result set.         */
182 	/*********************************************************************/
183 	qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
184 		buftyp, fldtyp, length, false, false);
185 
186 	crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
187 	crp->Name = "Nullable";
188 	crp->Next->Name = "Bpath";
189 
190 	if (info || !qrp)
191 		return qrp;
192 
193 	qrp->Nblin = n;
194 
195 	/*********************************************************************/
196 	/*  Now get the results into blocks.                                 */
197 	/*********************************************************************/
198 	for (i = 0, bcp = cmgd->fbcp; bcp; i++, bcp = bcp->Next) {
199 		if (bcp->Type == TYPE_UNKNOWN)            // Void column
200 			bcp->Type = TYPE_STRING;
201 
202 		crp = qrp->Colresp;                    // Column Name
203 		crp->Kdata->SetValue(bcp->Name, i);
204 		crp = crp->Next;                       // Data Type
205 		crp->Kdata->SetValue(bcp->Type, i);
206 		crp = crp->Next;                       // Type Name
207 		crp->Kdata->SetValue(GetTypeName(bcp->Type), i);
208 		crp = crp->Next;                       // Precision
209 		crp->Kdata->SetValue(bcp->Len, i);
210 		crp = crp->Next;                       // Length
211 		crp->Kdata->SetValue(bcp->Len, i);
212 		crp = crp->Next;                       // Scale (precision)
213 		crp->Kdata->SetValue(bcp->Scale, i);
214 		crp = crp->Next;                       // Nullable
215 		crp->Kdata->SetValue(bcp->Cbn ? 1 : 0, i);
216 		crp = crp->Next;                       // Field format
217 
218 		if (crp->Kdata)
219 			crp->Kdata->SetValue(bcp->Fmt, i);
220 
221 	} // endfor i
222 
223 	/*********************************************************************/
224 	/*  Return the result pointer.                                       */
225 	/*********************************************************************/
226 	return qrp;
227 
228 err:
229 	if (cmgd && cmgd->tmgp)
230 		cmgd->tmgp->CloseDB(g);
231 
232 	return NULL;
233 } // end of MGOColumns
234 
235 /***********************************************************************/
236 /*  Class used to get the columns of a mongo collection.               */
237 /***********************************************************************/
238 MGODISC::MGODISC(PGLOBAL g, int *lg) {
239 	length = lg;
240 	fbcp = NULL;
241 	pbcp = NULL;
242 	tmgp = NULL;
243 	drv = NULL;
244 	i = ncol = lvl = 0;
245 	all = false;
246 }	// end of MGODISC constructor
247 
248 /***********************************************************************/
249 /*  Class used to get the columns of a mongo collection.               */
250 /***********************************************************************/
251 int MGODISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt)
252 {
253 	PMGODEF tdp;
254 
255 	lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
256 	lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
257 	all = GetBooleanTableOption(g, topt, "Fullarray", false);
258 
259 	/*********************************************************************/
260 	/*  Open the MongoDB collection.                                     */
261 	/*********************************************************************/
262 	tdp = new(g) MGODEF;
263 	tdp->Uri = (uri && *uri) ? uri : "mongodb://localhost:27017";
264 	tdp->Driver = drv;
265 	tdp->Tabname = GetStringTableOption(g, topt, "Name", NULL);
266 	tdp->Tabname = GetStringTableOption(g, topt, "Tabname", tdp->Tabname);
267 	tdp->Tabschema = GetStringTableOption(g, topt, "Dbname", db);
268 	tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0;
269 	tdp->Colist = GetStringTableOption(g, topt, "Colist", "all");
270 	tdp->Filter = GetStringTableOption(g, topt, "Filter", NULL);
271 	tdp->Pipe = GetBooleanTableOption(g, topt, "Pipeline", false);
272 	tdp->Version = GetIntegerTableOption(g, topt, "Version", 3);
273 	tdp->Wrapname = (PSZ)GetStringTableOption(g, topt, "Wrapper",
274 		(tdp->Version == 2) ? "Mongo2Interface" : "Mongo3Interface");
275 
276 	if (trace(1))
277 		htrc("Uri %s coll=%s db=%s colist=%s filter=%s lvl=%d\n",
278 			tdp->Uri, tdp->Tabname, tdp->Tabschema, tdp->Colist, tdp->Filter, lvl);
279 
280 	tmgp = tdp->GetTable(g, MODE_READ);
281 	tmgp->SetMode(MODE_READ);
282 
283 	if (tmgp->OpenDB(g))
284 		return -1;
285 
286 	bcol.Next = NULL;
287 	bcol.Name = bcol.Fmt = NULL;
288 	bcol.Type = TYPE_UNKNOWN;
289 	bcol.Len = bcol.Scale = 0;
290 	bcol.Found = true;
291 	bcol.Cbn = false;
292 
293 	if (Init(g))
294 		return -1;
295 
296 	/*********************************************************************/
297 	/*  Analyse the BSON tree and define columns.                        */
298 	/*********************************************************************/
299 	for (i = 1; ; i++) {
300 		switch (tmgp->ReadDB(g)) {
301 			case RC_EF:
302 				return ncol;
303 			case RC_FX:
304 				return -1;
305 			default:
306 				GetDoc();
307 		} // endswitch ReadDB
308 
309 		if (Find(g))
310 			return -1;
311 
312 		// Missing columns can be null
313 		for (bcp = fbcp; bcp; bcp = bcp->Next) {
314 			bcp->Cbn |= !bcp->Found;
315 			bcp->Found = false;
316 		} // endfor bcp
317 
318 	} // endfor i
319 
320 	return ncol;
321 } // end of GetColumns
322 
323 /***********************************************************************/
324 /*  Add a new column in the column list.                               */
325 /***********************************************************************/
326 void MGODISC::AddColumn(PGLOBAL g, PCSZ colname, PCSZ fmt, int k)
327 {
328 	// Check whether this column was already found
329 	for (bcp = fbcp; bcp; bcp = bcp->Next)
330 		if (!strcmp(colname, bcp->Name))
331 			break;
332 
333 	if (bcp) {
334 		if (bcp->Type != bcol.Type)
335 			bcp->Type = TYPE_STRING;
336 
337 		if (k && *fmt && (!bcp->Fmt || strlen(bcp->Fmt) < strlen(fmt))) {
338 			bcp->Fmt = PlugDup(g, fmt);
339 			length[7] = MY_MAX(length[7], (signed)strlen(fmt));
340 		} // endif *fmt
341 
342 		bcp->Len = MY_MAX(bcp->Len, bcol.Len);
343 		bcp->Scale = MY_MAX(bcp->Scale, bcol.Scale);
344 		bcp->Cbn |= bcol.Cbn;
345 		bcp->Found = true;
346 	} else {
347 		// New column
348 		bcp = (PBCOL)PlugSubAlloc(g, NULL, sizeof(BCOL));
349 		*bcp = bcol;
350 		bcp->Cbn |= (i > 1);
351 		bcp->Name = PlugDup(g, colname);
352 		length[0] = MY_MAX(length[0], (signed)strlen(colname));
353 
354 		if (k || JsonAllPath()) {
355 			bcp->Fmt = PlugDup(g, fmt);
356 			length[7] = MY_MAX(length[7], (signed)strlen(fmt));
357 		} else
358 			bcp->Fmt = NULL;
359 
360 		if (pbcp) {
361 			bcp->Next = pbcp->Next;
362 			pbcp->Next = bcp;
363 		} else
364 			fbcp = bcp;
365 
366 		ncol++;
367 	} // endif jcp
368 
369 	pbcp = bcp;
370 } // end of AddColumn
371 
372 /* -------------------------- Class MGODEF --------------------------- */
373 
374 MGODEF::MGODEF(void)
375 {
376 	Driver = NULL;
377 	Uri = NULL;
378 	Colist = NULL;
379 	Filter = NULL;
380 	Base = 0;
381 	Version = 0;
382 	Pipe = false;
383 } // end of MGODEF constructor
384 
385 /***********************************************************************/
386 /*  DefineAM: define specific AM block values.                         */
387 /***********************************************************************/
388 bool MGODEF::DefineAM(PGLOBAL g, LPCSTR, int poff)
389 {
390 	if (EXTDEF::DefineAM(g, "MGO", poff))
391 		return true;
392 	else if (!Tabschema)
393 		Tabschema = GetStringCatInfo(g, "Dbname", "*");
394 
395 	Driver = GetStringCatInfo(g, "Driver", NULL);
396 	Uri = GetStringCatInfo(g, "Connect", "mongodb://localhost:27017");
397 	Colist = GetStringCatInfo(g, "Colist", NULL);
398 	Filter = GetStringCatInfo(g, "Filter", NULL);
399 	Strfy = GetStringCatInfo(g, "Stringify", NULL);
400 	Base = GetIntCatInfo("Base", 0) ? 1 : 0;
401 	Version = GetIntCatInfo("Version", 3);
402 
403 	if (Version == 2)
404 		Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo2Interface");
405 	else
406 		Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo3Interface");
407 
408 	Pipe = GetBoolCatInfo("Pipeline", false);
409 	return false;
410 } // end of DefineAM
411 
412 /***********************************************************************/
413 /*  GetTable: makes a new Table Description Block.                     */
414 /***********************************************************************/
415 PTDB MGODEF::GetTable(PGLOBAL g, MODE m)
416 {
417 	if (Driver && toupper(*Driver) == 'C') {
418 #if defined(CMGO_SUPPORT)
419 		if (Catfunc == FNC_COL)
420 			return new(g) TDBGOL(this);
421 		else
422 			return new(g) TDBCMG(this);
423 #else
424 		sprintf(g->Message, "Mongo %s Driver not available", "C");
425 		return NULL;
426 #endif
427 	} else if (Driver && toupper(*Driver) == 'J') {
428 #if defined(JAVA_SUPPORT)
429 		if (Catfunc == FNC_COL)
430 			return new(g) TDBJGL(this);
431 		else
432 			return new(g) TDBJMG(this);
433 #else
434 		sprintf(g->Message, "Mongo %s Driver not available", "Java");
435 		return NULL;
436 #endif
437 	} else {						 // Driver not specified
438 #if defined(CMGO_SUPPORT)
439 		if (Catfunc == FNC_COL)
440 			return new(g) TDBGOL(this);
441 		else
442 			return new(g) TDBCMG(this);
443 #else
444 		if (Catfunc == FNC_COL)
445 			return new(g) TDBJGL(this);
446 		else
447 			return new(g) TDBJMG(this);
448 #endif
449 	} // endif Driver
450 
451 } // end of GetTable
452