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