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 {
589 if (Mode != MODE_READX) {
590 Jcp->Close();
591 return true;
592 } else
593 Rbuf = 0;
594 }
595
596 } else
597 Rbuf = Qrp->Nblin;
598
599 CurNum = 0;
600 Fpos = 0;
601 Curpos = 1;
602 } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
603 // new update coming from a trigger or procedure
604 Query = NULL;
605 SetCondFil(NULL);
606 Qrystr = To_Def->GetStringCatInfo(g, "Query_String", "?");
607 } else { //if (Mode == MODE_INSERT)
608 } // endif Mode
609
610 return false;
611 } // endif use
612
613 /*********************************************************************/
614 /* Open an JDBC connection for this table. */
615 /* Note: this may not be the proper way to do. Perhaps it is better */
616 /* to test whether a connection is already open for this datasource */
617 /* and if so to allocate just a new result set. But this only for */
618 /* drivers allowing concurency in getting results ??? */
619 /*********************************************************************/
620 if (!Jcp)
621 Jcp = new(g)JDBConn(g, Wrapname);
622 else if (Jcp->IsOpen())
623 Jcp->Close();
624
625 if (Jcp->Connect(&Ops))
626 return true;
627 else if (Quoted)
628 Quote = Jcp->GetQuoteChar();
629
630 if (Mode != MODE_READ && Mode != MODE_READX)
631 if (Jcp->SetUUID(g, this))
632 PushWarning(g, this, 1);
633
634 Use = USE_OPEN; // Do it now in case we are recursively called
635
636 /*********************************************************************/
637 /* Make the command and allocate whatever is used for getting results*/
638 /*********************************************************************/
639 if (Mode == MODE_READ || Mode == MODE_READX) {
640 if (Memory > 1 && !Srcdef) {
641 int n;
642
643 if (!MakeSQL(g, true)) {
644 // Allocate a Count(*) column
645 Cnp = new(g)JDBCCOL;
646 Cnp->InitValue(g);
647
648 if ((n = Jcp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
649 char* msg = PlugDup(g, g->Message);
650
651 sprintf(g->Message, "Get result size: %s (rc=%d)", msg, n);
652 return true;
653 } else if (n) {
654 Jcp->m_Rows = n;
655
656 if ((Qrp = Jcp->AllocateResult(g, this)))
657 Memory = 2; // Must be filled
658 else {
659 strcpy(g->Message, "Result set memory allocation failed");
660 return true;
661 } // endif n
662
663 } else // Void result
664 Memory = 0;
665
666 Jcp->m_Rows = 0;
667 } else
668 return true;
669
670 } // endif Memory
671
672 if (!(rc = MakeSQL(g, false))) {
673 // for (PJDBCCOL colp = (PJDBCCOL)Columns; colp; colp = (PJDBCCOL)colp->GetNext())
674 // if (!colp->IsSpecial())
675 // colp->AllocateBuffers(g, Rows);
676
677 rc = (Mode == MODE_READ)
678 ? (Jcp->ExecuteQuery(Query->GetStr()) != RC_OK)
679 : false;
680 } // endif rc
681
682 } else if (Mode == MODE_INSERT) {
683 #if 0
684 if (!(rc = MakeInsert(g))) {
685 if (Nparm != Jcp->PrepareSQL(Query->GetStr())) {
686 strcpy(g->Message, MSG(PARM_CNT_MISS));
687 rc = true;
688 } else
689 rc = BindParameters(g);
690
691 } // endif rc
692 #endif // 0
693 rc = MakeInsert(g);
694 } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
695 rc = false; // wait for CheckCond before calling MakeCommand(g);
696 } else
697 sprintf(g->Message, "Invalid mode %d", Mode);
698
699 if (rc) {
700 Jcp->Close();
701 return true;
702 } // endif rc
703
704 /*********************************************************************/
705 /* Reset statistics values. */
706 /*********************************************************************/
707 num_read = num_there = num_eq[0] = num_eq[1] = 0;
708 return false;
709 } // end of OpenDB
710
711 #if 0
712 /***********************************************************************/
713 /* GetRecpos: return the position of last read record. */
714 /***********************************************************************/
715 int TDBJDBC::GetRecpos(void)
716 {
717 return Fpos;
718 } // end of GetRecpos
719 #endif // 0
720
721 /***********************************************************************/
722 /* SetRecpos: set the position of next read record. */
723 /***********************************************************************/
SetRecpos(PGLOBAL g,int recpos)724 bool TDBJDBC::SetRecpos(PGLOBAL g, int recpos)
725 {
726 if (Jcp->m_Full) {
727 Fpos = 0;
728 CurNum = 1;
729 } else if (Memory == 3) {
730 Fpos = 0;
731 CurNum = recpos;
732 } else if (Ops.Scrollable) {
733 // Is new position in the current row set?
734 if (recpos > 0 && recpos <= Rbuf) {
735 CurNum = recpos;
736 Fpos = recpos;
737 } else {
738 strcpy(g->Message, "Scrolling out of row set NIY");
739 return true;
740 } // endif recpos
741
742 } else {
743 strcpy(g->Message, "This action requires a scrollable cursor");
744 return true;
745 } // endif's
746
747 // Indicate the table position was externally set
748 Placed = true;
749 return false;
750 } // end of SetRecpos
751
752 /***********************************************************************/
753 /* Data Base indexed read routine for JDBC access method. */
754 /***********************************************************************/
ReadKey(PGLOBAL g,OPVAL op,const key_range * kr)755 bool TDBJDBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
756 {
757 char c = Quote ? *Quote : 0;
758 int rc, oldlen = Query->GetLength();
759 PHC hc = To_Def->GetHandler();
760
761 if (!(kr || hc->end_range) || op == OP_NEXT ||
762 Mode == MODE_UPDATE || Mode == MODE_DELETE) {
763 if (!kr && Mode == MODE_READX) {
764 // This is a false indexed read
765 rc = Jcp->ExecuteQuery((char*)Query->GetStr());
766 Mode = MODE_READ;
767 Rows = 1; // ???
768 return (rc != RC_OK);
769 } // endif key
770
771 return false;
772 } else {
773 if (hc->MakeKeyWhere(g, Query, op, c, kr))
774 return true;
775
776 if (To_CondFil) {
777 if (To_CondFil->Idx != hc->active_index) {
778 To_CondFil->Idx = hc->active_index;
779 To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
780 *To_CondFil->Body= 0;
781
782 if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond)))
783 PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
784
785 } // endif active_index
786
787 if (To_CondFil)
788 if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
789 strcpy(g->Message, "Readkey: Out of memory");
790 return true;
791 } // endif Append
792
793 } // endif To_Condfil
794
795 Mode = MODE_READ;
796 } // endif's op
797
798 if (trace(33))
799 htrc("JDBC ReadKey: Query=%s\n", Query->GetStr());
800
801 rc = Jcp->ExecuteQuery((char*)Query->GetStr());
802 Query->Truncate(oldlen);
803 Rows = 1; // ???
804 return (rc != RC_OK);
805 } // end of ReadKey
806
807 /***********************************************************************/
808 /* Data Base read routine for JDBC access method. */
809 /***********************************************************************/
ReadDB(PGLOBAL g)810 int TDBJDBC::ReadDB(PGLOBAL g)
811 {
812 int rc;
813
814 if (trace(2))
815 htrc("JDBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
816
817 if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
818 if (!Query && MakeCommand(g))
819 return RC_FX;
820
821 // Send the UPDATE/DELETE command to the remote table
822 rc = Jcp->ExecuteUpdate(Query->GetStr());
823
824 if (rc == RC_OK) {
825 AftRows = Jcp->m_Aff;
826 return RC_EF; // Nothing else to do
827 } else {
828 Werr = true;
829 return RC_FX;
830 } // endif rc
831
832 } // endif Mode
833
834 /*********************************************************************/
835 /* Now start the reading process. */
836 /* Here is the place to fetch the line(s). */
837 /*********************************************************************/
838 if (Placed) {
839 if (Fpos && CurNum >= 0)
840 Rbuf = Jcp->Fetch((Curpos = Fpos));
841 else
842 Fpos = CurNum;
843
844 rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
845 Placed = false;
846 } else {
847 if (Memory != 3) {
848 if (++CurNum >= Rbuf) {
849 Rbuf = Jcp->Fetch();
850 Curpos = Fpos + 1;
851 CurNum = 0;
852 } // endif CurNum
853
854 rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
855 } else // Getting result from memory
856 rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
857
858 if (rc == RC_OK) {
859 if (Memory == 2)
860 Qrp->Nblin++;
861
862 Fpos++; // Used for memory and pos
863 } // endif rc
864
865 } // endif placed
866
867 if (trace(2))
868 htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
869
870 return rc;
871 } // end of ReadDB
872
873 /***********************************************************************/
874 /* Data Base Insert write routine for JDBC access method. */
875 /***********************************************************************/
WriteDB(PGLOBAL g)876 int TDBJDBC::WriteDB(PGLOBAL g)
877 {
878 int rc;
879
880 if (Prepared) {
881 if (SetParameters(g)) {
882 Werr = true;
883 rc = RC_FX;
884 } else if ((rc = Jcp->ExecuteSQL()) == RC_OK)
885 AftRows += Jcp->m_Aff;
886 else
887 Werr = true;
888
889 return rc;
890 } // endif Prepared
891
892 // Statement was not prepared, we must construct and execute
893 // an insert query for each line to insert
894 uint len = Query->GetLength();
895 char buf[64];
896
897 // Make the Insert command value list
898 for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
899 if (!colp->GetValue()->IsNull()) {
900 char *s = colp->GetValue()->GetCharString(buf);
901
902 if (colp->GetResultType() == TYPE_STRING)
903 Query->Append_quoted(s);
904 else if (colp->GetResultType() == TYPE_DATE) {
905 DTVAL *dtv = (DTVAL*)colp->GetValue();
906
907 if (dtv->IsFormatted())
908 Query->Append_quoted(s);
909 else
910 Query->Append(s);
911
912 } else
913 Query->Append(s);
914
915 } else
916 Query->Append("NULL");
917
918 Query->Append(',');
919 } // endfor colp
920
921 if (unlikely(Query->IsTruncated())) {
922 strcpy(g->Message, "WriteDB: Out of memory");
923 return RC_FX;
924 } // endif Query
925
926 Query->RepLast(')');
927
928 if (trace(2))
929 htrc("Inserting: %s\n", Query->GetStr());
930
931 rc = Jcp->ExecuteUpdate(Query->GetStr());
932 Query->Truncate(len); // Restore query
933
934 if (rc == RC_OK)
935 AftRows += Jcp->m_Aff;
936 else
937 Werr = true;
938
939 return rc;
940 } // end of WriteDB
941
942 /***********************************************************************/
943 /* Data Base delete line routine for JDBC access method. */
944 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)945 int TDBJDBC::DeleteDB(PGLOBAL g, int irc)
946 {
947 if (irc == RC_FX) {
948 if (!Query && MakeCommand(g))
949 return RC_FX;
950
951 // Send the DELETE (all) command to the remote table
952 if (Jcp->ExecuteUpdate(Query->GetStr()) == RC_OK) {
953 AftRows = Jcp->m_Aff;
954 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
955
956 if (trace(1))
957 htrc("%s\n", g->Message);
958
959 PushWarning(g, this, 0); // 0 means a Note
960 return RC_OK; // This is a delete all
961 } else
962 return RC_FX; // Error
963
964 } else
965 return RC_OK; // Ignore
966
967 } // end of DeleteDB
968
969 /***********************************************************************/
970 /* Data Base close routine for JDBC access method. */
971 /***********************************************************************/
CloseDB(PGLOBAL g)972 void TDBJDBC::CloseDB(PGLOBAL g)
973 {
974 if (Jcp)
975 Jcp->Close();
976
977 if (trace(1))
978 htrc("JDBC CloseDB: closing %s\n", Name);
979
980 if (!Werr &&
981 (Mode == MODE_INSERT || Mode == MODE_UPDATE || Mode == MODE_DELETE)) {
982 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
983
984 if (trace(1))
985 htrc("%s\n", g->Message);
986
987 PushWarning(g, this, 0); // 0 means a Note
988 } // endif Mode
989
990 Prepared = false;
991 } // end of CloseDB
992
993 /* --------------------------- JDBCCOL ------------------------------- */
994
995 /***********************************************************************/
996 /* JDBCCOL public constructor. */
997 /***********************************************************************/
JDBCCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)998 JDBCCOL::JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
999 : EXTCOL(cdp, tdbp, cprec, i, am)
1000 {
1001 uuid = false;
1002 } // end of JDBCCOL constructor
1003
1004 /***********************************************************************/
1005 /* JDBCCOL private constructor. */
1006 /***********************************************************************/
JDBCCOL(void)1007 JDBCCOL::JDBCCOL(void) : EXTCOL()
1008 {
1009 uuid = false;
1010 } // end of JDBCCOL constructor
1011
1012 /***********************************************************************/
1013 /* JDBCCOL constructor used for copying columns. */
1014 /* tdbp is the pointer to the new table descriptor. */
1015 /***********************************************************************/
JDBCCOL(JDBCCOL * col1,PTDB tdbp)1016 JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
1017 {
1018 uuid = col1->uuid;
1019 } // end of JDBCCOL copy constructor
1020
1021 /***********************************************************************/
1022 /* ReadColumn: retrieve the column value via the JDBC driver. */
1023 /***********************************************************************/
ReadColumn(PGLOBAL g)1024 void JDBCCOL::ReadColumn(PGLOBAL g)
1025 {
1026 PTDBJDBC tdbp = (PTDBJDBC)To_Tdb;
1027 int i = tdbp->Fpos - 1;
1028
1029 if (tdbp->Memory == 3) {
1030 // Get the value from the stored memory
1031 if (Crp->Nulls && Crp->Nulls[i] == '*') {
1032 Value->Reset();
1033 Value->SetNull(true);
1034 } else {
1035 Value->SetValue_pvblk(Crp->Kdata, i);
1036 Value->SetNull(false);
1037 } // endif Nulls
1038
1039 return;
1040 } // endif Memory
1041
1042 /*********************************************************************/
1043 /* Get the column value. */
1044 /*********************************************************************/
1045 tdbp->Jcp->SetColumnValue(Rank, Name, Value);
1046
1047 if (tdbp->Memory != 2)
1048 return;
1049
1050 /*********************************************************************/
1051 /* Fill the allocated result structure. */
1052 /*********************************************************************/
1053 if (Value->IsNull()) {
1054 if (Crp->Nulls)
1055 Crp->Nulls[i] = '*'; // Null value
1056
1057 Crp->Kdata->Reset(i);
1058 } else
1059 Crp->Kdata->SetValue(Value, i);
1060
1061 } // end of ReadColumn
1062
1063 /***********************************************************************/
1064 /* WriteColumn: Convert if necessary. */
1065 /***********************************************************************/
WriteColumn(PGLOBAL g)1066 void JDBCCOL::WriteColumn(PGLOBAL g)
1067 {
1068 /*********************************************************************/
1069 /* Do convert the column value if necessary. */
1070 /*********************************************************************/
1071 if (Value != To_Val)
1072 Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
1073
1074 } // end of WriteColumn
1075
1076 /* -------------------------- Class TDBXJDC -------------------------- */
1077
1078 /***********************************************************************/
1079 /* Implementation of the TDBXJDC class. */
1080 /***********************************************************************/
TDBXJDC(PJDBCDEF tdp)1081 TDBXJDC::TDBXJDC(PJDBCDEF tdp) : TDBJDBC(tdp)
1082 {
1083 Cmdlist = NULL;
1084 Cmdcol = NULL;
1085 Mxr = tdp->Maxerr;
1086 Nerr = 0;
1087 } // end of TDBXJDC constructor
1088
1089 /***********************************************************************/
1090 /* Allocate XSRC column description block. */
1091 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)1092 PCOL TDBXJDC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1093 {
1094 PJSRCCOL colp = new(g)JSRCCOL(cdp, this, cprec, n);
1095
1096 if (!colp->Flag)
1097 Cmdcol = colp->GetName();
1098
1099 return colp;
1100 } // end of MakeCol
1101
1102 /***********************************************************************/
1103 /* MakeCMD: make the SQL statement to send to JDBC connection. */
1104 /***********************************************************************/
MakeCMD(PGLOBAL g)1105 PCMD TDBXJDC::MakeCMD(PGLOBAL g)
1106 {
1107 PCMD xcmd = NULL;
1108
1109 if (To_CondFil) {
1110 if (Cmdcol) {
1111 if (!stricmp(Cmdcol, To_CondFil->Body) &&
1112 (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
1113 xcmd = To_CondFil->Cmds;
1114 } else
1115 strcpy(g->Message, "Invalid command specification filter");
1116
1117 } else
1118 strcpy(g->Message, "No command column in select list");
1119
1120 } else if (!Srcdef)
1121 strcpy(g->Message, "No Srcdef default command");
1122 else
1123 xcmd = new(g) CMD(g, Srcdef);
1124
1125 return xcmd;
1126 } // end of MakeCMD
1127
1128 /***********************************************************************/
1129 /* XDBC GetMaxSize: returns table size (not always one row). */
1130 /***********************************************************************/
GetMaxSize(PGLOBAL g)1131 int TDBXJDC::GetMaxSize(PGLOBAL g)
1132 {
1133 if (MaxSize < 0)
1134 MaxSize = 2; // Just a guess
1135
1136 return MaxSize;
1137 } // end of GetMaxSize
1138
1139 /***********************************************************************/
1140 /* JDBC Access Method opening routine. */
1141 /* New method now that this routine is called recursively (last table */
1142 /* first in reverse order): index blocks are immediately linked to */
1143 /* join block of next table if it exists or else are discarted. */
1144 /***********************************************************************/
OpenDB(PGLOBAL g)1145 bool TDBXJDC::OpenDB(PGLOBAL g)
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