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