1/************ Jdbconn C++ Functions Source Code File (.CPP) ************/
2/* Name: JDBCONN.CPP Version 1.2 */
3/* */
4/* (C) Copyright to the author Olivier BERTRAND 2016-2018 */
5/* */
6/* This file contains the JDBC connection classes functions. */
7/***********************************************************************/
8
9#if defined(__WIN__)
10// This is needed for RegGetValue
11#define _WINVER 0x0601
12#undef _WIN32_WINNT
13#define _WIN32_WINNT 0x0601
14#endif // __WIN__
15
16/***********************************************************************/
17/* Include relevant MariaDB header file. */
18/***********************************************************************/
19#include <my_global.h>
20#include <m_string.h>
21#if defined(__WIN__)
22//nclude <io.h>
23//nclude <fcntl.h>
24#include <direct.h> // for getcwd
25#if defined(__BORLANDC__)
26#define __MFC_COMPAT__ // To define min/max as macro
27#endif // __BORLANDC__
28//#include <windows.h>
29#else // !__WIN__
30#if defined(UNIX)
31#include <errno.h>
32#else // !UNIX
33//nclude <io.h>
34#endif // !UNIX
35#include <stdio.h>
36#include <stdlib.h> // for getenv
37//nclude <fcntl.h>
38#define NODW
39#endif // !__WIN__
40
41/***********************************************************************/
42/* Required objects includes. */
43/***********************************************************************/
44#include "global.h"
45#include "plgdbsem.h"
46#include "xobject.h"
47#include "xtable.h"
48#include "tabext.h"
49#include "tabjdbc.h"
50//#include "jdbconn.h"
51#include "resource.h"
52#include "valblk.h"
53#include "osutil.h"
54
55
56//#if defined(__WIN__)
57//extern "C" HINSTANCE s_hModule; // Saved module handle
58//#endif // __WIN__
59#define nullptr 0
60
61TYPCONV GetTypeConv();
62int GetConvSize();
63//extern char *JvmPath; // The connect_jvm_path global variable value
64//extern char *ClassPath; // The connect_class_path global variable value
65
66//char *GetJavaWrapper(void); // The connect_java_wrapper variable value
67
68/***********************************************************************/
69/* Some macro's (should be defined elsewhere to be more accessible) */
70/***********************************************************************/
71//#if defined(_DEBUG)
72//#define ASSERT(f) assert(f)
73//#define DEBUG_ONLY(f) (f)
74//#else // !_DEBUG
75//#define ASSERT(f) ((void)0)
76//#define DEBUG_ONLY(f) ((void)0)
77//#endif // !_DEBUG
78
79// To avoid gcc warning
80int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v);
81
82/***********************************************************************/
83/* GetJDBCType: returns the SQL_TYPE corresponding to a PLG type. */
84/***********************************************************************/
85static short GetJDBCType(int type)
86{
87 short tp = 0; // NULL
88
89 switch (type) {
90 case TYPE_STRING: tp = 12; break; // VARCHAR
91 case TYPE_SHORT: tp = 5; break; // SMALLINT
92 case TYPE_INT: tp = 4; break; // INTEGER
93 case TYPE_DATE: tp = 93; break; // DATE
94//case TYPE_TIME: tp = 92; break; // TIME
95//case TYPE_TIMESTAMP: tp = 93; break; // TIMESTAMP
96 case TYPE_BIGINT: tp = -5; break; // BIGINT
97 case TYPE_DOUBLE: tp = 8; break; // DOUBLE
98 case TYPE_TINY: tp = -6; break; // TINYINT
99 case TYPE_DECIM: tp = 3; break; // DECIMAL
100 } // endswitch type
101
102 return tp;
103} // end of GetJDBCType
104
105/***********************************************************************/
106/* TranslateJDBCType: translate a JDBC Type to a PLG type. */
107/***********************************************************************/
108int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v)
109{
110 int type;
111
112 switch (stp) {
113 case -1: // LONGVARCHAR, TEXT
114 case -16: // LONGNVARCHAR, NTEXT (unicode)
115 if (GetTypeConv() != TPC_YES)
116 return TYPE_ERROR;
117 else
118 len = MY_MIN(abs(len), GetConvSize());
119
120 // Pass through
121 case 12: // VARCHAR
122 if (tn && !stricmp(tn, "TEXT"))
123 // Postgresql returns 12 for TEXT
124 if (GetTypeConv() == TPC_NO)
125 return TYPE_ERROR;
126
127 // Postgresql can return this
128 if (len == 0x7FFFFFFF)
129 len = GetConvSize();
130
131 // Pass through
132 case -9: // NVARCHAR (unicode)
133 // Postgresql can return this when size is unknown
134 if (len == 0x7FFFFFFF)
135 len = GetConvSize();
136
137 v = 'V';
138 // Pass through
139 case 1: // CHAR
140 case -15: // NCHAR (unicode)
141 case -8: // ROWID
142 type = TYPE_STRING;
143 break;
144 case 2: // NUMERIC
145 case 3: // DECIMAL
146 case -3: // VARBINARY
147 type = TYPE_DECIM;
148 break;
149 case 4: // INTEGER
150 type = TYPE_INT;
151 break;
152 case 5: // SMALLINT
153 type = TYPE_SHORT;
154 break;
155 case -6: // TINYINT
156 case -7: // BIT
157 case 16: // BOOLEAN
158 type = TYPE_TINY;
159 break;
160 case 6: // FLOAT
161 case 7: // REAL
162 case 8: // DOUBLE
163 type = TYPE_DOUBLE;
164 break;
165 case 93: // TIMESTAMP, DATETIME
166 type = TYPE_DATE;
167 len = 19 + ((prec) ? (prec+1) : 0);
168 v = (tn && toupper(tn[0]) == 'T') ? 'S' : 'E';
169 break;
170 case 91: // DATE, YEAR
171 type = TYPE_DATE;
172
173 if (!tn || toupper(tn[0]) != 'Y') {
174 len = 10;
175 v = 'D';
176 } else {
177 len = 4;
178 v = 'Y';
179 } // endif len
180
181 break;
182 case 92: // TIME
183 type = TYPE_DATE;
184 len = 8 + ((prec) ? (prec + 1) : 0);
185 v = 'T';
186 break;
187 case -5: // BIGINT
188 type = TYPE_BIGINT;
189 break;
190 case 1111: // UNKNOWN or UUID
191 if (!tn || !stricmp(tn, "UUID")) {
192 type = TYPE_STRING;
193 len = 36;
194 break;
195 } // endif tn
196
197 // Pass through
198 case 0: // NULL
199 case -2: // BINARY
200 case -4: // LONGVARBINARY
201 case 70: // DATALINK
202 case 2000: // JAVA_OBJECT
203 case 2001: // DISTINCT
204 case 2002: // STRUCT
205 case 2003: // ARRAY
206 case 2004: // BLOB
207 case 2005: // CLOB
208 case 2006: // REF
209 case 2009: // SQLXML
210 case 2011: // NCLOB
211 default:
212 type = TYPE_ERROR;
213 len = 0;
214 } // endswitch type
215
216 return type;
217} // end of TranslateJDBCType
218
219 /***********************************************************************/
220 /* A helper class to split an optionally qualified table name into */
221 /* components. */
222 /* These formats are understood: */
223 /* "CatalogName.SchemaName.TableName" */
224 /* "SchemaName.TableName" */
225 /* "TableName" */
226 /***********************************************************************/
227class SQLQualifiedName {
228 static const uint max_parts = 3; // Catalog.Schema.Table
229 MYSQL_LEX_STRING m_part[max_parts];
230 char m_buf[512];
231
232 void lex_string_set(MYSQL_LEX_STRING *S, char *str, size_t length)
233 {
234 S->str = str;
235 S->length = length;
236 } // end of lex_string_set
237
238 void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs)
239 {
240 DBUG_ASSERT(offs <= S->length);
241 S->str += offs;
242 S->length -= offs;
243 } // end of lex_string_shorten_down
244
245 /*********************************************************************/
246 /* Find the rightmost '.' delimiter and return the length */
247 /* of the qualifier, including the rightmost '.' delimier. */
248 /* For example, for the string {"a.b.c",5} it will return 4, */
249 /* which is the length of the qualifier "a.b." */
250 /*********************************************************************/
251 size_t lex_string_find_qualifier(MYSQL_LEX_STRING *S)
252 {
253 size_t i;
254 for (i = S->length; i > 0; i--)
255 {
256 if (S->str[i - 1] == '.')
257 {
258 S->str[i - 1] = '\0';
259 return i;
260 }
261 }
262 return 0;
263 } // end of lex_string_find_qualifier
264
265public:
266 /*********************************************************************/
267 /* Initialize to the given optionally qualified name. */
268 /* NULL pointer in "name" is supported. */
269 /* name qualifier has precedence over schema. */
270 /*********************************************************************/
271 SQLQualifiedName(JCATPARM *cap)
272 {
273 const char *name = (const char *)cap->Tab;
274 char *db = (char *)cap->DB;
275 size_t len, i;
276
277 // Initialize the parts
278 for (i = 0; i < max_parts; i++)
279 lex_string_set(&m_part[i], NULL, 0);
280
281 if (name) {
282 // Initialize the first (rightmost) part
283 lex_string_set(&m_part[0], m_buf,
284 strmake(m_buf, name, sizeof(m_buf) - 1) - m_buf);
285
286 // Initialize the other parts, if exist.
287 for (i = 1; i < max_parts; i++) {
288 if (!(len = lex_string_find_qualifier(&m_part[i - 1])))
289 break;
290
291 lex_string_set(&m_part[i], m_part[i - 1].str, len - 1);
292 lex_string_shorten_down(&m_part[i - 1], len);
293 } // endfor i
294
295 } // endif name
296
297 // If it was not specified, set schema as the passed db name
298 if (db && !m_part[1].length)
299 lex_string_set(&m_part[1], db, strlen(db));
300
301 } // end of SQLQualifiedName
302
303 char *ptr(uint i)
304 {
305 DBUG_ASSERT(i < max_parts);
306 return (char *)(m_part[i].length ? m_part[i].str : NULL);
307 } // end of ptr
308
309 size_t length(uint i)
310 {
311 DBUG_ASSERT(i < max_parts);
312 return m_part[i].length;
313 } // end of length
314
315}; // end of class SQLQualifiedName
316
317/***********************************************************************/
318/* Allocate the structure used to refer to the result set. */
319/***********************************************************************/
320static JCATPARM *AllocCatInfo(PGLOBAL g, JCATINFO fid, PCSZ db,
321 PCSZ tab, PQRYRES qrp)
322{
323 JCATPARM *cap;
324
325#if defined(_DEBUG)
326 assert(qrp);
327#endif
328
329 if ((cap = (JCATPARM *)PlgDBSubAlloc(g, NULL, sizeof(JCATPARM)))) {
330 memset(cap, 0, sizeof(JCATPARM));
331 cap->Id = fid;
332 cap->Qrp = qrp;
333 cap->DB = db;
334 cap->Tab = tab;
335 } // endif cap
336
337 return cap;
338} // end of AllocCatInfo
339
340/***********************************************************************/
341/* JDBCColumns: constructs the result blocks containing all columns */
342/* of a JDBC table that will be retrieved by GetData commands. */
343/***********************************************************************/
344PQRYRES JDBCColumns(PGLOBAL g, PCSZ db, PCSZ table, PCSZ colpat,
345 int maxres, bool info, PJPARM sjp)
346{
347 int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING,
348 TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT,
349 TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
350 XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_TABNAME, FLD_NAME,
351 FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH,
352 FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM};
353 unsigned int length[] = {0, 0, 0, 0, 6, 0, 10, 10, 6, 6, 6, 0};
354 bool b[] = {true, true, false, false, false, false, false, false, true, true, false, true};
355 int i, n, ncol = 12;
356 PCOLRES crp;
357 PQRYRES qrp;
358 JCATPARM *cap;
359 JDBConn *jcp = NULL;
360
361 /************************************************************************/
362 /* Do an evaluation of the result size. */
363 /************************************************************************/
364 if (!info) {
365 jcp = new(g)JDBConn(g, NULL);
366
367 if (jcp->Connect(sjp)) // openReadOnly + noJDBCdialog
368 return NULL;
369
370 if (table && !strchr(table, '%')) {
371 // We fix a MySQL limit because some data sources return 32767
372 n = jcp->GetMaxValue(1); // MAX_COLUMNS_IN_TABLE)
373 maxres = (n > 0) ? MY_MIN(n, 4096) : 4096;
374 } else if (!maxres)
375 maxres = 20000;
376
377 // n = jcp->GetMaxValue(2); MAX_CATALOG_NAME_LEN
378 // length[0] = (n) ? (n + 1) : 0;
379 // n = jcp->GetMaxValue(3); MAX_SCHEMA_NAME_LEN
380 // length[1] = (n) ? (n + 1) : 0;
381 // n = jcp->GetMaxValue(4); MAX_TABLE_NAME_LEN
382 // length[2] = (n) ? (n + 1) : 0;
383 n = jcp->GetMaxValue(5); // MAX_COLUMN_NAME_LEN
384 length[3] = (n > 0) ? (n + 1) : 128;
385 } else { // Info table
386 maxres = 0;
387 length[0] = 128;
388 length[1] = 128;
389 length[2] = 128;
390 length[3] = 128;
391 length[5] = 30;
392 length[11] = 255;
393 } // endif jcp
394
395 if (trace(1))
396 htrc("JDBCColumns: max=%d len=%d,%d,%d,%d\n",
397 maxres, length[0], length[1], length[2], length[3]);
398
399 /************************************************************************/
400 /* Allocate the structures used to refer to the result set. */
401 /************************************************************************/
402 qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS,
403 buftyp, fldtyp, length, false, true);
404
405 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
406 if (b[i])
407 crp->Kdata->SetNullable(true);
408
409 if (info || !qrp) // Info table
410 return qrp;
411
412 if (trace(1))
413 htrc("Getting col results ncol=%d\n", qrp->Nbcol);
414
415 if (!(cap = AllocCatInfo(g, JCAT_COL, db, table, qrp)))
416 return NULL;
417
418 // Colpat cannot be null or empty for some drivers
419 cap->Pat = (colpat && *colpat) ? colpat : PlugDup(g, "%");
420
421 /************************************************************************/
422 /* Now get the results into blocks. */
423 /************************************************************************/
424 if ((n = jcp->GetCatInfo(cap)) >= 0) {
425 qrp->Nblin = n;
426 // ResetNullValues(cap);
427
428 if (trace(1))
429 htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
430
431 } else
432 qrp = NULL;
433
434 /* Cleanup */
435 jcp->Close();
436
437 /************************************************************************/
438 /* Return the result pointer for use by GetData routines. */
439 /************************************************************************/
440 return qrp;
441} // end of JDBCColumns
442
443/**************************************************************************/
444/* JDBCSrcCols: constructs the result blocks containing the */
445/* description of all the columns of a Srcdef option. */
446/**************************************************************************/
447PQRYRES JDBCSrcCols(PGLOBAL g, PCSZ src, PJPARM sjp)
448{
449 char *sqry;
450 PQRYRES qrp;
451 JDBConn *jcp = new(g)JDBConn(g, NULL);
452
453 if (jcp->Connect(sjp))
454 return NULL;
455
456 if (strstr(src, "%s")) {
457 // Place holder for an eventual where clause
458 sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 2);
459 sprintf(sqry, src, "1=1"); // dummy where clause
460 } else
461 sqry = (char*)src;
462
463 qrp = jcp->GetMetaData(g, sqry);
464 jcp->Close();
465 return qrp;
466} // end of JDBCSrcCols
467
468/**************************************************************************/
469/* JDBCTables: constructs the result blocks containing all tables in */
470/* an JDBC database that will be retrieved by GetData commands. */
471/**************************************************************************/
472PQRYRES JDBCTables(PGLOBAL g, PCSZ db, PCSZ tabpat, PCSZ tabtyp,
473 int maxres, bool info, PJPARM sjp)
474{
475 int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
476 TYPE_STRING, TYPE_STRING};
477 XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_NAME, FLD_TYPE, FLD_REM};
478 unsigned int length[] = {0, 0, 0, 16, 0};
479 bool b[] = {true, true, false, false, true};
480 int i, n, ncol = 5;
481 PCOLRES crp;
482 PQRYRES qrp;
483 JCATPARM *cap;
484 JDBConn *jcp = NULL;
485
486 /************************************************************************/
487 /* Do an evaluation of the result size. */
488 /************************************************************************/
489 if (!info) {
490 /**********************************************************************/
491 /* Open the connection with the JDBC data source. */
492 /**********************************************************************/
493 jcp = new(g)JDBConn(g, NULL);
494
495 if (jcp->Connect(sjp))
496 return NULL;
497
498 if (!maxres)
499 maxres = 10000; // This is completely arbitrary
500
501 n = jcp->GetMaxValue(2); // Max catalog name length
502
503// if (n < 0)
504// return NULL;
505
506 length[0] = (n > 0) ? (n + 1) : 0;
507 n = jcp->GetMaxValue(3); // Max schema name length
508 length[1] = (n > 0) ? (n + 1) : 0;
509 n = jcp->GetMaxValue(4); // Max table name length
510 length[2] = (n > 0) ? (n + 1) : 128;
511 } else {
512 maxres = 0;
513 length[0] = 128;
514 length[1] = 128;
515 length[2] = 128;
516 length[4] = 255;
517 } // endif info
518
519 if (trace(1))
520 htrc("JDBCTables: max=%d len=%d,%d\n", maxres, length[0], length[1]);
521
522 /************************************************************************/
523 /* Allocate the structures used to refer to the result set. */
524 /************************************************************************/
525 qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES, buftyp,
526 fldtyp, length, false, true);
527
528 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
529 if (b[i])
530 crp->Kdata->SetNullable(true);
531
532 if (info || !qrp)
533 return qrp;
534
535 // Tabpat cannot be null or empty for some drivers
536 if (!(cap = AllocCatInfo(g, JCAT_TAB, db,
537 (tabpat && *tabpat) ? tabpat : PlugDup(g, "%"), qrp)))
538 return NULL;
539
540 cap->Pat = tabtyp;
541
542 if (trace(1))
543 htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol);
544
545 /************************************************************************/
546 /* Now get the results into blocks. */
547 /************************************************************************/
548 if ((n = jcp->GetCatInfo(cap)) >= 0) {
549 qrp->Nblin = n;
550 // ResetNullValues(cap);
551
552 if (trace(1))
553 htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
554
555 } else
556 qrp = NULL;
557
558 /************************************************************************/
559 /* Close any local connection. */
560 /************************************************************************/
561 jcp->Close();
562
563 /************************************************************************/
564 /* Return the result pointer for use by GetData routines. */
565 /************************************************************************/
566 return qrp;
567} // end of JDBCTables
568
569/*************************************************************************/
570/* JDBCDrivers: constructs the result blocks containing all JDBC */
571/* drivers available on the local host. */
572/* Called with info=true to have result column names. */
573/*************************************************************************/
574PQRYRES JDBCDrivers(PGLOBAL g, int maxres, bool info)
575{
576 int buftyp[] ={TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING};
577 XFLD fldtyp[] ={FLD_NAME, FLD_EXTRA, FLD_DEFAULT, FLD_REM };
578 unsigned int length[] ={ 128, 32, 4, 256 };
579 bool b[] ={ false, false, false, true };
580 int i, ncol = 4;
581 PCOLRES crp;
582 PQRYRES qrp;
583 JDBConn *jcp = NULL;
584
585 /************************************************************************/
586 /* Do an evaluation of the result size. */
587 /************************************************************************/
588 if (!info) {
589 jcp = new(g) JDBConn(g, NULL);
590
591 if (jcp->Open(g) != RC_OK)
592 return NULL;
593
594 if (!maxres)
595 maxres = 256; // Estimated max number of drivers
596
597 } else
598 maxres = 0;
599
600 if (trace(1))
601 htrc("JDBCDrivers: max=%d len=%d\n", maxres, length[0]);
602
603 /************************************************************************/
604 /* Allocate the structures used to refer to the result set. */
605 /************************************************************************/
606 qrp = PlgAllocResult(g, ncol, maxres, 0, buftyp, fldtyp, length, false, true);
607
608 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next) {
609 if (b[i])
610 crp->Kdata->SetNullable(true);
611
612 switch (i) {
613 case 0: crp->Name = "Name"; break;
614 case 1: crp->Name = "Version"; break;
615 case 2: crp->Name = "Compliant"; break;
616 case 3: crp->Name = "Description"; break;
617 } // endswitch
618
619 } // endfor i
620
621 /************************************************************************/
622 /* Now get the results into blocks. */
623 /************************************************************************/
624 if (!info && qrp && jcp->GetDrivers(qrp))
625 qrp = NULL;
626
627 if (!info)
628 jcp->Close();
629
630 /************************************************************************/
631 /* Return the result pointer for use by GetData routines. */
632 /************************************************************************/
633 return qrp;
634} // end of JDBCDrivers
635
636/***********************************************************************/
637/* JDBConn construction/destruction. */
638/***********************************************************************/
639JDBConn::JDBConn(PGLOBAL g, PCSZ wrapper) : JAVAConn(g, wrapper)
640{
641 xqid = xuid = xid = grs = readid = fetchid = typid = errid = nullptr;
642 prepid = xpid = pcid = nullptr;
643 chrfldid = intfldid = dblfldid = fltfldid = bigfldid = nullptr;
644 objfldid = datfldid = timfldid = tspfldid = uidfldid = nullptr;
645 DiscFunc = "JdbcDisconnect";
646 m_Ncol = 0;
647 m_Aff = 0;
648 //m_Rows = 0;
649 m_Fetch = 0;
650 m_RowsetSize = 0;
651 m_Updatable = true;
652 m_Transact = false;
653 m_Scrollable = false;
654 m_Full = false;
655 m_Opened = false;
656 m_IDQuoteChar[0] = '"';
657 m_IDQuoteChar[1] = 0;
658} // end of JDBConn
659
660/***********************************************************************/
661/* Search for UUID columns. */
662/***********************************************************************/
663bool JDBConn::SetUUID(PGLOBAL g, PTDBJDBC tjp)
664{
665 int ncol, ctyp;
666 bool brc = true;
667 PCSZ fnc = "GetColumns";
668 PCOL colp;
669 JCATPARM *cap;
670 //jint jtyp;
671 jboolean rc = false;
672 jobjectArray parms;
673 jmethodID catid = nullptr;
674
675 if (gmID(g, catid, fnc, "([Ljava/lang/String;)I"))
676 return true;
677 else if (gmID(g, intfldid, "IntField", "(ILjava/lang/String;)I"))
678 return true;
679 else if (gmID(g, readid, "ReadNext", "()I"))
680 return true;
681
682 cap = AllocCatInfo(g, JCAT_COL, tjp->Schema, tjp->TableName, NULL);
683 SQLQualifiedName name(cap);
684
685 // Build the java string array
686 parms = env->NewObjectArray(4, env->FindClass("java/lang/String"), NULL);
687 env->SetObjectArrayElement(parms, 0, env->NewStringUTF(name.ptr(2)));
688 env->SetObjectArrayElement(parms, 1, env->NewStringUTF(name.ptr(1)));
689 env->SetObjectArrayElement(parms, 2, env->NewStringUTF(name.ptr(0)));
690
691 for (colp = tjp->GetColumns(); colp; colp = colp->GetNext()) {
692 env->SetObjectArrayElement(parms, 3, env->NewStringUTF(colp->GetName()));
693 ncol = env->CallIntMethod(job, catid, parms);
694
695 if (Check(ncol)) {
696 sprintf(g->Message, "%s: %s", fnc, Msg);
697 goto err;
698 } // endif Check
699
700 rc = env->CallBooleanMethod(job, readid);
701
702 if (Check(rc)) {
703 sprintf(g->Message, "ReadNext: %s", Msg);
704 goto err;
705 } else if (rc == 0) {
706 sprintf(g->Message, "table %s does not exist", tjp->TableName);
707 goto err;
708 } // endif rc
709
710 // Returns 666 is case of error
711 //jtyp = env->CallIntMethod(job, typid, 5, nullptr);
712
713 //if (Check((jtyp == 666) ? -1 : 1)) {
714 // sprintf(g->Message, "Getting jtyp: %s", Msg);
715 // goto err;
716 //} // endif ctyp
717
718 ctyp = (int)env->CallIntMethod(job, intfldid, 5, nullptr);
719
720 if (Check(ctyp)) {
721 sprintf(g->Message, "Getting ctyp: %s", Msg);
722 goto err;
723 } // endif ctyp
724
725 if (ctyp == 1111)
726 ((PJDBCCOL)colp)->uuid = true;
727
728 } // endfor colp
729
730 // All is Ok
731 brc = false;
732
733 err:
734 // Not used anymore
735 env->DeleteLocalRef(parms);
736 return brc;
737} // end of SetUUID
738
739/***********************************************************************/
740/* Utility routine. */
741/***********************************************************************/
742int JDBConn::GetMaxValue(int n)
743{
744 jint m;
745 jmethodID maxid = nullptr;
746
747 if (gmID(m_G, maxid, "GetMaxValue", "(I)I"))
748 return -1;
749
750 // call method
751 if (Check(m = env->CallIntMethod(job, maxid, n)))
752 htrc("GetMaxValue: %s", Msg);
753
754 return (int)m;
755} // end of GetMaxValue
756
757/***********************************************************************/
758/* AddJars: add some jar file to the Class path. */
759/***********************************************************************/
760void JDBConn::AddJars(PSTRG jpop, char sep)
761{
762#if defined(DEVELOPMENT)
763 jpop->Append(
764 ";C:/Jconnectors/postgresql-9.4.1208.jar"
765 ";C:/Oracle/ojdbc7.jar"
766 ";C:/Apache/commons-dbcp2-2.1.1/commons-dbcp2-2.1.1.jar"
767 ";C:/Apache/commons-pool2-2.4.2/commons-pool2-2.4.2.jar"
768 ";C:/Apache/commons-logging-1.2/commons-logging-1.2.jar"
769 ";C:/Jconnectors/mysql-connector-java-6.0.2-bin.jar"
770 ";C:/Jconnectors/mariadb-java-client-2.0.1.jar"
771 ";C:/Jconnectors/sqljdbc42.jar");
772#endif // DEVELOPMENT
773} // end of AddJars
774
775/***********************************************************************/
776/* Connect: connect to a data source. */
777/***********************************************************************/
778bool JDBConn::Connect(PJPARM sop)
779{
780 int irc = RC_FX;
781 bool err = false;
782 jint rc;
783 jboolean jt = (trace(1));
784 PGLOBAL& g = m_G;
785
786 /*******************************************************************/
787 /* Create or attach a JVM. */
788 /*******************************************************************/
789 if (Open(g))
790 return true;
791
792 if (!sop) // DRIVER catalog table
793 return false;
794
795 jmethodID cid = nullptr;
796
797 if (gmID(g, cid, "JdbcConnect", "([Ljava/lang/String;IZ)I"))
798 return true;
799
800 // Build the java string array
801 jobjectArray parms = env->NewObjectArray(4, // constructs java array of 4
802 env->FindClass("java/lang/String"), NULL); // Strings
803
804 m_Scrollable = sop->Scrollable;
805 m_RowsetSize = sop->Fsize;
806 //m_LoginTimeout = sop->Cto;
807 //m_QueryTimeout = sop->Qto;
808 //m_UseCnc = sop->UseCnc;
809
810 // change some elements
811 if (sop->Driver)
812 env->SetObjectArrayElement(parms, 0, env->NewStringUTF(sop->Driver));
813
814 if (sop->Url)
815 env->SetObjectArrayElement(parms, 1, env->NewStringUTF(sop->Url));
816
817 if (sop->User)
818 env->SetObjectArrayElement(parms, 2, env->NewStringUTF(sop->User));
819
820 if (sop->Pwd)
821 env->SetObjectArrayElement(parms, 3, env->NewStringUTF(sop->Pwd));
822
823 // call method
824 rc = env->CallIntMethod(job, cid, parms, m_RowsetSize, m_Scrollable);
825 err = Check(rc);
826 env->DeleteLocalRef(parms); // Not used anymore
827
828 if (err) {
829 sprintf(g->Message, "Connecting: %s rc=%d", Msg, (int)rc);
830 return true;
831 } // endif Msg
832
833 jmethodID qcid = nullptr;
834
835 if (!gmID(g, qcid, "GetQuoteString", "()Ljava/lang/String;")) {
836 jstring s = (jstring)env->CallObjectMethod(job, qcid);
837
838 if (s != nullptr) {
839 char *qch = (char*)env->GetStringUTFChars(s, (jboolean)false);
840 m_IDQuoteChar[0] = *qch;
841 } else {
842 s = (jstring)env->CallObjectMethod(job, errid);
843 Msg = (char*)env->GetStringUTFChars(s, (jboolean)false);
844 } // endif s
845
846 } // endif qcid
847
848 if (gmID(g, typid, "ColumnType", "(ILjava/lang/String;)I"))
849 return true;
850 else
851 m_Connected = true;
852
853 return false;
854} // end of Connect
855
856
857/***********************************************************************/
858/* Execute an SQL command. */
859/***********************************************************************/
860int JDBConn::ExecuteCommand(PCSZ sql)
861{
862 int rc;
863 jint n;
864 jstring qry;
865 PGLOBAL& g = m_G;
866
867 // Get the methods used to execute a query and get the result
868 if (gmID(g, xid, "Execute", "(Ljava/lang/String;)I") ||
869 gmID(g, grs, "GetResult", "()I"))
870 return RC_FX;
871
872 qry = env->NewStringUTF(sql);
873 n = env->CallIntMethod(job, xid, qry);
874 env->DeleteLocalRef(qry);
875
876 if (Check(n)) {
877 sprintf(g->Message, "Execute: %s", Msg);
878 return RC_FX;
879 } // endif n
880
881 m_Ncol = env->CallIntMethod(job, grs);
882
883 if (Check(m_Ncol)) {
884 sprintf(g->Message, "GetResult: %s", Msg);
885 rc = RC_FX;
886 } else if (m_Ncol) {
887 strcpy(g->Message, "Result set column number");
888 rc = RC_OK; // A result set was returned
889 } else {
890 m_Aff = (int)n; // Affected rows
891 strcpy(g->Message, "Affected rows");
892 rc = RC_NF;
893 } // endif ncol
894
895 return rc;
896} // end of ExecuteCommand
897
898/***********************************************************************/
899/* Fetch next row. */
900/***********************************************************************/
901int JDBConn::Fetch(int pos)
902{
903 jint rc = JNI_ERR;
904 PGLOBAL& g = m_G;
905
906 if (m_Full) // Result set has one row
907 return 1;
908
909 if (pos) {
910 if (!m_Scrollable) {
911 strcpy(g->Message, "Cannot fetch(pos) if FORWARD ONLY");
912 return rc;
913 } else if (gmID(m_G, fetchid, "Fetch", "(I)Z"))
914 return rc;
915
916 if (env->CallBooleanMethod(job, fetchid, pos))
917 rc = m_Rows;
918
919 } else {
920 if (gmID(g, readid, "ReadNext", "()I"))
921 return rc;
922
923 rc = env->CallBooleanMethod(job, readid);
924
925 if (!Check(rc)) {
926 if (rc == 0)
927 m_Full = (m_Fetch == 1);
928 else
929 m_Fetch++;
930
931 m_Rows += (int)rc;
932 } else
933 sprintf(g->Message, "Fetch: %s", Msg);
934
935 } // endif pos
936
937 return (int)rc;
938} // end of Fetch
939
940/***********************************************************************/
941/* Restart from beginning of result set */
942/***********************************************************************/
943int JDBConn::Rewind(PCSZ sql)
944{
945 int rbuf = -1;
946
947 if (m_Full)
948 rbuf = m_Rows; // No need to "rewind"
949 else if (m_Scrollable) {
950 if (gmID(m_G, fetchid, "Fetch", "(I)Z"))
951 return -1;
952
953 jboolean b = env->CallBooleanMethod(job, fetchid, 0);
954
955 rbuf = m_Rows;
956 } else if (ExecuteCommand(sql) != RC_FX)
957 rbuf = 0;
958
959 return rbuf;
960} // end of Rewind
961
962/***********************************************************************/
963/* Retrieve and set the column value from the result set. */
964/***********************************************************************/
965void JDBConn::SetColumnValue(int rank, PSZ name, PVAL val)
966{
967 const char *field;
968 PGLOBAL& g = m_G;
969 jint ctyp;
970 jstring cn, jn = nullptr;
971 jobject jb = nullptr;
972
973 if (rank == 0)
974 if (!name || (jn = env->NewStringUTF(name)) == nullptr) {
975 sprintf(g->Message, "Fail to allocate jstring %s", SVP(name));
976 throw (int)TYPE_AM_JDBC;
977 } // endif name
978
979 // Returns 666 is case of error
980 ctyp = env->CallIntMethod(job, typid, rank, jn);
981
982 if (Check((ctyp == 666) ? -1 : 1)) {
983 sprintf(g->Message, "Getting ctyp: %s", Msg);
984 throw (int)TYPE_AM_JDBC;
985 } // endif Check
986
987 if (val->GetNullable())
988 if (!gmID(g, objfldid, "ObjectField", "(ILjava/lang/String;)Ljava/lang/Object;")) {
989 jb = env->CallObjectMethod(job, objfldid, (jint)rank, jn);
990
991 if (Check(0)) {
992 sprintf(g->Message, "Getting jp: %s", Msg);
993 throw (int)TYPE_AM_JDBC;
994 } // endif Check
995
996 if (jb == nullptr) {
997 val->Reset();
998 val->SetNull(true);
999 goto chk;
1000 } // endif job
1001
1002 } // endif objfldid
1003
1004 switch (ctyp) {
1005 case 12: // VARCHAR
1006 case -9: // NVARCHAR
1007 case -1: // LONGVARCHAR, TEXT
1008 case 1: // CHAR
1009 case -15: // NCHAR
1010 case -16: // LONGNVARCHAR, NTEXT
1011 case 3: // DECIMAL
1012 case -8: // ROWID
1013 if (jb && ctyp != 3)
1014 cn = (jstring)jb;
1015 else if (!gmID(g, chrfldid, "StringField", "(ILjava/lang/String;)Ljava/lang/String;"))
1016 cn = (jstring)env->CallObjectMethod(job, chrfldid, (jint)rank, jn);
1017 else
1018 cn = nullptr;
1019
1020 if (cn) {
1021 field = env->GetStringUTFChars(cn, (jboolean)false);
1022 val->SetValue_psz((PSZ)field);
1023 } else
1024 val->Reset();
1025
1026 break;
1027 case 4: // INTEGER
1028 case 5: // SMALLINT
1029 case -6: // TINYINT
1030 case 16: // BOOLEAN
1031 case -7: // BIT
1032 if (!gmID(g, intfldid, "IntField", "(ILjava/lang/String;)I"))
1033 val->SetValue((int)env->CallIntMethod(job, intfldid, rank, jn));
1034 else
1035 val->Reset();
1036
1037 break;
1038 case 8: // DOUBLE
1039 case 2: // NUMERIC
1040//case 3: // DECIMAL
1041 if (!gmID(g, dblfldid, "DoubleField", "(ILjava/lang/String;)D"))
1042 val->SetValue((double)env->CallDoubleMethod(job, dblfldid, rank, jn));
1043 else
1044 val->Reset();
1045
1046 break;
1047 case 7: // REAL
1048 case 6: // FLOAT
1049 if (!gmID(g, fltfldid, "FloatField", "(ILjava/lang/String;)F"))
1050 val->SetValue((float)env->CallFloatMethod(job, fltfldid, rank, jn));
1051 else
1052 val->Reset();
1053
1054 break;
1055 case 91: // DATE
1056 if (!gmID(g, datfldid, "DateField", "(ILjava/lang/String;)I")) {
1057 val->SetValue((int)env->CallIntMethod(job, datfldid, (jint)rank, jn));
1058 } else
1059 val->Reset();
1060
1061 break;
1062 case 92: // TIME
1063 if (!gmID(g, timfldid, "TimeField", "(ILjava/lang/String;)I")) {
1064 val->SetValue((int)env->CallIntMethod(job, timfldid, (jint)rank, jn));
1065 } else
1066 val->Reset();
1067
1068 break;
1069 case 93: // TIMESTAMP
1070 if (!gmID(g, tspfldid, "TimestampField", "(ILjava/lang/String;)I")) {
1071 val->SetValue((int)env->CallIntMethod(job, tspfldid, (jint)rank, jn));
1072 } else
1073 val->Reset();
1074
1075 break;
1076 case -5: // BIGINT
1077 if (!gmID(g, bigfldid, "BigintField", "(ILjava/lang/String;)J"))
1078 val->SetValue((long long)env->CallLongMethod(job, bigfldid, (jint)rank, jn));
1079 else
1080 val->Reset();
1081
1082 break;
1083 /* case java.sql.Types.SMALLINT:
1084 System.out.print(jdi.IntField(i));
1085 break;
1086 case java.sql.Types.BOOLEAN:
1087 System.out.print(jdi.BooleanField(i)); */
1088 case 1111: // UUID
1089 if (!gmID(g, uidfldid, "UuidField", "(ILjava/lang/String;)Ljava/lang/String;"))
1090 cn = (jstring)env->CallObjectMethod(job, uidfldid, (jint)rank, jn);
1091 else
1092 cn = nullptr;
1093
1094 if (cn) {
1095 const char *field = env->GetStringUTFChars(cn, (jboolean)false);
1096 val->SetValue_psz((PSZ)field);
1097 } else
1098 val->Reset();
1099
1100 break;
1101 case 0: // NULL
1102 val->SetNull(true);
1103 // passthru
1104 default:
1105 val->Reset();
1106 } // endswitch Type
1107
1108 chk:
1109 if (Check()) {
1110 if (rank == 0)
1111 env->DeleteLocalRef(jn);
1112
1113 sprintf(g->Message, "SetColumnValue: %s rank=%d ctyp=%d", Msg, rank, (int)ctyp);
1114 throw (int)TYPE_AM_JDBC;
1115 } // endif Check
1116
1117 if (rank == 0)
1118 env->DeleteLocalRef(jn);
1119
1120} // end of SetColumnValue
1121
1122/***********************************************************************/
1123/* Prepare an SQL statement for insert. */
1124/***********************************************************************/
1125bool JDBConn::PrepareSQL(PCSZ sql)
1126{
1127 bool b = true;
1128 PGLOBAL& g = m_G;
1129
1130 if (!gmID(g, prepid, "CreatePrepStmt", "(Ljava/lang/String;)I")) {
1131 // Create the prepared statement
1132 jstring qry = env->NewStringUTF(sql);
1133
1134 if (Check(env->CallBooleanMethod(job, prepid, qry)))
1135 sprintf(g->Message, "CreatePrepStmt: %s", Msg);
1136 else
1137 b = false;
1138
1139 env->DeleteLocalRef(qry);
1140 } // endif prepid
1141
1142 return b;
1143} // end of PrepareSQL
1144
1145/***********************************************************************/
1146/* Execute an SQL query that returns a result set. */
1147/***********************************************************************/
1148int JDBConn::ExecuteQuery(PCSZ sql)
1149{
1150 int rc = RC_FX;
1151 jint ncol;
1152 jstring qry;
1153 PGLOBAL& g = m_G;
1154
1155 // Get the methods used to execute a query and get the result
1156 if (!gmID(g, xqid, "ExecuteQuery", "(Ljava/lang/String;)I")) {
1157 qry = env->NewStringUTF(sql);
1158 ncol = env->CallIntMethod(job, xqid, qry);
1159
1160 if (!Check(ncol)) {
1161 m_Ncol = (int)ncol;
1162 m_Aff = 0; // Affected rows
1163 rc = RC_OK;
1164 } else
1165 sprintf(g->Message, "ExecuteQuery: %s", Msg);
1166
1167 env->DeleteLocalRef(qry);
1168 } // endif xqid
1169
1170 return rc;
1171} // end of ExecuteQuery
1172
1173/***********************************************************************/
1174/* Execute an SQL query and get the affected rows. */
1175/***********************************************************************/
1176int JDBConn::ExecuteUpdate(PCSZ sql)
1177{
1178 int rc = RC_FX;
1179 jint n;
1180 jstring qry;
1181 PGLOBAL& g = m_G;
1182
1183 // Get the methods used to execute a query and get the affected rows
1184 if (!gmID(g, xuid, "ExecuteUpdate", "(Ljava/lang/String;)I")) {
1185 qry = env->NewStringUTF(sql);
1186 n = env->CallIntMethod(job, xuid, qry);
1187
1188 if (!Check(n)) {
1189 m_Ncol = 0;
1190 m_Aff = (int)n; // Affected rows
1191 rc = RC_OK;
1192 } else
1193 sprintf(g->Message, "ExecuteUpdate: %s n=%d", Msg, n);
1194
1195 env->DeleteLocalRef(qry);
1196 } // endif xuid
1197
1198 return rc;
1199} // end of ExecuteUpdate
1200
1201/***********************************************************************/
1202/* Get the number of lines of the result set. */
1203/***********************************************************************/
1204int JDBConn::GetResultSize(PCSZ sql, PCOL colp)
1205{
1206 int rc, n = 0;
1207
1208 if ((rc = ExecuteQuery(sql)) != RC_OK)
1209 return -1;
1210
1211 if ((rc = Fetch()) > 0)
1212 SetColumnValue(1, NULL, colp->GetValue());
1213 else
1214 return -2;
1215
1216 if ((rc = Fetch()) != 0)
1217 return -3;
1218
1219 m_Full = false;
1220 return colp->GetIntValue();
1221} // end of GetResultSize
1222
1223/***********************************************************************/
1224/* Execute a prepared statement. */
1225/***********************************************************************/
1226int JDBConn::ExecuteSQL(void)
1227{
1228 int rc = RC_FX;
1229 PGLOBAL& g = m_G;
1230
1231 // Get the methods used to execute a prepared statement
1232 if (!gmID(g, xpid, "ExecutePrep", "()I")) {
1233 jint n = env->CallIntMethod(job, xpid);
1234
1235 if (n == -3)
1236 strcpy(g->Message, "SQL statement is not prepared");
1237 else if (Check(n))
1238 sprintf(g->Message, "ExecutePrep: %s", Msg);
1239 else {
1240 m_Aff = (int)n;
1241 rc = RC_OK;
1242 } // endswitch n
1243
1244 } // endif xpid
1245
1246 return rc;
1247} // end of ExecuteSQL
1248
1249/***********************************************************************/
1250/* Set a parameter for inserting. */
1251/***********************************************************************/
1252bool JDBConn::SetParam(JDBCCOL *colp)
1253{
1254 PGLOBAL& g = m_G;
1255 bool rc = false;
1256 PVAL val = colp->GetValue();
1257 jint n, jrc = 0, i = (jint)colp->GetRank();
1258 jshort s;
1259 jlong lg;
1260//jfloat f;
1261 jdouble d;
1262 jclass dat;
1263 jobject datobj;
1264 jstring jst = nullptr;
1265 jmethodID dtc, setid = nullptr;
1266
1267 if (val->GetNullable() && val->IsNull()) {
1268 if (gmID(g, setid, "SetNullParm", "(II)I"))
1269 return true;
1270
1271 jrc = env->CallIntMethod(job, setid, i,
1272 (colp->uuid ? 1111 : (jint)GetJDBCType(val->GetType())));
1273 } else if (colp->uuid) {
1274 if (gmID(g, setid, "SetUuidParm", "(ILjava/lang/String;)V"))
1275 return true;
1276
1277 jst = env->NewStringUTF(val->GetCharValue());
1278 env->CallVoidMethod(job, setid, i, jst);
1279 } else switch (val->GetType()) {
1280 case TYPE_STRING:
1281 if (gmID(g, setid, "SetStringParm", "(ILjava/lang/String;)V"))
1282 return true;
1283
1284 jst = env->NewStringUTF(val->GetCharValue());
1285 env->CallVoidMethod(job, setid, i, jst);
1286 break;
1287 case TYPE_INT:
1288 if (gmID(g, setid, "SetIntParm", "(II)V"))
1289 return true;
1290
1291 n = (jint)val->GetIntValue();
1292 env->CallVoidMethod(job, setid, i, n);
1293 break;
1294 case TYPE_TINY:
1295 case TYPE_SHORT:
1296 if (gmID(g, setid, "SetShortParm", "(IS)V"))
1297 return true;
1298
1299 s = (jshort)val->GetShortValue();
1300 env->CallVoidMethod(job, setid, i, s);
1301 break;
1302 case TYPE_BIGINT:
1303 if (gmID(g, setid, "SetBigintParm", "(IJ)V"))
1304 return true;
1305
1306 lg = (jlong)val->GetBigintValue();
1307 env->CallVoidMethod(job, setid, i, lg);
1308 break;
1309 case TYPE_DOUBLE:
1310 case TYPE_DECIM:
1311 if (gmID(g, setid, "SetDoubleParm", "(ID)V"))
1312 return true;
1313
1314 d = (jdouble)val->GetFloatValue();
1315 env->CallVoidMethod(job, setid, i, d);
1316 break;
1317 case TYPE_DATE:
1318 if ((dat = env->FindClass("java/sql/Timestamp")) == nullptr) {
1319 strcpy(g->Message, "Cannot find Timestamp class");
1320 return true;
1321 } else if (!(dtc = env->GetMethodID(dat, "<init>", "(J)V"))) {
1322 strcpy(g->Message, "Cannot find Timestamp class constructor");
1323 return true;
1324 } // endif's
1325
1326 lg = (jlong)val->GetBigintValue() * 1000;
1327
1328 if ((datobj = env->NewObject(dat, dtc, lg)) == nullptr) {
1329 strcpy(g->Message, "Cannot make Timestamp object");
1330 return true;
1331 } else if (gmID(g, setid, "SetTimestampParm", "(ILjava/sql/Timestamp;)V"))
1332 return true;
1333
1334 env->CallVoidMethod(job, setid, i, datobj);
1335 break;
1336 default:
1337 sprintf(g->Message, "Parm type %d not supported", val->GetType());
1338 return true;
1339 } // endswitch Type
1340
1341 if (Check(jrc)) {
1342 sprintf(g->Message, "SetParam: col=%s msg=%s", colp->GetName(), Msg);
1343 rc = true;
1344 } // endif msg
1345
1346 if (jst)
1347 env->DeleteLocalRef(jst);
1348
1349 return rc;
1350 } // end of SetParam
1351
1352 /***********************************************************************/
1353 /* Get the list of Drivers and set it in qrp. */
1354 /***********************************************************************/
1355 bool JDBConn::GetDrivers(PQRYRES qrp)
1356 {
1357 PSZ sval;
1358 int i, n, size;
1359 PCOLRES crp;
1360 jstring js;
1361 jmethodID gdid = nullptr;
1362
1363 if (gmID(m_G, gdid, "GetDrivers", "([Ljava/lang/String;I)I"))
1364 return true;
1365
1366 // Build the java string array
1367 jobjectArray s = env->NewObjectArray(4 * qrp->Maxres,
1368 env->FindClass("java/lang/String"), NULL);
1369
1370 size = env->CallIntMethod(job, gdid, s, qrp->Maxres);
1371
1372 for (i = 0, n = 0; i < size; i++) {
1373 crp = qrp->Colresp;
1374 js = (jstring)env->GetObjectArrayElement(s, n++);
1375 sval = (PSZ)env->GetStringUTFChars(js, 0);
1376 crp->Kdata->SetValue(sval, i);
1377 crp = crp->Next;
1378 js = (jstring)env->GetObjectArrayElement(s, n++);
1379 sval = (PSZ)env->GetStringUTFChars(js, 0);
1380 crp->Kdata->SetValue(sval, i);
1381 crp = crp->Next;
1382 js = (jstring)env->GetObjectArrayElement(s, n++);
1383 sval = (PSZ)env->GetStringUTFChars(js, 0);
1384 crp->Kdata->SetValue(sval, i);
1385 crp = crp->Next;
1386 js = (jstring)env->GetObjectArrayElement(s, n++);
1387 sval = (PSZ)env->GetStringUTFChars(js, 0);
1388 crp->Kdata->SetValue(sval, i);
1389 } // endfor i
1390
1391 // Not used anymore
1392 env->DeleteLocalRef(s);
1393
1394 qrp->Nblin = size;
1395 return false;
1396 } // end of GetDrivers
1397
1398 /**************************************************************************/
1399 /* GetMetaData: constructs the result blocks containing the */
1400 /* description of all the columns of an SQL command. */
1401 /**************************************************************************/
1402 PQRYRES JDBConn::GetMetaData(PGLOBAL g, PCSZ src)
1403 {
1404 static int buftyp[] = {TYPE_STRING, TYPE_INT, TYPE_INT,
1405 TYPE_INT, TYPE_INT};
1406 static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
1407 FLD_SCALE, FLD_NULL };
1408 static unsigned int length[] = {0, 6, 10, 6, 6};
1409 const char *name;
1410 int len, qcol = 5;
1411 PQRYRES qrp = NULL;
1412 PCOLRES crp;
1413 ushort i;
1414 jint *n = nullptr;
1415 jstring label;
1416 jmethodID colid = nullptr;
1417 int rc = ExecuteCommand(src);
1418
1419 if (rc == RC_NF) {
1420 strcpy(g->Message, "Srcdef is not returning a result set");
1421 return NULL;
1422 } else if ((rc) == RC_FX) {
1423 return NULL;
1424 } else if (m_Ncol == 0) {
1425 strcpy(g->Message, "Invalid Srcdef");
1426 return NULL;
1427 } // endif's
1428
1429 if (gmID(g, colid, "ColumnDesc", "(I[I)Ljava/lang/String;"))
1430 return NULL;
1431
1432 // Get max column name length
1433 len = GetMaxValue(5);
1434 length[0] = (len > 0) ? len + 1 : 128;
1435
1436 /************************************************************************/
1437 /* Allocate the structures used to refer to the result set. */
1438 /************************************************************************/
1439 if (!(qrp = PlgAllocResult(g, qcol, m_Ncol, IDS_COLUMNS + 3,
1440 buftyp, fldtyp, length, false, true)))
1441 return NULL;
1442
1443 // Some columns must be renamed
1444 for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
1445 switch (++i) {
1446 case 3: crp->Name = "Precision"; break;
1447 case 4: crp->Name = "Scale"; break;
1448 case 5: crp->Name = "Nullable"; break;
1449 } // endswitch i
1450
1451 // Build the java int array
1452 jintArray val = env->NewIntArray(4);
1453
1454 if (val == nullptr) {
1455 strcpy(m_G->Message, "Cannot allocate jint array");
1456 return NULL;
1457 } // endif colid
1458
1459 /************************************************************************/
1460 /* Now get the results into blocks. */
1461 /************************************************************************/
1462 for (i = 0; i < m_Ncol; i++) {
1463 if (!(label = (jstring)env->CallObjectMethod(job, colid, i + 1, val))) {
1464 if (Check())
1465 sprintf(g->Message, "ColumnDesc: %s", Msg);
1466 else
1467 strcpy(g->Message, "No result metadata");
1468
1469 env->ReleaseIntArrayElements(val, n, 0);
1470 return NULL;
1471 } // endif label
1472
1473 name = env->GetStringUTFChars(label, (jboolean)false);
1474 crp = qrp->Colresp; // Column_Name
1475 crp->Kdata->SetValue((char*)name, i);
1476 n = env->GetIntArrayElements(val, 0);
1477 crp = crp->Next; // Data_Type
1478 crp->Kdata->SetValue((int)n[0], i);
1479 crp = crp->Next; // Precision (length)
1480 crp->Kdata->SetValue((int)n[1], i);
1481 crp = crp->Next; // Scale
1482 crp->Kdata->SetValue((int)n[2], i);
1483 crp = crp->Next; // Nullable
1484 crp->Kdata->SetValue((int)n[3], i);
1485 qrp->Nblin++;
1486 } // endfor i
1487
1488 /* Cleanup */
1489 env->ReleaseIntArrayElements(val, n, 0);
1490
1491 /************************************************************************/
1492 /* Return the result pointer for use by GetData routines. */
1493 /************************************************************************/
1494 return qrp;
1495 } // end of GetMetaData
1496
1497 /***********************************************************************/
1498 /* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */
1499 /***********************************************************************/
1500 int JDBConn::GetCatInfo(JCATPARM *cap)
1501 {
1502 PGLOBAL& g = m_G;
1503// void *buffer;
1504 int i, ncol;
1505 PCSZ fnc = "Unknown";
1506 uint n;
1507 short len, tp;
1508 int crow = 0;
1509 PQRYRES qrp = cap->Qrp;
1510 PCOLRES crp;
1511 jboolean rc = false;
1512// HSTMT hstmt = NULL;
1513// SQLLEN *vl, *vlen = NULL;
1514 PVAL *pval = NULL;
1515 char* *pbuf = NULL;
1516 jobjectArray parms;
1517 jmethodID catid = nullptr;
1518
1519 if (qrp->Maxres <= 0)
1520 return 0; // 0-sized result"
1521
1522 SQLQualifiedName name(cap);
1523
1524 // Build the java string array
1525 parms = env->NewObjectArray(4, env->FindClass("java/lang/String"), NULL);
1526 env->SetObjectArrayElement(parms, 0, env->NewStringUTF(name.ptr(2)));
1527 env->SetObjectArrayElement(parms, 1, env->NewStringUTF(name.ptr(1)));
1528 env->SetObjectArrayElement(parms, 2, env->NewStringUTF(name.ptr(0)));
1529 env->SetObjectArrayElement(parms, 3, env->NewStringUTF((const char*)cap->Pat));
1530
1531 // Now do call the proper JDBC API
1532 switch (cap->Id) {
1533 case JCAT_COL:
1534 fnc = "GetColumns";
1535 break;
1536 case JCAT_TAB:
1537 fnc = "GetTables";
1538 break;
1539#if 0
1540 case JCAT_KEY:
1541 fnc = "SQLPrimaryKeys";
1542 rc = SQLPrimaryKeys(hstmt, name.ptr(2), name.length(2),
1543 name.ptr(1), name.length(1),
1544 name.ptr(0), name.length(0));
1545 break;
1546#endif // 0
1547 default:
1548 sprintf(g->Message, "Invalid SQL function id");
1549 return -1;
1550 } // endswitch infotype
1551
1552 if (gmID(g, catid, fnc, "([Ljava/lang/String;)I"))
1553 return -1;
1554
1555 // call method
1556 ncol = env->CallIntMethod(job, catid, parms);
1557
1558 if (Check(ncol)) {
1559 sprintf(g->Message, "%s: %s", fnc, Msg);
1560 env->DeleteLocalRef(parms);
1561 return -1;
1562 } // endif Check
1563
1564 // Not used anymore
1565 env->DeleteLocalRef(parms);
1566
1567 if (trace(1))
1568 htrc("Method %s returned %d columns\n", fnc, ncol);
1569
1570 // n because we no more ignore the first column
1571 if ((n = qrp->Nbcol) > (uint)ncol) {
1572 strcpy(g->Message, MSG(COL_NUM_MISM));
1573 return -1;
1574 } // endif n
1575
1576 // Unconditional to handle STRBLK's
1577 pval = (PVAL *)PlugSubAlloc(g, NULL, n * sizeof(PVAL));
1578// vlen = (SQLLEN *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN));
1579 pbuf = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
1580
1581 // Prepare retrieving column values
1582 for (n = 0, crp = qrp->Colresp; crp; crp = crp->Next) {
1583 if (!(tp = GetJDBCType(crp->Type))) {
1584 sprintf(g->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name);
1585 return -1;
1586 } // endif tp
1587
1588 if (!(len = GetTypeSize(crp->Type, crp->Length))) {
1589 len = 255; // for STRBLK's
1590 ((STRBLK*)crp->Kdata)->SetSorted(true);
1591 } // endif len
1592
1593 pval[n] = AllocateValue(g, crp->Type, len);
1594 pval[n]->SetNullable(true);
1595
1596 if (crp->Type == TYPE_STRING) {
1597 pbuf[n] = (char*)PlugSubAlloc(g, NULL, len);
1598// buffer = pbuf[n];
1599 } // endif Type
1600// } else
1601// buffer = pval[n]->GetTo_Val();
1602
1603 n++;
1604 } // endfor n
1605
1606 // Now fetch the result
1607 for (i = 0; i < qrp->Maxres; i++) {
1608 if (Check(rc = Fetch(0))) {
1609 sprintf(g->Message, "Fetch: %s", Msg);
1610 return -1;
1611 } if (rc == 0) {
1612 if (trace(1))
1613 htrc("End of fetches i=%d\n", i);
1614
1615 break;
1616 } // endif rc
1617
1618 for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
1619 SetColumnValue(n + 1, nullptr, pval[n]);
1620 crp->Kdata->SetValue(pval[n], i);
1621 } // endfor n
1622
1623 } // endfor i
1624
1625 if (rc > 0)
1626 qrp->Truncated = true;
1627
1628 return i;
1629 } // end of GetCatInfo
1630
1631 /***********************************************************************/
1632 /* Allocate a CONNECT result structure from the JDBC result. */
1633 /***********************************************************************/
1634 PQRYRES JDBConn::AllocateResult(PGLOBAL g, PTDB tdbp)
1635 {
1636 bool uns;
1637 PCOL colp;
1638 PCOLRES *pcrp, crp;
1639 PQRYRES qrp;
1640
1641 if (!m_Rows) {
1642 strcpy(g->Message, "Void result");
1643 return NULL;
1644 } // endif m_Rows
1645
1646 /*********************************************************************/
1647 /* Allocate the result storage for future retrieval. */
1648 /*********************************************************************/
1649 qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
1650 pcrp = &qrp->Colresp;
1651 qrp->Continued = FALSE;
1652 qrp->Truncated = FALSE;
1653 qrp->Info = FALSE;
1654 qrp->Suball = TRUE;
1655 qrp->BadLines = 0;
1656 qrp->Maxsize = m_Rows;
1657 qrp->Maxres = m_Rows;
1658 qrp->Nbcol = 0;
1659 qrp->Nblin = 0;
1660 qrp->Cursor = 0;
1661
1662 for (colp = tdbp->GetColumns(); colp; colp = colp->GetNext())
1663 if (!colp->IsSpecial()) {
1664 *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
1665 crp = *pcrp;
1666 pcrp = &crp->Next;
1667 memset(crp, 0, sizeof(COLRES));
1668 crp->Ncol = ++qrp->Nbcol;
1669 crp->Name = colp->GetName();
1670 crp->Type = colp->GetResultType();
1671 crp->Prec = colp->GetScale();
1672 crp->Length = colp->GetLength();
1673 crp->Clen = colp->GetValue()->GetClen();
1674 uns = colp->IsUnsigned();
1675
1676 if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
1677 crp->Clen, 0, FALSE, TRUE, uns))) {
1678 sprintf(g->Message, MSG(INV_RESULT_TYPE),
1679 GetFormatType(crp->Type));
1680 return NULL;
1681 } // endif Kdata
1682
1683 if (!colp->IsNullable())
1684 crp->Nulls = NULL;
1685 else {
1686 crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
1687 memset(crp->Nulls, ' ', m_Rows);
1688 } // endelse Nullable
1689
1690 ((EXTCOL*)colp)->SetCrp(crp);
1691 } // endif colp
1692
1693 *pcrp = NULL;
1694 return qrp;
1695 } // end of AllocateResult
1696