diff options
Diffstat (limited to 'storage/connect/jdbconn.cpp')
-rw-r--r-- | storage/connect/jdbconn.cpp | 1691 |
1 files changed, 1691 insertions, 0 deletions
diff --git a/storage/connect/jdbconn.cpp b/storage/connect/jdbconn.cpp new file mode 100644 index 00000000..c73808fa --- /dev/null +++ b/storage/connect/jdbconn.cpp @@ -0,0 +1,1691 @@ +/************ Jdbconn C++ Functions Source Code File (.CPP) ************/ +/* Name: JDBCONN.CPP Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2016-2018 */ +/* */ +/* This file contains the JDBC connection classes functions. */ +/***********************************************************************/ + +#if defined(_WIN32) +// This is needed for RegGetValue +#define _WINVER 0x0601 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 +#endif // _WIN32 + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include <my_global.h> +#include <m_string.h> +#if defined(_WIN32) +//nclude <io.h> +//nclude <fcntl.h> +#include <direct.h> // for getcwd +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !_WIN32 +#if defined(UNIX) +#include <errno.h> +#else // !UNIX +//nclude <io.h> +#endif // !UNIX +#include <stdio.h> +#include <stdlib.h> // for getenv +//nclude <fcntl.h> +#define NODW +#endif // !_WIN32 + +/***********************************************************************/ +/* Required objects includes. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +#include "xtable.h" +#include "tabext.h" +#include "tabjdbc.h" +//#include "jdbconn.h" +#include "resource.h" +#include "valblk.h" +#include "osutil.h" + + +//#if defined(_WIN32) +//extern "C" HINSTANCE s_hModule; // Saved module handle +//#endif // _WIN32 +#define nullptr 0 + +TYPCONV GetTypeConv(); +int GetConvSize(); +//extern char *JvmPath; // The connect_jvm_path global variable value +//extern char *ClassPath; // The connect_class_path global variable value + +//char *GetJavaWrapper(void); // The connect_java_wrapper variable value + +/***********************************************************************/ +/* Some macro's (should be defined elsewhere to be more accessible) */ +/***********************************************************************/ +//#if defined(_DEBUG) +//#define ASSERT(f) assert(f) +//#define DEBUG_ONLY(f) (f) +//#else // !_DEBUG +//#define ASSERT(f) ((void)0) +//#define DEBUG_ONLY(f) ((void)0) +//#endif // !_DEBUG + +// To avoid gcc warning +int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v); + +/***********************************************************************/ +/* GetJDBCType: returns the SQL_TYPE corresponding to a PLG type. */ +/***********************************************************************/ +static short GetJDBCType(int type) +{ + short tp = 0; // NULL + + switch (type) { + case TYPE_STRING: tp = 12; break; // VARCHAR + case TYPE_SHORT: tp = 5; break; // SMALLINT + case TYPE_INT: tp = 4; break; // INTEGER + case TYPE_DATE: tp = 93; break; // DATE +//case TYPE_TIME: tp = 92; break; // TIME +//case TYPE_TIMESTAMP: tp = 93; break; // TIMESTAMP + case TYPE_BIGINT: tp = -5; break; // BIGINT + case TYPE_DOUBLE: tp = 8; break; // DOUBLE + case TYPE_TINY: tp = -6; break; // TINYINT + case TYPE_DECIM: tp = 3; break; // DECIMAL + } // endswitch type + + return tp; +} // end of GetJDBCType + +/***********************************************************************/ +/* TranslateJDBCType: translate a JDBC Type to a PLG type. */ +/***********************************************************************/ +int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v) +{ + int type; + + switch (stp) { + case -1: // LONGVARCHAR, TEXT + case -16: // LONGNVARCHAR, NTEXT (unicode) + if (GetTypeConv() != TPC_YES) + return TYPE_ERROR; + else + len = MY_MIN(abs(len), GetConvSize()); + + /* fall through */ + case 12: // VARCHAR + if (tn && !stricmp(tn, "TEXT")) + // Postgresql returns 12 for TEXT + if (GetTypeConv() == TPC_NO) + return TYPE_ERROR; + + // Postgresql can return this + if (len == 0x7FFFFFFF) + len = GetConvSize(); + + /* fall through */ + case -9: // NVARCHAR (unicode) + // Postgresql can return this when size is unknown + if (len == 0x7FFFFFFF) + len = GetConvSize(); + + v = 'V'; + /* fall through */ + case 1: // CHAR + case -15: // NCHAR (unicode) + case -8: // ROWID + type = TYPE_STRING; + break; + case 2: // NUMERIC + case 3: // DECIMAL + case -3: // VARBINARY + type = TYPE_DECIM; + break; + case 4: // INTEGER + type = TYPE_INT; + break; + case 5: // SMALLINT + type = TYPE_SHORT; + break; + case -6: // TINYINT + case -7: // BIT + case 16: // BOOLEAN + type = TYPE_TINY; + break; + case 6: // FLOAT + case 7: // REAL + case 8: // DOUBLE + type = TYPE_DOUBLE; + break; + case 93: // TIMESTAMP, DATETIME + type = TYPE_DATE; + len = 19 + ((prec) ? (prec+1) : 0); + v = (tn && toupper(tn[0]) == 'T') ? 'S' : 'E'; + break; + case 91: // DATE, YEAR + type = TYPE_DATE; + + if (!tn || toupper(tn[0]) != 'Y') { + len = 10; + v = 'D'; + } else { + len = 4; + v = 'Y'; + } // endif len + + break; + case 92: // TIME + type = TYPE_DATE; + len = 8 + ((prec) ? (prec + 1) : 0); + v = 'T'; + break; + case -5: // BIGINT + type = TYPE_BIGINT; + break; + case 1111: // UNKNOWN or UUID + if (!tn || !stricmp(tn, "UUID")) { + type = TYPE_STRING; + len = 36; + break; + } // endif tn + + /* fall through */ + case 0: // NULL + case -2: // BINARY + case -4: // LONGVARBINARY + case 70: // DATALINK + case 2000: // JAVA_OBJECT + case 2001: // DISTINCT + case 2002: // STRUCT + case 2003: // ARRAY + case 2004: // BLOB + case 2005: // CLOB + case 2006: // REF + case 2009: // SQLXML + case 2011: // NCLOB + default: + type = TYPE_ERROR; + len = 0; + } // endswitch type + + return type; +} // end of TranslateJDBCType + + /***********************************************************************/ + /* A helper class to split an optionally qualified table name into */ + /* components. */ + /* These formats are understood: */ + /* "CatalogName.SchemaName.TableName" */ + /* "SchemaName.TableName" */ + /* "TableName" */ + /***********************************************************************/ +class SQLQualifiedName { + static const uint max_parts = 3; // Catalog.Schema.Table + MYSQL_LEX_STRING m_part[max_parts]; + char m_buf[512]; + + void lex_string_set(MYSQL_LEX_STRING *S, char *str, size_t length) + { + S->str = str; + S->length = length; + } // end of lex_string_set + + void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs) + { + DBUG_ASSERT(offs <= S->length); + S->str += offs; + S->length -= offs; + } // end of lex_string_shorten_down + + /*********************************************************************/ + /* Find the rightmost '.' delimiter and return the length */ + /* of the qualifier, including the rightmost '.' delimier. */ + /* For example, for the string {"a.b.c",5} it will return 4, */ + /* which is the length of the qualifier "a.b." */ + /*********************************************************************/ + size_t lex_string_find_qualifier(MYSQL_LEX_STRING *S) + { + size_t i; + for (i = S->length; i > 0; i--) + { + if (S->str[i - 1] == '.') + { + S->str[i - 1] = '\0'; + return i; + } + } + return 0; + } // end of lex_string_find_qualifier + +public: + /*********************************************************************/ + /* Initialize to the given optionally qualified name. */ + /* NULL pointer in "name" is supported. */ + /* name qualifier has precedence over schema. */ + /*********************************************************************/ + SQLQualifiedName(JCATPARM *cap) + { + const char *name = (const char *)cap->Tab; + char *db = (char *)cap->DB; + size_t len, i; + + // Initialize the parts + for (i = 0; i < max_parts; i++) + lex_string_set(&m_part[i], NULL, 0); + + if (name) { + // Initialize the first (rightmost) part + lex_string_set(&m_part[0], m_buf, + strmake(m_buf, name, sizeof(m_buf) - 1) - m_buf); + + // Initialize the other parts, if exist. + for (i = 1; i < max_parts; i++) { + if (!(len = lex_string_find_qualifier(&m_part[i - 1]))) + break; + + lex_string_set(&m_part[i], m_part[i - 1].str, len - 1); + lex_string_shorten_down(&m_part[i - 1], len); + } // endfor i + + } // endif name + + // If it was not specified, set schema as the passed db name + if (db && !m_part[1].length) + lex_string_set(&m_part[1], db, strlen(db)); + + } // end of SQLQualifiedName + + char *ptr(uint i) + { + DBUG_ASSERT(i < max_parts); + return (char *)(m_part[i].length ? m_part[i].str : NULL); + } // end of ptr + + size_t length(uint i) + { + DBUG_ASSERT(i < max_parts); + return m_part[i].length; + } // end of length + +}; // end of class SQLQualifiedName + +/***********************************************************************/ +/* Allocate the structure used to refer to the result set. */ +/***********************************************************************/ +static JCATPARM *AllocCatInfo(PGLOBAL g, JCATINFO fid, PCSZ db, + PCSZ tab, PQRYRES qrp) +{ + JCATPARM *cap; + + if ((cap = (JCATPARM *)PlgDBSubAlloc(g, NULL, sizeof(JCATPARM)))) { + memset(cap, 0, sizeof(JCATPARM)); + cap->Id = fid; + cap->Qrp = qrp; + cap->DB = db; + cap->Tab = tab; + } // endif cap + + return cap; +} // end of AllocCatInfo + +/***********************************************************************/ +/* JDBCColumns: constructs the result blocks containing all columns */ +/* of a JDBC table that will be retrieved by GetData commands. */ +/***********************************************************************/ +PQRYRES JDBCColumns(PGLOBAL g, PCSZ db, PCSZ table, PCSZ colpat, + int maxres, bool info, PJPARM sjp) +{ + int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING, + TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT, + TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING}; + XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_TABNAME, FLD_NAME, + FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH, + FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM}; + unsigned int length[] = {0, 0, 0, 0, 6, 0, 10, 10, 6, 6, 6, 0}; + bool b[] = {true, true, false, false, false, false, false, false, true, true, false, true}; + int i, n, ncol = 12; + PCOLRES crp; + PQRYRES qrp; + JCATPARM *cap; + JDBConn *jcp = NULL; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + jcp = new(g)JDBConn(g, NULL); + + if (jcp->Connect(sjp)) // openReadOnly + noJDBCdialog + return NULL; + + if (table && !strchr(table, '%')) { + // We fix a MySQL limit because some data sources return 32767 + n = jcp->GetMaxValue(1); // MAX_COLUMNS_IN_TABLE) + maxres = (n > 0) ? MY_MIN(n, 4096) : 4096; + } else if (!maxres) + maxres = 20000; + + // n = jcp->GetMaxValue(2); MAX_CATALOG_NAME_LEN + // length[0] = (n) ? (n + 1) : 0; + // n = jcp->GetMaxValue(3); MAX_SCHEMA_NAME_LEN + // length[1] = (n) ? (n + 1) : 0; + // n = jcp->GetMaxValue(4); MAX_TABLE_NAME_LEN + // length[2] = (n) ? (n + 1) : 0; + n = jcp->GetMaxValue(5); // MAX_COLUMN_NAME_LEN + length[3] = (n > 0) ? (n + 1) : 128; + } else { // Info table + maxres = 0; + length[0] = 128; + length[1] = 128; + length[2] = 128; + length[3] = 128; + length[5] = 30; + length[11] = 255; + } // endif jcp + + if (trace(1)) + htrc("JDBCColumns: max=%d len=%d,%d,%d,%d\n", + maxres, length[0], length[1], length[2], length[3]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS, + buftyp, fldtyp, length, false, true); + + for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next) + if (b[i]) + crp->Kdata->SetNullable(true); + + if (info || !qrp) // Info table + return qrp; + + if (trace(1)) + htrc("Getting col results ncol=%d\n", qrp->Nbcol); + + if (!(cap = AllocCatInfo(g, JCAT_COL, db, table, qrp))) + return NULL; + + // Colpat cannot be null or empty for some drivers + cap->Pat = (colpat && *colpat) ? colpat : PlugDup(g, "%"); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = jcp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + // ResetNullValues(cap); + + if (trace(1)) + htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); + + } else + qrp = NULL; + + /* Cleanup */ + jcp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; +} // end of JDBCColumns + +/**************************************************************************/ +/* JDBCSrcCols: constructs the result blocks containing the */ +/* description of all the columns of a Srcdef option. */ +/**************************************************************************/ +PQRYRES JDBCSrcCols(PGLOBAL g, PCSZ src, PJPARM sjp) +{ + char *sqry; + PQRYRES qrp; + JDBConn *jcp = new(g)JDBConn(g, NULL); + + if (jcp->Connect(sjp)) + return NULL; + + if (strstr(src, "%s")) { + // Place holder for an eventual where clause + size_t sqry_size = strlen(src) + 2; + sqry = (char*)PlugSubAlloc(g, NULL, sqry_size); + // Function PlugSubAlloc(...) recalculate string size + // while allocate memory - it rounds size up size to multiple of 8 + // we need to know the real allocated size + // to use it in sprintf(...) + const int sqry_real_allocated_size = ROUNDUP_TO_8(sqry_size); + snprintf(sqry, sqry_real_allocated_size, src, "1=1"); // dummy where clause + } else + sqry = (char*)src; + + qrp = jcp->GetMetaData(g, sqry); + jcp->Close(); + return qrp; +} // end of JDBCSrcCols + +/**************************************************************************/ +/* JDBCTables: constructs the result blocks containing all tables in */ +/* an JDBC database that will be retrieved by GetData commands. */ +/**************************************************************************/ +PQRYRES JDBCTables(PGLOBAL g, PCSZ db, PCSZ tabpat, PCSZ tabtyp, + int maxres, bool info, PJPARM sjp) +{ + int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, + TYPE_STRING, TYPE_STRING}; + XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_NAME, FLD_TYPE, FLD_REM}; + unsigned int length[] = {0, 0, 0, 16, 0}; + bool b[] = {true, true, false, false, true}; + int i, n, ncol = 5; + PCOLRES crp; + PQRYRES qrp; + JCATPARM *cap; + JDBConn *jcp = NULL; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + /**********************************************************************/ + /* Open the connection with the JDBC data source. */ + /**********************************************************************/ + jcp = new(g)JDBConn(g, NULL); + + if (jcp->Connect(sjp)) + return NULL; + + if (!maxres) + maxres = 10000; // This is completely arbitrary + + n = jcp->GetMaxValue(2); // Max catalog name length + +// if (n < 0) +// return NULL; + + length[0] = (n > 0) ? (n + 1) : 0; + n = jcp->GetMaxValue(3); // Max schema name length + length[1] = (n > 0) ? (n + 1) : 0; + n = jcp->GetMaxValue(4); // Max table name length + length[2] = (n > 0) ? (n + 1) : 128; + } else { + maxres = 0; + length[0] = 128; + length[1] = 128; + length[2] = 128; + length[4] = 255; + } // endif info + + if (trace(1)) + htrc("JDBCTables: max=%d len=%d,%d\n", maxres, length[0], length[1]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES, buftyp, + fldtyp, length, false, true); + + for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next) + if (b[i]) + crp->Kdata->SetNullable(true); + + if (info || !qrp) + return qrp; + + // Tabpat cannot be null or empty for some drivers + if (!(cap = AllocCatInfo(g, JCAT_TAB, db, + (tabpat && *tabpat) ? tabpat : PlugDup(g, "%"), qrp))) + return NULL; + + cap->Pat = tabtyp; + + if (trace(1)) + htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = jcp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + // ResetNullValues(cap); + + if (trace(1)) + htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); + + } else + qrp = NULL; + + /************************************************************************/ + /* Close any local connection. */ + /************************************************************************/ + jcp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; +} // end of JDBCTables + +/*************************************************************************/ +/* JDBCDrivers: constructs the result blocks containing all JDBC */ +/* drivers available on the local host. */ +/* Called with info=true to have result column names. */ +/*************************************************************************/ +PQRYRES JDBCDrivers(PGLOBAL g, int maxres, bool info) +{ + int buftyp[] ={TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING}; + XFLD fldtyp[] ={FLD_NAME, FLD_EXTRA, FLD_DEFAULT, FLD_REM }; + unsigned int length[] ={ 128, 32, 4, 256 }; + bool b[] ={ false, false, false, true }; + int i, ncol = 4; + PCOLRES crp; + PQRYRES qrp; + JDBConn *jcp = NULL; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + jcp = new(g) JDBConn(g, NULL); + + if (jcp->Open(g) != RC_OK) + return NULL; + + if (!maxres) + maxres = 256; // Estimated max number of drivers + + } else + maxres = 0; + + if (trace(1)) + htrc("JDBCDrivers: max=%d len=%d\n", maxres, length[0]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, 0, buftyp, fldtyp, length, false, true); + + for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next) { + if (b[i]) + crp->Kdata->SetNullable(true); + + switch (i) { + case 0: crp->Name = "Name"; break; + case 1: crp->Name = "Version"; break; + case 2: crp->Name = "Compliant"; break; + case 3: crp->Name = "Description"; break; + } // endswitch + + } // endfor i + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if (!info && qrp && jcp->GetDrivers(qrp)) + qrp = NULL; + + if (!info) + jcp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; +} // end of JDBCDrivers + +/***********************************************************************/ +/* JDBConn construction/destruction. */ +/***********************************************************************/ +JDBConn::JDBConn(PGLOBAL g, PCSZ wrapper) : JAVAConn(g, wrapper) +{ + xqid = xuid = xid = grs = readid = fetchid = typid = errid = nullptr; + prepid = xpid = pcid = nullptr; + chrfldid = intfldid = dblfldid = fltfldid = bigfldid = nullptr; + objfldid = datfldid = timfldid = tspfldid = uidfldid = nullptr; + DiscFunc = "JdbcDisconnect"; + m_Ncol = 0; + m_Aff = 0; + //m_Rows = 0; + m_Fetch = 0; + m_RowsetSize = 0; + m_Updatable = true; + m_Transact = false; + m_Scrollable = false; + m_Full = false; + m_Opened = false; + m_IDQuoteChar[0] = '"'; + m_IDQuoteChar[1] = 0; +} // end of JDBConn + +/***********************************************************************/ +/* Search for UUID columns. */ +/***********************************************************************/ +bool JDBConn::SetUUID(PGLOBAL g, PTDBJDBC tjp) +{ + int ncol, ctyp; + bool brc = true; + PCSZ fnc = "GetColumns"; + PCOL colp; + JCATPARM *cap; + //jint jtyp; + jboolean rc = false; + jobjectArray parms; + jmethodID catid = nullptr; + + if (gmID(g, catid, fnc, "([Ljava/lang/String;)I")) + return true; + else if (gmID(g, intfldid, "IntField", "(ILjava/lang/String;)I")) + return true; + else if (gmID(g, readid, "ReadNext", "()I")) + return true; + + cap = AllocCatInfo(g, JCAT_COL, tjp->Schema, tjp->TableName, NULL); + SQLQualifiedName name(cap); + + // Build the java string array + parms = env->NewObjectArray(4, env->FindClass("java/lang/String"), NULL); + env->SetObjectArrayElement(parms, 0, env->NewStringUTF(name.ptr(2))); + env->SetObjectArrayElement(parms, 1, env->NewStringUTF(name.ptr(1))); + env->SetObjectArrayElement(parms, 2, env->NewStringUTF(name.ptr(0))); + + for (colp = tjp->GetColumns(); colp; colp = colp->GetNext()) { + env->SetObjectArrayElement(parms, 3, env->NewStringUTF(colp->GetName())); + ncol = env->CallIntMethod(job, catid, parms); + + if (Check(ncol)) { + snprintf(g->Message, sizeof(g->Message), "%s: %s", fnc, Msg); + goto err; + } // endif Check + + rc = env->CallBooleanMethod(job, readid); + + if (Check(rc)) { + snprintf(g->Message, sizeof(g->Message), "ReadNext: %s", Msg); + goto err; + } else if (rc == 0) { + snprintf(g->Message, sizeof(g->Message), "table %s does not exist", tjp->TableName); + goto err; + } // endif rc + + // Should return 666 is case of error (not done yet) + ctyp = (int)env->CallIntMethod(job, intfldid, 5, nullptr); + + //if (Check((ctyp == 666) ? -1 : 1)) { + // snprintf(g->Message, sizeof(g->Message), "Getting ctyp: %s", Msg); + // goto err; + //} // endif ctyp + + if (ctyp == 1111) + ((PJDBCCOL)colp)->uuid = true; + + } // endfor colp + + // All is Ok + brc = false; + + err: + // Not used anymore + env->DeleteLocalRef(parms); + return brc; +} // end of SetUUID + +/***********************************************************************/ +/* Utility routine. */ +/***********************************************************************/ +int JDBConn::GetMaxValue(int n) +{ + jint m; + jmethodID maxid = nullptr; + + if (gmID(m_G, maxid, "GetMaxValue", "(I)I")) + return -1; + + // call method + if (Check(m = env->CallIntMethod(job, maxid, n))) + htrc("GetMaxValue: %s", Msg); + + return (int)m; +} // end of GetMaxValue + +/***********************************************************************/ +/* AddJars: add some jar file to the Class path. */ +/***********************************************************************/ +void JDBConn::AddJars(PSTRG jpop, char sep) +{ +#if defined(DEVELOPMENT) + jpop->Append( + ";C:/Jconnectors/postgresql-9.4.1208.jar" + ";C:/Oracle/ojdbc7.jar" + ";C:/Apache/commons-dbcp2-2.1.1/commons-dbcp2-2.1.1.jar" + ";C:/Apache/commons-pool2-2.4.2/commons-pool2-2.4.2.jar" + ";C:/Apache/commons-logging-1.2/commons-logging-1.2.jar" + ";C:/Jconnectors/mysql-connector-java-6.0.2-bin.jar" + ";C:/Jconnectors/mariadb-java-client-2.0.1.jar" + ";C:/Jconnectors/sqljdbc42.jar"); +#endif // DEVELOPMENT +} // end of AddJars + +/***********************************************************************/ +/* Connect: connect to a data source. */ +/***********************************************************************/ +bool JDBConn::Connect(PJPARM sop) +{ + bool err = false; + jint rc; + PGLOBAL& g = m_G; + + /*******************************************************************/ + /* Create or attach a JVM. */ + /*******************************************************************/ + if (Open(g)) + return true; + + if (!sop) // DRIVER catalog table + return false; + + jmethodID cid = nullptr; + + if (gmID(g, cid, "JdbcConnect", "([Ljava/lang/String;IZ)I")) + return true; + + // Build the java string array + jobjectArray parms = env->NewObjectArray(4, // constructs java array of 4 + env->FindClass("java/lang/String"), NULL); // Strings + + m_Scrollable = sop->Scrollable; + m_RowsetSize = sop->Fsize; + //m_LoginTimeout = sop->Cto; + //m_QueryTimeout = sop->Qto; + //m_UseCnc = sop->UseCnc; + + // change some elements + if (sop->Driver) + env->SetObjectArrayElement(parms, 0, env->NewStringUTF(sop->Driver)); + + if (sop->Url) + env->SetObjectArrayElement(parms, 1, env->NewStringUTF(sop->Url)); + + if (sop->User) + env->SetObjectArrayElement(parms, 2, env->NewStringUTF(sop->User)); + + if (sop->Pwd) + env->SetObjectArrayElement(parms, 3, env->NewStringUTF(sop->Pwd)); + + // call method + rc = env->CallIntMethod(job, cid, parms, m_RowsetSize, m_Scrollable); + err = Check(rc); + env->DeleteLocalRef(parms); // Not used anymore + + if (err) { + snprintf(g->Message, sizeof(g->Message), "Connecting: %s rc=%d", Msg, (int)rc); + return true; + } // endif Msg + + jmethodID qcid = nullptr; + + if (!gmID(g, qcid, "GetQuoteString", "()Ljava/lang/String;")) { + jstring s = (jstring)env->CallObjectMethod(job, qcid); + + if (s != nullptr) { + char *qch = GetUTFString(s); + m_IDQuoteChar[0] = *qch; + } else { + s = (jstring)env->CallObjectMethod(job, errid); + Msg = GetUTFString(s); + } // endif s + + } // endif qcid + + if (gmID(g, typid, "ColumnType", "(ILjava/lang/String;)I")) + return true; + else + m_Connected = true; + + return false; +} // end of Connect + + +/***********************************************************************/ +/* Execute an SQL command. */ +/***********************************************************************/ +int JDBConn::ExecuteCommand(PCSZ sql) +{ + int rc; + jint n; + jstring qry; + PGLOBAL& g = m_G; + + // Get the methods used to execute a query and get the result + if (gmID(g, xid, "Execute", "(Ljava/lang/String;)I") || + gmID(g, grs, "GetResult", "()I")) + return RC_FX; + + qry = env->NewStringUTF(sql); + n = env->CallIntMethod(job, xid, qry); + env->DeleteLocalRef(qry); + + if (Check(n)) { + snprintf(g->Message, sizeof(g->Message), "Execute: %s", Msg); + return RC_FX; + } // endif n + + m_Ncol = env->CallIntMethod(job, grs); + + if (Check(m_Ncol)) { + snprintf(g->Message, sizeof(g->Message), "GetResult: %s", Msg); + rc = RC_FX; + } else if (m_Ncol) { + snprintf(g->Message, sizeof(g->Message), "Result set column number"); + rc = RC_OK; // A result set was returned + } else { + m_Aff = (int)n; // Affected rows + snprintf(g->Message, sizeof(g->Message), "Affected rows"); + rc = RC_NF; + } // endif ncol + + return rc; +} // end of ExecuteCommand + +/***********************************************************************/ +/* Fetch next row. */ +/***********************************************************************/ +int JDBConn::Fetch(int pos) +{ + jint rc = JNI_ERR; + PGLOBAL& g = m_G; + + if (m_Full) // Result set has one row + return 1; + + if (pos) { + if (!m_Scrollable) { + snprintf(g->Message, sizeof(g->Message), "Cannot fetch(pos) if FORWARD ONLY"); + return rc; + } else if (gmID(m_G, fetchid, "Fetch", "(I)Z")) + return rc; + + if (env->CallBooleanMethod(job, fetchid, pos)) + rc = m_Rows; + + } else { + if (gmID(g, readid, "ReadNext", "()I")) + return rc; + + rc = env->CallBooleanMethod(job, readid); + + if (!Check(rc)) { + if (rc == 0) + m_Full = (m_Fetch == 1); + else + m_Fetch++; + + m_Rows += (int)rc; + } else + snprintf(g->Message, sizeof(g->Message), "Fetch: %s", Msg); + + } // endif pos + + return (int)rc; +} // end of Fetch + +/***********************************************************************/ +/* Restart from beginning of result set */ +/***********************************************************************/ +int JDBConn::Rewind(PCSZ sql) +{ + int rbuf = -1; + + if (m_Full) + rbuf = m_Rows; // No need to "rewind" + else if (m_Scrollable) { + if (gmID(m_G, fetchid, "Fetch", "(I)Z")) + return -1; + + (void) env->CallBooleanMethod(job, fetchid, 0); + + rbuf = m_Rows; + } else if (ExecuteCommand(sql) != RC_FX) + rbuf = 0; + + return rbuf; +} // end of Rewind + +/***********************************************************************/ +/* Retrieve and set the column value from the result set. */ +/***********************************************************************/ +void JDBConn::SetColumnValue(int rank, PSZ name, PVAL val) +{ + const char *field; + PGLOBAL& g = m_G; + jint ctyp; + jstring cn, jn = nullptr; + jobject jb = nullptr; + + if (rank == 0) + if (!name || (jn = env->NewStringUTF(name)) == nullptr) { + snprintf(g->Message, sizeof(g->Message), "Fail to allocate jstring %s", SVP(name)); + throw (int)TYPE_AM_JDBC; + } // endif name + + // Returns 666 is case of error + ctyp = env->CallIntMethod(job, typid, rank, jn); + + if (Check((ctyp == 666) ? -1 : 1)) { + snprintf(g->Message, sizeof(g->Message), "Getting ctyp: %s", Msg); + throw (int)TYPE_AM_JDBC; + } // endif Check + + if (val->GetNullable()) + if (!gmID(g, objfldid, "ObjectField", "(ILjava/lang/String;)Ljava/lang/Object;")) { + jb = env->CallObjectMethod(job, objfldid, (jint)rank, jn); + + if (Check(0)) { + snprintf(g->Message, sizeof(g->Message), "Getting jp: %s", Msg); + throw (int)TYPE_AM_JDBC; + } // endif Check + + if (jb == nullptr) { + val->Reset(); + val->SetNull(true); + goto chk; + } // endif job + + } // endif objfldid + + switch (ctyp) { + case 12: // VARCHAR + case -9: // NVARCHAR + case -1: // LONGVARCHAR, TEXT + case 1: // CHAR + case -15: // NCHAR + case -16: // LONGNVARCHAR, NTEXT + case 3: // DECIMAL + case -8: // ROWID + if (jb && ctyp != 3) + cn = (jstring)jb; + else if (!gmID(g, chrfldid, "StringField", "(ILjava/lang/String;)Ljava/lang/String;")) + cn = (jstring)env->CallObjectMethod(job, chrfldid, (jint)rank, jn); + else + cn = nullptr; + + if (cn) { + field = GetUTFString(cn); + val->SetValue_psz((PSZ)field); + } else + val->Reset(); + + break; + case 4: // INTEGER + case 5: // SMALLINT + case -6: // TINYINT + case 16: // BOOLEAN + case -7: // BIT + if (!gmID(g, intfldid, "IntField", "(ILjava/lang/String;)I")) + val->SetValue((int)env->CallIntMethod(job, intfldid, rank, jn)); + else + val->Reset(); + + break; + case 8: // DOUBLE + case 2: // NUMERIC +//case 3: // DECIMAL + if (!gmID(g, dblfldid, "DoubleField", "(ILjava/lang/String;)D")) + val->SetValue((double)env->CallDoubleMethod(job, dblfldid, rank, jn)); + else + val->Reset(); + + break; + case 7: // REAL + case 6: // FLOAT + if (!gmID(g, fltfldid, "FloatField", "(ILjava/lang/String;)F")) + val->SetValue((float)env->CallFloatMethod(job, fltfldid, rank, jn)); + else + val->Reset(); + + break; + case 91: // DATE + if (!gmID(g, datfldid, "DateField", "(ILjava/lang/String;)I")) { + val->SetValue((int)env->CallIntMethod(job, datfldid, (jint)rank, jn)); + } else + val->Reset(); + + break; + case 92: // TIME + if (!gmID(g, timfldid, "TimeField", "(ILjava/lang/String;)I")) { + val->SetValue((int)env->CallIntMethod(job, timfldid, (jint)rank, jn)); + } else + val->Reset(); + + break; + case 93: // TIMESTAMP + if (!gmID(g, tspfldid, "TimestampField", "(ILjava/lang/String;)I")) { + val->SetValue((int)env->CallIntMethod(job, tspfldid, (jint)rank, jn)); + } else + val->Reset(); + + break; + case -5: // BIGINT + if (!gmID(g, bigfldid, "BigintField", "(ILjava/lang/String;)J")) + val->SetValue((long long)env->CallLongMethod(job, bigfldid, (jint)rank, jn)); + else + val->Reset(); + + break; + /* case java.sql.Types.SMALLINT: + System.out.print(jdi.IntField(i)); + break; + case java.sql.Types.BOOLEAN: + System.out.print(jdi.BooleanField(i)); */ + case 1111: // UUID + if (!gmID(g, uidfldid, "UuidField", "(ILjava/lang/String;)Ljava/lang/String;")) + cn = (jstring)env->CallObjectMethod(job, uidfldid, (jint)rank, jn); + else + cn = nullptr; + + if (cn) { + val->SetValue_psz((PSZ)GetUTFString(cn)); + } else + val->Reset(); + + break; + case 0: // NULL + val->SetNull(true); + /* fall through */ + default: + val->Reset(); + } // endswitch Type + + chk: + if (Check()) { + if (rank == 0) + env->DeleteLocalRef(jn); + + snprintf(g->Message, sizeof(g->Message), "SetColumnValue: %s rank=%d ctyp=%d", Msg, rank, (int)ctyp); + throw (int)TYPE_AM_JDBC; + } // endif Check + + if (rank == 0) + env->DeleteLocalRef(jn); + +} // end of SetColumnValue + +/***********************************************************************/ +/* Prepare an SQL statement for insert. */ +/***********************************************************************/ +bool JDBConn::PrepareSQL(PCSZ sql) +{ + bool b = true; + PGLOBAL& g = m_G; + + if (!gmID(g, prepid, "CreatePrepStmt", "(Ljava/lang/String;)I")) { + // Create the prepared statement + jstring qry = env->NewStringUTF(sql); + + if (Check(env->CallBooleanMethod(job, prepid, qry))) + snprintf(g->Message, sizeof(g->Message), "CreatePrepStmt: %s", Msg); + else + b = false; + + env->DeleteLocalRef(qry); + } // endif prepid + + return b; +} // end of PrepareSQL + +/***********************************************************************/ +/* Execute an SQL query that returns a result set. */ +/***********************************************************************/ +int JDBConn::ExecuteQuery(PCSZ sql) +{ + int rc = RC_FX; + jint ncol; + jstring qry; + PGLOBAL& g = m_G; + + // Get the methods used to execute a query and get the result + if (!gmID(g, xqid, "ExecuteQuery", "(Ljava/lang/String;)I")) { + qry = env->NewStringUTF(sql); + ncol = env->CallIntMethod(job, xqid, qry); + + if (!Check(ncol)) { + m_Ncol = (int)ncol; + m_Aff = 0; // Affected rows + rc = RC_OK; + } else + snprintf(g->Message, sizeof(g->Message), "ExecuteQuery: %s", Msg); + + env->DeleteLocalRef(qry); + } // endif xqid + + return rc; +} // end of ExecuteQuery + +/***********************************************************************/ +/* Execute an SQL query and get the affected rows. */ +/***********************************************************************/ +int JDBConn::ExecuteUpdate(PCSZ sql) +{ + int rc = RC_FX; + jint n; + jstring qry; + PGLOBAL& g = m_G; + + // Get the methods used to execute a query and get the affected rows + if (!gmID(g, xuid, "ExecuteUpdate", "(Ljava/lang/String;)I")) { + qry = env->NewStringUTF(sql); + n = env->CallIntMethod(job, xuid, qry); + + if (!Check(n)) { + m_Ncol = 0; + m_Aff = (int)n; // Affected rows + rc = RC_OK; + } else + snprintf(g->Message, sizeof(g->Message), "ExecuteUpdate: %s n=%d", Msg, n); + + env->DeleteLocalRef(qry); + } // endif xuid + + return rc; +} // end of ExecuteUpdate + +/***********************************************************************/ +/* Get the number of lines of the result set. */ +/***********************************************************************/ +int JDBConn::GetResultSize(PCSZ sql, PCOL colp) +{ + int rc; + + if ((rc = ExecuteQuery(sql)) != RC_OK) + return -1; + + if ((rc = Fetch()) > 0) { + try { + SetColumnValue(1, NULL, colp->GetValue()); + } catch (...) { + return -4; + } // end catch + + } else + return -2; + + if ((rc = Fetch()) != 0) + return -3; + + m_Full = false; + return colp->GetIntValue(); +} // end of GetResultSize + +/***********************************************************************/ +/* Execute a prepared statement. */ +/***********************************************************************/ +int JDBConn::ExecuteSQL(void) +{ + int rc = RC_FX; + PGLOBAL& g = m_G; + + // Get the methods used to execute a prepared statement + if (!gmID(g, xpid, "ExecutePrep", "()I")) { + jint n = env->CallIntMethod(job, xpid); + + if (n == -3) + snprintf(g->Message, sizeof(g->Message), "SQL statement is not prepared"); + else if (Check(n)) + snprintf(g->Message, sizeof(g->Message), "ExecutePrep: %s", Msg); + else { + m_Aff = (int)n; + rc = RC_OK; + } // endswitch n + + } // endif xpid + + return rc; +} // end of ExecuteSQL + +/***********************************************************************/ +/* Set a parameter for inserting. */ +/***********************************************************************/ +bool JDBConn::SetParam(JDBCCOL *colp) +{ + PGLOBAL& g = m_G; + bool rc = false; + PVAL val = colp->GetValue(); + jint n, jrc = 0, i = (jint)colp->GetRank(); + jshort s; + jlong lg; +//jfloat f; + jdouble d; + jclass dat; + jobject datobj; + jstring jst = nullptr; + jmethodID dtc, setid = nullptr; + + if (val->GetNullable() && val->IsNull()) { + if (gmID(g, setid, "SetNullParm", "(II)I")) + return true; + + jrc = env->CallIntMethod(job, setid, i, + (colp->uuid ? 1111 : (jint)GetJDBCType(val->GetType()))); + } else if (colp->uuid) { + if (gmID(g, setid, "SetUuidParm", "(ILjava/lang/String;)V")) + return true; + + jst = env->NewStringUTF(val->GetCharValue()); + env->CallVoidMethod(job, setid, i, jst); + } else switch (val->GetType()) { + case TYPE_STRING: + if (gmID(g, setid, "SetStringParm", "(ILjava/lang/String;)V")) + return true; + + jst = env->NewStringUTF(val->GetCharValue()); + env->CallVoidMethod(job, setid, i, jst); + break; + case TYPE_INT: + if (gmID(g, setid, "SetIntParm", "(II)V")) + return true; + + n = (jint)val->GetIntValue(); + env->CallVoidMethod(job, setid, i, n); + break; + case TYPE_TINY: + case TYPE_SHORT: + if (gmID(g, setid, "SetShortParm", "(IS)V")) + return true; + + s = (jshort)val->GetShortValue(); + env->CallVoidMethod(job, setid, i, s); + break; + case TYPE_BIGINT: + if (gmID(g, setid, "SetBigintParm", "(IJ)V")) + return true; + + lg = (jlong)val->GetBigintValue(); + env->CallVoidMethod(job, setid, i, lg); + break; + case TYPE_DOUBLE: + case TYPE_DECIM: + if (gmID(g, setid, "SetDoubleParm", "(ID)V")) + return true; + + d = (jdouble)val->GetFloatValue(); + env->CallVoidMethod(job, setid, i, d); + break; + case TYPE_DATE: + if ((dat = env->FindClass("java/sql/Timestamp")) == nullptr) { + snprintf(g->Message, sizeof(g->Message), "Cannot find Timestamp class"); + return true; + } else if (!(dtc = env->GetMethodID(dat, "<init>", "(J)V"))) { + snprintf(g->Message, sizeof(g->Message), "Cannot find Timestamp class constructor"); + return true; + } // endif's + + lg = (jlong)val->GetBigintValue() * 1000; + + if ((datobj = env->NewObject(dat, dtc, lg)) == nullptr) { + snprintf(g->Message, sizeof(g->Message), "Cannot make Timestamp object"); + return true; + } else if (gmID(g, setid, "SetTimestampParm", "(ILjava/sql/Timestamp;)V")) + return true; + + env->CallVoidMethod(job, setid, i, datobj); + break; + default: + snprintf(g->Message, sizeof(g->Message), "Parm type %d not supported", val->GetType()); + return true; + } // endswitch Type + + if (Check(jrc)) { + snprintf(g->Message, sizeof(g->Message), "SetParam: col=%s msg=%s", colp->GetName(), Msg); + rc = true; + } // endif msg + + if (jst) + env->DeleteLocalRef(jst); + + return rc; + } // end of SetParam + + /***********************************************************************/ + /* Get the list of Drivers and set it in qrp. */ + /***********************************************************************/ + bool JDBConn::GetDrivers(PQRYRES qrp) + { + PSZ sval; + int i, n, size; + PCOLRES crp; + jstring js; + jmethodID gdid = nullptr; + + if (gmID(m_G, gdid, "GetDrivers", "([Ljava/lang/String;I)I")) + return true; + + // Build the java string array + jobjectArray s = env->NewObjectArray(4 * qrp->Maxres, + env->FindClass("java/lang/String"), NULL); + + size = env->CallIntMethod(job, gdid, s, qrp->Maxres); + + for (i = 0, n = 0; i < size; i++) { + crp = qrp->Colresp; + js = (jstring)env->GetObjectArrayElement(s, n++); + sval = GetUTFString(js); + crp->Kdata->SetValue(sval, i); + crp = crp->Next; + js = (jstring)env->GetObjectArrayElement(s, n++); + sval = GetUTFString(js); + crp->Kdata->SetValue(sval, i); + crp = crp->Next; + js = (jstring)env->GetObjectArrayElement(s, n++); + sval = GetUTFString(js); + crp->Kdata->SetValue(sval, i); + crp = crp->Next; + js = (jstring)env->GetObjectArrayElement(s, n++); + sval = GetUTFString(js); + crp->Kdata->SetValue(sval, i); + } // endfor i + + // Not used anymore + env->DeleteLocalRef(s); + + qrp->Nblin = size; + return false; + } // end of GetDrivers + + /**************************************************************************/ + /* GetMetaData: constructs the result blocks containing the */ + /* description of all the columns of an SQL command. */ + /**************************************************************************/ + PQRYRES JDBConn::GetMetaData(PGLOBAL g, PCSZ src) + { + static int buftyp[] = {TYPE_STRING, TYPE_INT, TYPE_INT, + TYPE_INT, TYPE_INT}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC, + FLD_SCALE, FLD_NULL }; + static unsigned int length[] = {0, 6, 10, 6, 6}; + const char *name; + int len, qcol = 5; + PQRYRES qrp = NULL; + PCOLRES crp; + ushort i; + jint *n = nullptr; + jstring label; + jmethodID colid = nullptr; + int rc = ExecuteCommand(src); + + if (rc == RC_NF) { + snprintf(g->Message, sizeof(g->Message), "Srcdef is not returning a result set"); + return NULL; + } else if ((rc) == RC_FX) { + return NULL; + } else if (m_Ncol == 0) { + snprintf(g->Message, sizeof(g->Message), "Invalid Srcdef"); + return NULL; + } // endif's + + if (gmID(g, colid, "ColumnDesc", "(I[I)Ljava/lang/String;")) + return NULL; + + // Get max column name length + len = GetMaxValue(5); + length[0] = (len > 0) ? len + 1 : 128; + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + if (!(qrp = PlgAllocResult(g, qcol, m_Ncol, IDS_COLUMNS + 3, + buftyp, fldtyp, length, false, true))) + return NULL; + + // Some columns must be renamed + for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) + switch (++i) { + case 3: crp->Name = "Precision"; break; + case 4: crp->Name = "Scale"; break; + case 5: crp->Name = "Nullable"; break; + } // endswitch i + + // Build the java int array + jintArray val = env->NewIntArray(4); + + if (val == nullptr) { + strcpy(m_G->Message, "Cannot allocate jint array"); + return NULL; + } // endif colid + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + for (i = 0; i < m_Ncol; i++) { + if (!(label = (jstring)env->CallObjectMethod(job, colid, i + 1, val))) { + if (Check()) + snprintf(g->Message, sizeof(g->Message), "ColumnDesc: %s", Msg); + else + snprintf(g->Message, sizeof(g->Message), "No result metadata"); + + env->ReleaseIntArrayElements(val, n, 0); + return NULL; + } // endif label + + name = GetUTFString(label); + crp = qrp->Colresp; // Column_Name + crp->Kdata->SetValue((char*)name, i); + n = env->GetIntArrayElements(val, 0); + crp = crp->Next; // Data_Type + crp->Kdata->SetValue((int)n[0], i); + crp = crp->Next; // Precision (length) + crp->Kdata->SetValue((int)n[1], i); + crp = crp->Next; // Scale + crp->Kdata->SetValue((int)n[2], i); + crp = crp->Next; // Nullable + crp->Kdata->SetValue((int)n[3], i); + qrp->Nblin++; + } // endfor i + + /* Cleanup */ + env->ReleaseIntArrayElements(val, n, 0); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of GetMetaData + + /***********************************************************************/ + /* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */ + /***********************************************************************/ + int JDBConn::GetCatInfo(JCATPARM *cap) + { + PGLOBAL& g = m_G; +// void *buffer; + int i, ncol; + PCSZ fnc = "Unknown"; + uint n; + short len, tp; + PQRYRES qrp = cap->Qrp; + PCOLRES crp; + jboolean rc = false; +// HSTMT hstmt = NULL; +// SQLLEN *vl, *vlen = NULL; + PVAL *pval = NULL; + char* *pbuf = NULL; + jobjectArray parms; + jmethodID catid = nullptr; + + if (qrp->Maxres <= 0) + return 0; // 0-sized result" + + SQLQualifiedName name(cap); + + // Build the java string array + parms = env->NewObjectArray(4, env->FindClass("java/lang/String"), NULL); + env->SetObjectArrayElement(parms, 0, env->NewStringUTF(name.ptr(2))); + env->SetObjectArrayElement(parms, 1, env->NewStringUTF(name.ptr(1))); + env->SetObjectArrayElement(parms, 2, env->NewStringUTF(name.ptr(0))); + env->SetObjectArrayElement(parms, 3, env->NewStringUTF((const char*)cap->Pat)); + + // Now do call the proper JDBC API + switch (cap->Id) { + case JCAT_COL: + fnc = "GetColumns"; + break; + case JCAT_TAB: + fnc = "GetTables"; + break; +#if 0 + case JCAT_KEY: + fnc = "SQLPrimaryKeys"; + rc = SQLPrimaryKeys(hstmt, name.ptr(2), name.length(2), + name.ptr(1), name.length(1), + name.ptr(0), name.length(0)); + break; +#endif // 0 + default: + snprintf(g->Message, sizeof(g->Message), "Invalid SQL function id"); + return -1; + } // endswitch infotype + + if (gmID(g, catid, fnc, "([Ljava/lang/String;)I")) + return -1; + + // call method + ncol = env->CallIntMethod(job, catid, parms); + + if (Check(ncol)) { + snprintf(g->Message, sizeof(g->Message), "%s: %s", fnc, Msg); + env->DeleteLocalRef(parms); + return -1; + } // endif Check + + // Not used anymore + env->DeleteLocalRef(parms); + + if (trace(1)) + htrc("Method %s returned %d columns\n", fnc, ncol); + + // n because we no more ignore the first column + if ((n = qrp->Nbcol) > (uint)ncol) { + snprintf(g->Message, sizeof(g->Message), MSG(COL_NUM_MISM)); + return -1; + } // endif n + + // Unconditional to handle STRBLK's + pval = (PVAL *)PlugSubAlloc(g, NULL, n * sizeof(PVAL)); +// vlen = (SQLLEN *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN)); + pbuf = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*)); + + // Prepare retrieving column values + for (n = 0, crp = qrp->Colresp; crp; crp = crp->Next) { + if (!(tp = GetJDBCType(crp->Type))) { + snprintf(g->Message, sizeof(g->Message), MSG(INV_COLUMN_TYPE), crp->Type, crp->Name); + return -1; + } // endif tp + + if (!(len = GetTypeSize(crp->Type, crp->Length))) { + len = 255; // for STRBLK's + ((STRBLK*)crp->Kdata)->SetSorted(true); + } // endif len + + pval[n] = AllocateValue(g, crp->Type, len); + pval[n]->SetNullable(true); + + if (crp->Type == TYPE_STRING) { + pbuf[n] = (char*)PlugSubAlloc(g, NULL, len); +// buffer = pbuf[n]; + } // endif Type +// } else +// buffer = pval[n]->GetTo_Val(); + + n++; + } // endfor n + + // Now fetch the result + for (i = 0; i < qrp->Maxres; i++) { + if (Check(rc = Fetch(0))) { + snprintf(g->Message, sizeof(g->Message), "Fetch: %s", Msg); + return -1; + } if (rc == 0) { + if (trace(1)) + htrc("End of fetches i=%d\n", i); + + break; + } // endif rc + + for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) { + SetColumnValue(n + 1, nullptr, pval[n]); + crp->Kdata->SetValue(pval[n], i); + } // endfor n + + } // endfor i + + if (rc > 0) + qrp->Truncated = true; + + return i; + } // end of GetCatInfo + + /***********************************************************************/ + /* Allocate a CONNECT result structure from the JDBC result. */ + /***********************************************************************/ + PQRYRES JDBConn::AllocateResult(PGLOBAL g, PTDB tdbp) + { + bool uns; + PCOL colp; + PCOLRES *pcrp, crp; + PQRYRES qrp; + + if (!m_Rows) { + snprintf(g->Message, sizeof(g->Message), "Void result"); + return NULL; + } // endif m_Rows + + /*********************************************************************/ + /* Allocate the result storage for future retrieval. */ + /*********************************************************************/ + qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES)); + pcrp = &qrp->Colresp; + qrp->Continued = FALSE; + qrp->Truncated = FALSE; + qrp->Info = FALSE; + qrp->Suball = TRUE; + qrp->BadLines = 0; + qrp->Maxsize = m_Rows; + qrp->Maxres = m_Rows; + qrp->Nbcol = 0; + qrp->Nblin = 0; + qrp->Cursor = 0; + + for (colp = tdbp->GetColumns(); colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) { + *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES)); + crp = *pcrp; + pcrp = &crp->Next; + memset(crp, 0, sizeof(COLRES)); + crp->Ncol = ++qrp->Nbcol; + crp->Name = colp->GetName(); + crp->Type = colp->GetResultType(); + crp->Prec = colp->GetScale(); + crp->Length = colp->GetLength(); + crp->Clen = colp->GetValue()->GetClen(); + uns = colp->IsUnsigned(); + + if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows, + crp->Clen, 0, FALSE, TRUE, uns))) { + snprintf(g->Message, sizeof(g->Message), MSG(INV_RESULT_TYPE), + GetFormatType(crp->Type)); + return NULL; + } // endif Kdata + + if (!colp->IsNullable()) + crp->Nulls = NULL; + else { + crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows); + memset(crp->Nulls, ' ', m_Rows); + } // endelse Nullable + + ((EXTCOL*)colp)->SetCrp(crp); + } // endif colp + + *pcrp = NULL; + return qrp; + } // end of AllocateResult |