1/************* TabJDBC C++ Program Source Code File (.CPP) *************/
2/* PROGRAM NAME: TABJDBC */
3/* ------------- */
4/* Version 1.2 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* This program are the TABJDBC class DB execution routines. */
13/* */
14/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
15/* -------------------------------------- */
16/* */
17/* REQUIRED FILES: */
18/* --------------- */
19/* TABJDBC.CPP - Source code */
20/* PLGDBSEM.H - DB application declaration file */
21/* TABJDBC.H - TABJDBC classes declaration file */
22/* GLOBAL.H - Global declaration file */
23/* */
24/* REQUIRED LIBRARIES: */
25/* ------------------- */
26/* Large model C library */
27/* */
28/* REQUIRED PROGRAMS: */
29/* ------------------ */
30/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
31/* */
32/***********************************************************************/
33
34/***********************************************************************/
35/* Include relevant MariaDB header file. */
36/***********************************************************************/
37#define MYSQL_SERVER 1
38#include "my_global.h"
39#include "sql_class.h"
40#include "sql_servers.h"
41#if defined(__WIN__)
42#include <io.h>
43#include <fcntl.h>
44#if defined(__BORLANDC__)
45#define __MFC_COMPAT__ // To define min/max as macro
46#endif
47//#include <windows.h>
48#include <sqltypes.h>
49#else
50#if defined(UNIX)
51#include <errno.h>
52#define NODW
53#include "osutil.h"
54#else
55#include <io.h>
56#endif
57#include <fcntl.h>
58#endif
59
60/***********************************************************************/
61/* Include application header files: */
62/* global.h is header containing all global declarations. */
63/* plgdbsem.h is header containing the DB application declarations. */
64/* kindex.h is kindex header that also includes tabdos.h. */
65/* tabJDBC.h is header containing the TABJDBC class declarations. */
66/* JDBConn.h is header containing JDBC connection declarations. */
67/***********************************************************************/
68#include "global.h"
69#include "plgdbsem.h"
70#include "mycat.h"
71#include "xtable.h"
72#include "tabext.h"
73#include "tabjdbc.h"
74#include "tabmul.h"
75//#include "reldef.h"
76#include "tabcol.h"
77#include "valblk.h"
78#include "ha_connect.h"
79
80#include "sql_string.h"
81
82/***********************************************************************/
83/* DB static variables. */
84/***********************************************************************/
85// int num_read, num_there, num_eq[2], num_nf; // Statistics
86extern int num_read, num_there, num_eq[2]; // Statistics
87
88/***********************************************************************/
89/* External function. */
90/***********************************************************************/
91bool ExactInfo(void);
92
93/* -------------------------- Class JDBCDEF -------------------------- */
94
95/***********************************************************************/
96/* Constructor. */
97/***********************************************************************/
98JDBCDEF::JDBCDEF(void)
99{
100 Driver = Url = Wrapname = NULL;
101} // end of JDBCDEF constructor
102
103/***********************************************************************/
104/* Called on table construction. */
105/***********************************************************************/
106bool JDBCDEF::SetParms(PJPARM sjp)
107{
108 sjp->Url= Url;
109 sjp->User= Username;
110 sjp->Pwd= Password;
111//sjp->Properties = Prop;
112 return true;
113} // end of SetParms
114
115/***********************************************************************/
116/* Parse connection string */
117/* */
118/* SYNOPSIS */
119/* ParseURL() */
120/* Url The connection string to parse */
121/* */
122/* DESCRIPTION */
123/* This is used to set the Url in case a wrapper server as been */
124/* specified. This is rather experimental yet. */
125/* */
126/* RETURN VALUE */
127/* RC_OK Url was a true URL */
128/* RC_NF Url was a server name/table */
129/* RC_FX Error */
130/* */
131/***********************************************************************/
132int JDBCDEF::ParseURL(PGLOBAL g, char *url, bool b)
133{
134 if (strncmp(url, "jdbc:", 5)) {
135 PSZ p;
136
137 // No "jdbc:" in connection string. Must be a straight
138 // "server" or "server/table"
139 // ok, so we do a little parsing, but not completely!
140 if ((p = strchr(url, '/'))) {
141 // If there is a single '/' in the connection string,
142 // this means the user is specifying a table name
143 *p++= '\0';
144
145 // there better not be any more '/'s !
146 if (strchr(p, '/'))
147 return RC_FX;
148
149 Tabname = p;
150// } else if (b) {
151// // Otherwise, straight server name,
152// Tabname = GetStringCatInfo(g, "Name", NULL);
153// Tabname = GetStringCatInfo(g, "Tabname", Tabname);
154 } // endif
155
156 if (trace(1))
157 htrc("server: %s Tabname: %s", url, Tabname);
158
159 // Now make the required URL
160 FOREIGN_SERVER *server, server_buffer;
161
162 // get_server_by_name() clones the server if exists
163 if (!(server= get_server_by_name(current_thd->mem_root, url, &server_buffer))) {
164 sprintf(g->Message, "Server %s does not exist!", url);
165 return RC_FX;
166 } // endif server
167
168 if (strncmp(server->host, "jdbc:", 5)) {
169 // Now make the required URL
170 Url = (PSZ)PlugSubAlloc(g, NULL, 0);
171 strcat(strcpy(Url, "jdbc:"), server->scheme);
172 strcat(strcat(Url, "://"), server->host);
173
174 if (server->port) {
175 char buf[16];
176
177 sprintf(buf, "%ld", server->port);
178 strcat(strcat(Url, ":"), buf);
179 } // endif port
180
181 if (server->db)
182 strcat(strcat(Url, "/"), server->db);
183
184 PlugSubAlloc(g, NULL, strlen(Url) + 1);
185 } else // host is a URL
186 Url = PlugDup(g, server->host);
187
188 if (server->username)
189 Username = PlugDup(g, server->username);
190
191 if (server->password)
192 Password = PlugDup(g, server->password);
193
194 return RC_NF;
195 } // endif
196
197 // Url was a JDBC URL, nothing to do
198 return RC_OK;
199} // end of ParseURL
200
201/***********************************************************************/
202/* DefineAM: define specific AM block values from JDBC file. */
203/***********************************************************************/
204bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
205{
206 int rc = RC_OK;
207
208 if (EXTDEF::DefineAM(g, am, poff))
209 return true;
210
211 Driver = GetStringCatInfo(g, "Driver", NULL);
212 Desc = Url = GetStringCatInfo(g, "Connect", NULL);
213
214 if (!Url && !Catfunc) {
215 // Look in the option list (deprecated)
216 Url = GetStringCatInfo(g, "Url", NULL);
217
218 if (!Url) {
219 sprintf(g->Message, "Missing URL for JDBC table %s", Name);
220 return true;
221 } // endif Url
222
223 } // endif Connect
224
225 if (Url)
226 if ((rc = ParseURL(g, Url)) == RC_FX) {
227 sprintf(g->Message, "Wrong JDBC URL %s", Url);
228 return true;
229 } // endif rc
230
231 Wrapname = GetStringCatInfo(g, "Wrapper", NULL);
232 return false;
233} // end of DefineAM
234
235/***********************************************************************/
236/* GetTable: makes a new Table Description Block. */
237/***********************************************************************/
238PTDB JDBCDEF::GetTable(PGLOBAL g, MODE m)
239{
240 PTDB tdbp = NULL;
241
242 /*********************************************************************/
243 /* Allocate a TDB of the proper type. */
244 /* Column blocks will be allocated only when needed. */
245 /*********************************************************************/
246 if (Xsrc)
247 tdbp = new(g)TDBXJDC(this);
248 else switch (Catfunc) {
249 case FNC_COL:
250 tdbp = new(g)TDBJDBCL(this);
251 break;
252#if 0
253 case FNC_DSN:
254 tdbp = new(g)TDBJSRC(this);
255 break;
256#endif // 0
257 case FNC_TABLE:
258 tdbp = new(g)TDBJTB(this);
259 break;
260 case FNC_DRIVER:
261 tdbp = new(g)TDBJDRV(this);
262 break;
263 default:
264 tdbp = new(g)TDBJDBC(this);
265
266 if (Multiple == 1)
267 tdbp = new(g)TDBMUL(tdbp);
268 else if (Multiple == 2)
269 strcpy(g->Message, "NO_JDBC_MUL");
270
271 } // endswitch Catfunc
272
273 return tdbp;
274} // end of GetTable
275
276/***********************************************************************/
277/* The MySQL and MariaDB JDBC drivers return by default a result set */
278/* containing the entire result of the executed query. This can be an */
279/* issue for big tables and memory error can occur. An alternative is */
280/* to use streaming (reading one row at a time) but to specify this, */
281/* a fech size of the integer min value must be send to the driver. */
282/***********************************************************************/
283int JDBCPARM::CheckSize(int rows)
284{
285 if (Url && rows == 1) {
286 // Are we connected to a MySQL JDBC connector?
287 bool b = (!strncmp(Url, "jdbc:mysql:", 11) ||
288 !strncmp(Url, "jdbc:mariadb:", 13));
289 return b ? INT_MIN32 : rows;
290 } else
291 return rows;
292
293} // end of CheckSize
294
295/* -------------------------- Class TDBJDBC -------------------------- */
296
297/***********************************************************************/
298/* Implementation of the TDBJDBC class. */
299/***********************************************************************/
300TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBEXT(tdp)
301{
302 Jcp = NULL;
303 Cnp = NULL;
304
305 if (tdp) {
306 Ops.Driver = tdp->Driver;
307 Ops.Url = tdp->Url;
308 Wrapname = tdp->Wrapname;
309 Ops.User = tdp->Username;
310 Ops.Pwd = tdp->Password;
311 Ops.Scrollable = tdp->Scrollable;
312 } else {
313 Wrapname = NULL;
314 Ops.Driver = NULL;
315 Ops.Url = NULL;
316 Ops.User = NULL;
317 Ops.Pwd = NULL;
318 Ops.Scrollable = false;
319 } // endif tdp
320
321 Prepared = false;
322 Werr = false;
323 Rerr = false;
324 Ops.Fsize = Ops.CheckSize(Rows);
325} // end of TDBJDBC standard constructor
326
327TDBJDBC::TDBJDBC(PTDBJDBC tdbp) : TDBEXT(tdbp)
328{
329 Jcp = tdbp->Jcp; // is that right ?
330 Cnp = tdbp->Cnp;
331 Wrapname = tdbp->Wrapname;
332 Ops = tdbp->Ops;
333 Prepared = tdbp->Prepared;
334 Werr = tdbp->Werr;
335 Rerr = tdbp->Rerr;
336} // end of TDBJDBC copy constructor
337
338// Method
339PTDB TDBJDBC::Clone(PTABS t)
340{
341 PTDB tp;
342 PJDBCCOL cp1, cp2;
343 PGLOBAL g = t->G; // Is this really useful ???
344
345 tp = new(g)TDBJDBC(this);
346
347 for (cp1 = (PJDBCCOL)Columns; cp1; cp1 = (PJDBCCOL)cp1->GetNext()) {
348 cp2 = new(g)JDBCCOL(cp1, tp); // Make a copy
349 NewPointer(t, cp1, cp2);
350 } // endfor cp1
351
352 return tp;
353} // end of Clone
354
355/***********************************************************************/
356/* Allocate JDBC column description block. */
357/***********************************************************************/
358PCOL TDBJDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
359{
360 return new(g)JDBCCOL(cdp, this, cprec, n);
361} // end of MakeCol
362
363/***********************************************************************/
364/* MakeInsert: make the Insert statement used with JDBC connection. */
365/***********************************************************************/
366bool TDBJDBC::MakeInsert(PGLOBAL g)
367{
368 PCSZ schmp = NULL;
369 char *catp = NULL, buf[NAM_LEN * 3];
370 int len = 0;
371 uint pos;
372 bool b = false;
373 PTABLE tablep = To_Table;
374 PCOL colp;
375
376 for (colp = Columns; colp; colp = colp->GetNext())
377 if (colp->IsSpecial()) {
378 strcpy(g->Message, "No JDBC special columns");
379 return true;
380 } else {
381 // Column name can be encoded in UTF-8
382 Decode(colp->GetName(), buf, sizeof(buf));
383 len += (strlen(buf) + 6); // comma + quotes + valist
384 ((PEXTCOL)colp)->SetRank(++Nparm);
385 } // endif colp
386
387 // Below 32 is enough to contain the fixed part of the query
388 if (Catalog && *Catalog)
389 catp = Catalog;
390
391 if (catp)
392 len += strlen(catp) + 1;
393
394 //if (tablep->GetSchema())
395 // schmp = (char*)tablep->GetSchema();
396 //else
397 if (Schema && *Schema)
398 schmp = Schema;
399
400 if (schmp)
401 len += strlen(schmp) + 1;
402
403 // Table name can be encoded in UTF-8
404 Decode(TableName, buf, sizeof(buf));
405 len += (strlen(buf) + 32);
406 Query = new(g)STRING(g, len, "INSERT INTO ");
407
408 if (catp) {
409 Query->Append(catp);
410
411 if (schmp) {
412 Query->Append('.');
413 Query->Append(schmp);
414 } // endif schmp
415
416 Query->Append('.');
417 } else if (schmp) {
418 Query->Append(schmp);
419 Query->Append('.');
420 } // endif schmp
421
422 if (Quote) {
423 // Put table name between identifier quotes in case in contains blanks
424 Query->Append(Quote);
425 Query->Append(buf);
426 Query->Append(Quote);
427 } else
428 Query->Append(buf);
429
430 Query->Append('(');
431
432 for (colp = Columns; colp; colp = colp->GetNext()) {
433 if (b)
434 Query->Append(", ");
435 else
436 b = true;
437
438 // Column name can be in UTF-8 encoding
439 Decode(colp->GetName(), buf, sizeof(buf));
440
441 if (Quote) {
442 // Put column name between identifier quotes in case in contains blanks
443 Query->Append(Quote);
444 Query->Append(buf);
445 Query->Append(Quote);
446 } else
447 Query->Append(buf);
448
449 } // endfor colp
450
451 if ((Query->Append(") VALUES ("))) {
452 strcpy(g->Message, "MakeInsert: Out of memory");
453 return true;
454 } else // in case prepared statement fails
455 pos = Query->GetLength();
456
457 // Make prepared statement
458 for (int i = 0; i < Nparm; i++)
459 Query->Append("?,");
460
461 if (Query->IsTruncated()) {
462 strcpy(g->Message, "MakeInsert: Out of memory");
463 return true;
464 } else
465 Query->RepLast(')');
466
467 // Now see if we can use prepared statement
468 if (Jcp->PrepareSQL(Query->GetStr()))
469 Query->Truncate(pos); // Restore query to not prepared
470 else
471 Prepared = true;
472
473 if (trace(33))
474 htrc("Insert=%s\n", Query->GetStr());
475
476 return false;
477} // end of MakeInsert
478
479/***********************************************************************/
480/* JDBC Set Parameter function. */
481/***********************************************************************/
482bool TDBJDBC::SetParameters(PGLOBAL g)
483{
484 PJDBCCOL colp;
485
486 for (colp = (PJDBCCOL)Columns; colp; colp = (PJDBCCOL)colp->Next)
487 if (Jcp->SetParam(colp))
488 return true;
489
490 return false;
491} // end of SetParameters
492
493/***********************************************************************/
494/* ResetSize: call by TDBMUL when calculating size estimate. */
495/***********************************************************************/
496void TDBJDBC::ResetSize(void)
497{
498 MaxSize = -1;
499
500 if (Jcp && Jcp->IsOpen())
501 Jcp->Close();
502
503} // end of ResetSize
504
505/***********************************************************************/
506/* JDBC Cardinality: returns table size in number of rows. */
507/***********************************************************************/
508int TDBJDBC::Cardinality(PGLOBAL g)
509{
510 if (!g)
511 return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
512
513#if 0
514 if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
515 // Info command, we must return the exact table row number
516 char qry[96], tbn[64];
517 JDBConn *jcp = new(g)JDBConn(g, this);
518
519 if (jcp->Open(&Ops) == RC_FX)
520 return -1;
521
522 // Table name can be encoded in UTF-8
523 Decode(TableName, tbn, sizeof(tbn));
524 strcpy(qry, "SELECT COUNT(*) FROM ");
525
526 if (Quote)
527 strcat(strcat(strcat(qry, Quote), tbn), Quote);
528 else
529 strcat(qry, tbn);
530
531 // Allocate a Count(*) column (must not use the default constructor)
532 Cnp = new(g)JDBCCOL;
533 Cnp->InitValue(g);
534
535 if ((Cardinal = jcp->GetResultSize(qry, Cnp)) < 0)
536 return -3;
537
538 jcp->Close();
539 } else
540#endif // 0
541 Cardinal = 10; // To make MariaDB happy
542
543 return Cardinal;
544} // end of Cardinality
545
546/***********************************************************************/
547/* JDBC Access Method opening routine. */
548/* New method now that this routine is called recursively (last table */
549/* first in reverse order): index blocks are immediately linked to */
550/* join block of next table if it exists or else are discarted. */
551/***********************************************************************/
552bool TDBJDBC::OpenDB(PGLOBAL g)
553{
554 bool rc = true;
555
556 if (trace(1))
557 htrc("JDBC OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
558 this, Tdb_No, Use, Mode);
559
560 if (Use == USE_OPEN) {
561 /*******************************************************************/
562 /* Table already open, just replace it at its beginning. */
563 /*******************************************************************/
564 if (Memory == 1) {
565 if ((Qrp = Jcp->AllocateResult(g, this)))
566 Memory = 2; // Must be filled
567 else
568 Memory = 0; // Allocation failed, don't use it
569
570 } else if (Memory == 2)
571 Memory = 3; // Ok to use memory result
572
573 if (Memory < 3) {
574 // Method will depend on cursor type
575 if ((Rbuf = Query ? Jcp->Rewind(Query->GetStr()) : 0) < 0)
576 if (Mode != MODE_READX) {
577 Jcp->Close();
578 return true;
579 } else
580 Rbuf = 0;
581
582 } else
583 Rbuf = Qrp->Nblin;
584
585 CurNum = 0;
586 Fpos = 0;
587 Curpos = 1;
588 return false;
589 } // endif use
590
591 /*********************************************************************/
592 /* Open an JDBC connection for this table. */
593 /* Note: this may not be the proper way to do. Perhaps it is better */
594 /* to test whether a connection is already open for this datasource */
595 /* and if so to allocate just a new result set. But this only for */
596 /* drivers allowing concurency in getting results ??? */
597 /*********************************************************************/
598 if (!Jcp)
599 Jcp = new(g)JDBConn(g, Wrapname);
600 else if (Jcp->IsOpen())
601 Jcp->Close();
602
603 if (Jcp->Connect(&Ops))
604 return true;
605 else if (Quoted)
606 Quote = Jcp->GetQuoteChar();
607
608 if (Mode != MODE_READ && Mode != MODE_READX)
609 if (Jcp->SetUUID(g, this))
610 PushWarning(g, this, 1);
611
612 Use = USE_OPEN; // Do it now in case we are recursively called
613
614 /*********************************************************************/
615 /* Make the command and allocate whatever is used for getting results*/
616 /*********************************************************************/
617 if (Mode == MODE_READ || Mode == MODE_READX) {
618 if (Memory > 1 && !Srcdef) {
619 int n;
620
621 if (!MakeSQL(g, true)) {
622 // Allocate a Count(*) column
623 Cnp = new(g)JDBCCOL;
624 Cnp->InitValue(g);
625
626 if ((n = Jcp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
627 sprintf(g->Message, "Cannot get result size rc=%d", n);
628 return true;
629 } else if (n) {
630 Jcp->m_Rows = n;
631
632 if ((Qrp = Jcp->AllocateResult(g, this)))
633 Memory = 2; // Must be filled
634 else {
635 strcpy(g->Message, "Result set memory allocation failed");
636 return true;
637 } // endif n
638
639 } else // Void result
640 Memory = 0;
641
642 Jcp->m_Rows = 0;
643 } else
644 return true;
645
646 } // endif Memory
647
648 if (!(rc = MakeSQL(g, false))) {
649// for (PJDBCCOL colp = (PJDBCCOL)Columns; colp; colp = (PJDBCCOL)colp->GetNext())
650// if (!colp->IsSpecial())
651// colp->AllocateBuffers(g, Rows);
652
653 rc = (Mode == MODE_READ)
654 ? (Jcp->ExecuteQuery(Query->GetStr()) != RC_OK)
655 : false;
656 } // endif rc
657
658 } else if (Mode == MODE_INSERT) {
659#if 0
660 if (!(rc = MakeInsert(g))) {
661 if (Nparm != Jcp->PrepareSQL(Query->GetStr())) {
662 strcpy(g->Message, MSG(PARM_CNT_MISS));
663 rc = true;
664 } else
665 rc = BindParameters(g);
666
667 } // endif rc
668#endif // 0
669 rc = MakeInsert(g);
670 } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
671 rc = false; // wait for CheckCond before calling MakeCommand(g);
672 } else
673 sprintf(g->Message, "Invalid mode %d", Mode);
674
675 if (rc) {
676 Jcp->Close();
677 return true;
678 } // endif rc
679
680 /*********************************************************************/
681 /* Reset statistics values. */
682 /*********************************************************************/
683 num_read = num_there = num_eq[0] = num_eq[1] = 0;
684 return false;
685} // end of OpenDB
686
687#if 0
688/***********************************************************************/
689/* GetRecpos: return the position of last read record. */
690/***********************************************************************/
691int TDBJDBC::GetRecpos(void)
692{
693 return Fpos;
694} // end of GetRecpos
695#endif // 0
696
697/***********************************************************************/
698/* SetRecpos: set the position of next read record. */
699/***********************************************************************/
700bool TDBJDBC::SetRecpos(PGLOBAL g, int recpos)
701{
702 if (Jcp->m_Full) {
703 Fpos = 0;
704 CurNum = 1;
705 } else if (Memory == 3) {
706 Fpos = 0;
707 CurNum = recpos;
708 } else if (Ops.Scrollable) {
709 // Is new position in the current row set?
710 if (recpos > 0 && recpos <= Rbuf) {
711 CurNum = recpos;
712 Fpos = recpos;
713 } else {
714 strcpy(g->Message, "Scrolling out of row set NIY");
715 return true;
716 } // endif recpos
717
718 } else {
719 strcpy(g->Message, "This action requires a scrollable cursor");
720 return true;
721 } // endif's
722
723 // Indicate the table position was externally set
724 Placed = true;
725 return false;
726} // end of SetRecpos
727
728/***********************************************************************/
729/* Data Base indexed read routine for JDBC access method. */
730/***********************************************************************/
731bool TDBJDBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
732{
733 char c = Quote ? *Quote : 0;
734 int rc, oldlen = Query->GetLength();
735 PHC hc = To_Def->GetHandler();
736
737 if (!(kr || hc->end_range) || op == OP_NEXT ||
738 Mode == MODE_UPDATE || Mode == MODE_DELETE) {
739 if (!kr && Mode == MODE_READX) {
740 // This is a false indexed read
741 rc = Jcp->ExecuteQuery((char*)Query->GetStr());
742 Mode = MODE_READ;
743 Rows = 1; // ???
744 return (rc != RC_OK);
745 } // endif key
746
747 return false;
748 } else {
749 if (hc->MakeKeyWhere(g, Query, op, c, kr))
750 return true;
751
752 if (To_CondFil) {
753 if (To_CondFil->Idx != hc->active_index) {
754 To_CondFil->Idx = hc->active_index;
755 To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
756 *To_CondFil->Body= 0;
757
758 if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond)))
759 PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
760
761 } // endif active_index
762
763 if (To_CondFil)
764 if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
765 strcpy(g->Message, "Readkey: Out of memory");
766 return true;
767 } // endif Append
768
769 } // endif To_Condfil
770
771 Mode = MODE_READ;
772 } // endif's op
773
774 if (trace(33))
775 htrc("JDBC ReadKey: Query=%s\n", Query->GetStr());
776
777 rc = Jcp->ExecuteQuery((char*)Query->GetStr());
778 Query->Truncate(oldlen);
779 Rows = 1; // ???
780 return (rc != RC_OK);
781} // end of ReadKey
782
783/***********************************************************************/
784/* Data Base read routine for JDBC access method. */
785/***********************************************************************/
786int TDBJDBC::ReadDB(PGLOBAL g)
787{
788 int rc;
789
790 if (trace(2))
791 htrc("JDBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
792
793 if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
794 if (!Query && MakeCommand(g))
795 return RC_FX;
796
797 // Send the UPDATE/DELETE command to the remote table
798 rc = Jcp->ExecuteUpdate(Query->GetStr());
799
800 if (rc == RC_OK) {
801 AftRows = Jcp->m_Aff;
802 return RC_EF; // Nothing else to do
803 } else {
804 Werr = true;
805 return RC_FX;
806 } // endif rc
807
808 } // endif Mode
809
810 /*********************************************************************/
811 /* Now start the reading process. */
812 /* Here is the place to fetch the line(s). */
813 /*********************************************************************/
814 if (Placed) {
815 if (Fpos && CurNum >= 0)
816 Rbuf = Jcp->Fetch((Curpos = Fpos));
817 else
818 Fpos = CurNum;
819
820 rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
821 Placed = false;
822 } else {
823 if (Memory != 3) {
824 if (++CurNum >= Rbuf) {
825 Rbuf = Jcp->Fetch();
826 Curpos = Fpos + 1;
827 CurNum = 0;
828 } // endif CurNum
829
830 rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
831 } else // Getting result from memory
832 rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
833
834 if (rc == RC_OK) {
835 if (Memory == 2)
836 Qrp->Nblin++;
837
838 Fpos++; // Used for memory and pos
839 } // endif rc
840
841 } // endif placed
842
843 if (trace(2))
844 htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
845
846 return rc;
847} // end of ReadDB
848
849/***********************************************************************/
850/* Data Base Insert write routine for JDBC access method. */
851/***********************************************************************/
852int TDBJDBC::WriteDB(PGLOBAL g)
853{
854 int rc;
855
856 if (Prepared) {
857 if (SetParameters(g)) {
858 Werr = true;
859 rc = RC_FX;
860 } else if ((rc = Jcp->ExecuteSQL()) == RC_OK)
861 AftRows += Jcp->m_Aff;
862 else
863 Werr = true;
864
865 return rc;
866 } // endif Prepared
867
868 // Statement was not prepared, we must construct and execute
869 // an insert query for each line to insert
870 uint len = Query->GetLength();
871 char buf[64];
872
873 // Make the Insert command value list
874 for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
875 if (!colp->GetValue()->IsNull()) {
876 char *s = colp->GetValue()->GetCharString(buf);
877
878 if (colp->GetResultType() == TYPE_STRING)
879 Query->Append_quoted(s);
880 else if (colp->GetResultType() == TYPE_DATE) {
881 DTVAL *dtv = (DTVAL*)colp->GetValue();
882
883 if (dtv->IsFormatted())
884 Query->Append_quoted(s);
885 else
886 Query->Append(s);
887
888 } else
889 Query->Append(s);
890
891 } else
892 Query->Append("NULL");
893
894 Query->Append(',');
895 } // endfor colp
896
897 if (unlikely(Query->IsTruncated())) {
898 strcpy(g->Message, "WriteDB: Out of memory");
899 return RC_FX;
900 } // endif Query
901
902 Query->RepLast(')');
903
904 if (trace(2))
905 htrc("Inserting: %s\n", Query->GetStr());
906
907 rc = Jcp->ExecuteUpdate(Query->GetStr());
908 Query->Truncate(len); // Restore query
909
910 if (rc == RC_OK)
911 AftRows += Jcp->m_Aff;
912 else
913 Werr = true;
914
915 return rc;
916} // end of WriteDB
917
918/***********************************************************************/
919/* Data Base delete line routine for JDBC access method. */
920/***********************************************************************/
921int TDBJDBC::DeleteDB(PGLOBAL g, int irc)
922{
923 if (irc == RC_FX) {
924 if (!Query && MakeCommand(g))
925 return RC_FX;
926
927 // Send the DELETE (all) command to the remote table
928 if (Jcp->ExecuteUpdate(Query->GetStr()) == RC_OK) {
929 AftRows = Jcp->m_Aff;
930 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
931
932 if (trace(1))
933 htrc("%s\n", g->Message);
934
935 PushWarning(g, this, 0); // 0 means a Note
936 return RC_OK; // This is a delete all
937 } else
938 return RC_FX; // Error
939
940 } else
941 return RC_OK; // Ignore
942
943} // end of DeleteDB
944
945/***********************************************************************/
946/* Data Base close routine for JDBC access method. */
947/***********************************************************************/
948void TDBJDBC::CloseDB(PGLOBAL g)
949{
950 if (Jcp)
951 Jcp->Close();
952
953 if (trace(1))
954 htrc("JDBC CloseDB: closing %s\n", Name);
955
956 if (!Werr &&
957 (Mode == MODE_INSERT || Mode == MODE_UPDATE || Mode == MODE_DELETE)) {
958 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
959
960 if (trace(1))
961 htrc("%s\n", g->Message);
962
963 PushWarning(g, this, 0); // 0 means a Note
964 } // endif Mode
965
966 Prepared = false;
967} // end of CloseDB
968
969/* --------------------------- JDBCCOL ------------------------------- */
970
971/***********************************************************************/
972/* JDBCCOL public constructor. */
973/***********************************************************************/
974JDBCCOL::JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
975 : EXTCOL(cdp, tdbp, cprec, i, am)
976{
977 uuid = false;
978} // end of JDBCCOL constructor
979
980/***********************************************************************/
981/* JDBCCOL private constructor. */
982/***********************************************************************/
983JDBCCOL::JDBCCOL(void) : EXTCOL()
984{
985 uuid = false;
986} // end of JDBCCOL constructor
987
988/***********************************************************************/
989/* JDBCCOL constructor used for copying columns. */
990/* tdbp is the pointer to the new table descriptor. */
991/***********************************************************************/
992JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
993{
994 uuid = col1->uuid;
995} // end of JDBCCOL copy constructor
996
997/***********************************************************************/
998/* ReadColumn: retrieve the column value via the JDBC driver. */
999/***********************************************************************/
1000void JDBCCOL::ReadColumn(PGLOBAL g)
1001{
1002 PTDBJDBC tdbp = (PTDBJDBC)To_Tdb;
1003 int i = tdbp->Fpos - 1, n = tdbp->CurNum;
1004
1005 if (tdbp->Memory == 3) {
1006 // Get the value from the stored memory
1007 if (Crp->Nulls && Crp->Nulls[i] == '*') {
1008 Value->Reset();
1009 Value->SetNull(true);
1010 } else {
1011 Value->SetValue_pvblk(Crp->Kdata, i);
1012 Value->SetNull(false);
1013 } // endif Nulls
1014
1015 return;
1016 } // endif Memory
1017
1018 /*********************************************************************/
1019 /* Get the column value. */
1020 /*********************************************************************/
1021 tdbp->Jcp->SetColumnValue(Rank, Name, Value);
1022
1023 if (tdbp->Memory != 2)
1024 return;
1025
1026 /*********************************************************************/
1027 /* Fill the allocated result structure. */
1028 /*********************************************************************/
1029 if (Value->IsNull()) {
1030 if (Crp->Nulls)
1031 Crp->Nulls[i] = '*'; // Null value
1032
1033 Crp->Kdata->Reset(i);
1034 } else
1035 Crp->Kdata->SetValue(Value, i);
1036
1037} // end of ReadColumn
1038
1039/***********************************************************************/
1040/* WriteColumn: Convert if necessary. */
1041/***********************************************************************/
1042void JDBCCOL::WriteColumn(PGLOBAL g)
1043{
1044 /*********************************************************************/
1045 /* Do convert the column value if necessary. */
1046 /*********************************************************************/
1047 if (Value != To_Val)
1048 Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
1049
1050} // end of WriteColumn
1051
1052/* -------------------------- Class TDBXJDC -------------------------- */
1053
1054/***********************************************************************/
1055/* Implementation of the TDBXJDC class. */
1056/***********************************************************************/
1057TDBXJDC::TDBXJDC(PJDBCDEF tdp) : TDBJDBC(tdp)
1058{
1059 Cmdlist = NULL;
1060 Cmdcol = NULL;
1061 Mxr = tdp->Maxerr;
1062 Nerr = 0;
1063} // end of TDBXJDC constructor
1064
1065/***********************************************************************/
1066/* Allocate XSRC column description block. */
1067/***********************************************************************/
1068PCOL TDBXJDC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1069{
1070 PJSRCCOL colp = new(g)JSRCCOL(cdp, this, cprec, n);
1071
1072 if (!colp->Flag)
1073 Cmdcol = colp->GetName();
1074
1075 return colp;
1076} // end of MakeCol
1077
1078/***********************************************************************/
1079/* MakeCMD: make the SQL statement to send to JDBC connection. */
1080/***********************************************************************/
1081PCMD TDBXJDC::MakeCMD(PGLOBAL g)
1082{
1083 PCMD xcmd = NULL;
1084
1085 if (To_CondFil) {
1086 if (Cmdcol) {
1087 if (!stricmp(Cmdcol, To_CondFil->Body) &&
1088 (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
1089 xcmd = To_CondFil->Cmds;
1090 } else
1091 strcpy(g->Message, "Invalid command specification filter");
1092
1093 } else
1094 strcpy(g->Message, "No command column in select list");
1095
1096 } else if (!Srcdef)
1097 strcpy(g->Message, "No Srcdef default command");
1098 else
1099 xcmd = new(g) CMD(g, Srcdef);
1100
1101 return xcmd;
1102} // end of MakeCMD
1103
1104/***********************************************************************/
1105/* XDBC GetMaxSize: returns table size (not always one row). */
1106/***********************************************************************/
1107int TDBXJDC::GetMaxSize(PGLOBAL g)
1108{
1109 if (MaxSize < 0)
1110 MaxSize = 2; // Just a guess
1111
1112 return MaxSize;
1113} // end of GetMaxSize
1114
1115/***********************************************************************/
1116/* JDBC Access Method opening routine. */
1117/* New method now that this routine is called recursively (last table */
1118/* first in reverse order): index blocks are immediately linked to */
1119/* join block of next table if it exists or else are discarted. */
1120/***********************************************************************/
1121bool TDBXJDC::OpenDB(PGLOBAL g)
1122{
1123 bool rc = false;
1124
1125 if (trace(1))
1126 htrc("JDBC OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
1127 this, Tdb_No, Use, Mode);
1128
1129 if (Use == USE_OPEN) {
1130 strcpy(g->Message, "Multiple execution is not allowed");
1131 return true;
1132 } // endif use
1133
1134 /*********************************************************************/
1135 /* Open an JDBC connection for this table. */
1136 /* Note: this may not be the proper way to do. Perhaps it is better */
1137 /* to test whether a connection is already open for this datasource */
1138 /* and if so to allocate just a new result set. But this only for */
1139 /* drivers allowing concurency in getting results ??? */
1140 /*********************************************************************/
1141 if (!Jcp) {
1142 Jcp = new(g) JDBConn(g, Wrapname);
1143 } else if (Jcp->IsOpen())
1144 Jcp->Close();
1145
1146 if (Jcp->Connect(&Ops))
1147 return true;
1148
1149 Use = USE_OPEN; // Do it now in case we are recursively called
1150
1151 if (Mode != MODE_READ && Mode != MODE_READX) {
1152 strcpy(g->Message, "No INSERT/DELETE/UPDATE of XJDBC tables");
1153 return true;
1154 } // endif Mode
1155
1156 /*********************************************************************/
1157 /* Get the command to execute. */
1158 /*********************************************************************/
1159 if (!(Cmdlist = MakeCMD(g))) {
1160 Jcp->Close();
1161 return true;
1162 } // endif Query
1163
1164 Rows = 1;
1165 return false;
1166} // end of OpenDB
1167
1168/***********************************************************************/
1169/* ReadDB: Data Base read routine for xdbc access method. */
1170/***********************************************************************/
1171int TDBXJDC::ReadDB(PGLOBAL g)
1172{
1173 if (Cmdlist) {
1174 int rc;
1175
1176 if (!Query)
1177 Query = new(g) STRING(g, 0, Cmdlist->Cmd);
1178 else
1179 Query->Set(Cmdlist->Cmd);
1180
1181 if ((rc = Jcp->ExecuteCommand(Query->GetStr())) == RC_FX)
1182 Nerr++;
1183
1184 if (rc == RC_NF)
1185 AftRows = Jcp->m_Aff;
1186 else if (rc == RC_OK)
1187 AftRows = Jcp->m_Ncol;
1188
1189 Fpos++; // Used for progress info
1190 Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
1191 return RC_OK;
1192 } else
1193 return RC_EF;
1194
1195} // end of ReadDB
1196
1197/***********************************************************************/
1198/* Data Base write line routine for JDBC access method. */
1199/***********************************************************************/
1200int TDBXJDC::WriteDB(PGLOBAL g)
1201{
1202 strcpy(g->Message, "Execsrc tables are read only");
1203 return RC_FX;
1204} // end of DeleteDB
1205
1206/***********************************************************************/
1207/* Data Base delete line routine for JDBC access method. */
1208/***********************************************************************/
1209int TDBXJDC::DeleteDB(PGLOBAL g, int irc)
1210{
1211 strcpy(g->Message, "NO_XJDBC_DELETE");
1212 return RC_FX;
1213} // end of DeleteDB
1214
1215/* --------------------------- JSRCCOL ------------------------------- */
1216
1217/***********************************************************************/
1218/* JSRCCOL public constructor. */
1219/***********************************************************************/
1220JSRCCOL::JSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1221 : JDBCCOL(cdp, tdbp, cprec, i, am)
1222{
1223 // Set additional JDBC access method information for column.
1224 Flag = cdp->GetOffset();
1225} // end of JSRCCOL constructor
1226
1227/***********************************************************************/
1228/* ReadColumn: set column value according to Flag. */
1229/***********************************************************************/
1230void JSRCCOL::ReadColumn(PGLOBAL g)
1231{
1232 PTDBXJDC tdbp = (PTDBXJDC)To_Tdb;
1233
1234 switch (Flag) {
1235 case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
1236 case 1: Value->SetValue(tdbp->AftRows); break;
1237 case 2: Value->SetValue_psz(g->Message); break;
1238 default: Value->SetValue_psz("Invalid Flag"); break;
1239 } // endswitch Flag
1240
1241} // end of ReadColumn
1242
1243/***********************************************************************/
1244/* WriteColumn: Should never be called. */
1245/***********************************************************************/
1246void JSRCCOL::WriteColumn(PGLOBAL g)
1247{
1248 // Should never be called
1249} // end of WriteColumn
1250
1251/* ---------------------------TDBJDRV class -------------------------- */
1252
1253/***********************************************************************/
1254/* GetResult: Get the list of JDBC drivers. */
1255/***********************************************************************/
1256PQRYRES TDBJDRV::GetResult(PGLOBAL g)
1257{
1258 return JDBCDrivers(g, Maxres, false);
1259} // end of GetResult
1260
1261/* ---------------------------TDBJTB class --------------------------- */
1262
1263/***********************************************************************/
1264/* TDBJTB class constructor. */
1265/***********************************************************************/
1266TDBJTB::TDBJTB(PJDBCDEF tdp) : TDBJDRV(tdp)
1267{
1268 Schema = tdp->Tabschema;
1269 Tab = tdp->Tabname;
1270 Tabtype = tdp->Tabtyp;
1271 Ops.Driver = tdp->Driver;
1272 Ops.Url = tdp->Url;
1273 Ops.User = tdp->Username;
1274 Ops.Pwd = tdp->Password;
1275 Ops.Fsize = 0;
1276 Ops.Scrollable = false;
1277} // end of TDBJTB constructor
1278
1279/***********************************************************************/
1280/* GetResult: Get the list of JDBC tables. */
1281/***********************************************************************/
1282PQRYRES TDBJTB::GetResult(PGLOBAL g)
1283{
1284 return JDBCTables(g, Schema, Tab, Tabtype, Maxres, false, &Ops);
1285} // end of GetResult
1286
1287/* --------------------------TDBJDBCL class -------------------------- */
1288
1289/***********************************************************************/
1290/* TDBJDBCL class constructor. */
1291/***********************************************************************/
1292TDBJDBCL::TDBJDBCL(PJDBCDEF tdp) : TDBJTB(tdp)
1293{
1294 Colpat = tdp->Colpat;
1295} // end of TDBJDBCL constructor
1296
1297/***********************************************************************/
1298/* GetResult: Get the list of JDBC table columns. */
1299/***********************************************************************/
1300PQRYRES TDBJDBCL::GetResult(PGLOBAL g)
1301{
1302 return JDBCColumns(g, Schema, Tab, Colpat, Maxres, false, &Ops);
1303} // end of GetResult
1304