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 | |
32 | PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt, bool info); |
33 | |
34 | /* -------------------------- Class JMGDISC -------------------------- */ |
35 | |
36 | /***********************************************************************/ |
37 | /* Constructor */ |
38 | /***********************************************************************/ |
39 | JMGDISC::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 | /***********************************************************************/ |
47 | bool 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 | /***********************************************************************/ |
64 | bool 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 | /***********************************************************************/ |
72 | bool 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 | /***********************************************************************/ |
151 | TDBJMG::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 | |
190 | TDBJMG::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 |
205 | PTDB 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 | /***********************************************************************/ |
225 | PCOL 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 | /***********************************************************************/ |
233 | PCOL 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 | /***********************************************************************/ |
246 | int 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 | /***********************************************************************/ |
259 | int 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 | /***********************************************************************/ |
270 | bool 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 | /***********************************************************************/ |
297 | bool 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 | /***********************************************************************/ |
347 | bool 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 | /***********************************************************************/ |
356 | int 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 | /***********************************************************************/ |
379 | int 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 | /***********************************************************************/ |
397 | int 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 | /***********************************************************************/ |
405 | void 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 | /***********************************************************************/ |
416 | JMGCOL::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 | /***********************************************************************/ |
428 | JMGCOL::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 | /***********************************************************************/ |
438 | PSZ 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 | /***********************************************************************/ |
474 | char *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 | /***********************************************************************/ |
513 | void JMGCOL::ReadColumn(PGLOBAL g) |
514 | { |
515 | Value->SetValue_psz(Tmgp->Jcp->GetColumnValue(Jpath)); |
516 | } // end of ReadColumn |
517 | |
518 | /***********************************************************************/ |
519 | /* WriteColumn: */ |
520 | /***********************************************************************/ |
521 | void 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 | /***********************************************************************/ |
533 | bool 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 | /***********************************************************************/ |
589 | TDBJGL::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 | /***********************************************************************/ |
599 | PQRYRES TDBJGL::GetResult(PGLOBAL g) |
600 | { |
601 | return MGOColumns(g, Db, Uri, Topt, false); |
602 | } // end of GetResult |
603 | |
604 | /* -------------------------- End of mongo --------------------------- */ |
605 | |