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