summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2016-05-12 23:08:22 +0200
committerOlivier Bertrand <bertrandop@gmail.com>2016-05-12 23:08:22 +0200
commit5af076e564602559158db38a69a9cb4a09537986 (patch)
tree4acb32d38b0bcc1c73556e4078d3f45b46a7532d /storage
parent26adbb2dd50db0d62e6f7f5d12bdab0cd3a0bf31 (diff)
downloadmariadb-git-5af076e564602559158db38a69a9cb4a09537986.tar.gz
Add all changes made on 10.1
Diffstat (limited to 'storage')
-rw-r--r--storage/connect/CMakeLists.txt28
-rw-r--r--storage/connect/JdbcInterface.classbin0 -> 15117 bytes
-rw-r--r--storage/connect/JdbcInterface.java639
-rw-r--r--storage/connect/ha_connect.cc282
-rw-r--r--storage/connect/inihandl.c7
-rw-r--r--storage/connect/jdbccat.h29
-rw-r--r--storage/connect/jdbconn.cpp2151
-rw-r--r--storage/connect/jdbconn.h171
-rw-r--r--storage/connect/jsonudf.cpp155
-rw-r--r--storage/connect/jsonudf.h4
-rw-r--r--storage/connect/plgdbutl.cpp34
-rw-r--r--storage/connect/reldef.cpp7
-rw-r--r--storage/connect/tabjdbc.cpp1707
-rw-r--r--storage/connect/tabjdbc.h343
14 files changed, 5398 insertions, 159 deletions
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index 7ff2ca7753c..fa2fb7b4ddd 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -257,6 +257,32 @@ int main() {
ENDIF(UNIX)
ENDIF(CONNECT_WITH_ODBC)
+#
+# JDBC
+#
+
+OPTION(CONNECT_WITH_JDBC "Compile CONNECT storage engine with JDBC support" ON)
+
+IF(CONNECT_WITH_JDBC)
+ # TODO: detect Java SDK and the presence of JDBC connectors
+ # TODO: Find how to compile and install the JdbcInterface.java class
+ # Find required libraries and include directories
+
+ FIND_PACKAGE(Java)
+ FIND_PACKAGE(JNI)
+ IF (JAVA_FOUND AND JNI_FOUND)
+ INCLUDE_DIRECTORIES(${JAVA_INCLUDE_PATH})
+ INCLUDE_DIRECTORIES(${JAVA_INCLUDE_PATH2})
+ # SET(JDBC_LIBRARY ${JAVA_JVM_LIBRARY})
+ SET(CONNECT_SOURCES ${CONNECT_SOURCES}
+ JdbcInterface.java JdbcInterface.class
+ jdbconn.cpp tabjdbc.cpp jdbconn.h tabjdbc.h jdbccat.h)
+ add_definitions(-DJDBC_SUPPORT)
+ ELSE()
+ SET(JDBC_LIBRARY "")
+ ENDIF()
+ENDIF(CONNECT_WITH_JDBC)
+
#
# XMAP
@@ -277,5 +303,5 @@ MYSQL_ADD_PLUGIN(connect ${CONNECT_SOURCES}
COMPONENT connect-engine
RECOMPILE_FOR_EMBEDDED
LINK_LIBRARIES ${ZLIB_LIBRARY} ${XML_LIBRARY} ${ICONV_LIBRARY}
- ${ODBC_LIBRARY} ${IPHLPAPI_LIBRARY})
+ ${ODBC_LIBRARY} ${JDBC_LIBRARY} ${IPHLPAPI_LIBRARY})
diff --git a/storage/connect/JdbcInterface.class b/storage/connect/JdbcInterface.class
new file mode 100644
index 00000000000..816f575212b
--- /dev/null
+++ b/storage/connect/JdbcInterface.class
Binary files differ
diff --git a/storage/connect/JdbcInterface.java b/storage/connect/JdbcInterface.java
new file mode 100644
index 00000000000..5e01516d0cb
--- /dev/null
+++ b/storage/connect/JdbcInterface.java
@@ -0,0 +1,639 @@
+import java.math.*;
+import java.sql.*;
+import java.util.Collections;
+import java.util.List;
+
+public class JdbcInterface {
+ boolean DEBUG = false;
+ Connection conn = null;
+ DatabaseMetaData dbmd = null;
+ Statement stmt = null;
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ ResultSetMetaData rsmd = null;
+
+ // === Constructors/finalize =========================================
+ public JdbcInterface() {
+ this(true);
+ } // end of default constructor
+
+ public JdbcInterface(boolean b) {
+ DEBUG = b;
+ } // end of constructor
+
+ public int JdbcConnect(String[] parms, int fsize, boolean scrollable) {
+ int rc = 0;
+
+ if (DEBUG)
+ System.out.println("In JdbcInterface: driver=" + parms[0]);
+
+ try {
+ if (DEBUG)
+ System.out.println("In try block");
+
+ if (parms[0] != null && !parms[0].isEmpty()) {
+ System.out.println("b is true!");
+ Class.forName(parms[0]); //loads the driver
+ } // endif driver
+
+ if (DEBUG)
+ System.out.println("URL=" + parms[1]);
+
+ if (parms[2] != null && !parms[2].isEmpty()) {
+ if (DEBUG)
+ System.out.println("user=" + parms[2] + " pwd=" + parms[3]);
+
+ conn = DriverManager.getConnection(parms[1], parms[2], parms[3]);
+ } else
+ conn = DriverManager.getConnection(parms[1]);
+
+ if (DEBUG)
+ System.out.println("Connection " + conn.toString() + " established");
+
+ // Get the data base meta data object
+ dbmd = conn.getMetaData();
+
+ // Get a statement from the connection
+ if (scrollable)
+ stmt = conn.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
+ else
+ stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
+
+ if (DEBUG)
+ System.out.println("Statement type = " + stmt.getResultSetType()
+ + " concurrency = " + stmt.getResultSetConcurrency());
+
+ if (DEBUG) // Get the fetch size of a statement
+ System.out.println("Default fetch size = " + stmt.getFetchSize());
+
+ if (fsize != 0) {
+ // Set the fetch size
+ stmt.setFetchSize(fsize);
+
+ if (DEBUG)
+ System.out.println("New fetch size = " + stmt.getFetchSize());
+
+ } // endif fsize
+
+ } catch(ClassNotFoundException e) {
+ System.err.println("ClassNotFoundException: " + e.getMessage());
+ rc = 1;
+ } catch (SQLException se) {
+ System.out.println("SQL Exception:");
+
+ // Loop through the SQL Exceptions
+ while (se != null) {
+ System.out.println("State : " + se.getSQLState());
+ System.out.println("Message: " + se.getMessage());
+ System.out.println("Error : " + se.getErrorCode());
+
+ se = se.getNextException();
+ } // end while se
+
+ rc = 2;
+ } catch( Exception e ) {
+ System.out.println(e);
+ rc = 3;
+ } // end try/catch
+
+ return rc;
+ } // end of JdbcConnect
+
+ public boolean CreatePrepStmt(String sql) {
+ boolean b = false;
+
+ try {
+ pstmt = conn.prepareStatement(sql);
+ } catch (SQLException se) {
+ System.out.println(se);
+ b = true;
+ } catch (Exception e) {
+ System.out.println(e);
+ b = true;
+ } // end try/catch
+
+ return b;
+ } // end of CreatePrepStmt
+
+ public void SetStringParm(int i, String s) {
+ try {
+ pstmt.setString(i, s);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetStringParm
+
+ public void SetIntParm(int i, int n) {
+ try {
+ pstmt.setInt(i, n);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetIntParm
+
+ public void SetShortParm(int i, short n) {
+ try {
+ pstmt.setShort(i, n);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetShortParm
+
+ public void SetBigintParm(int i, long n) {
+ try {
+ pstmt.setLong(i, n);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetBigintParm
+
+ public void SetFloatParm(int i, float f) {
+ try {
+ pstmt.setFloat(i, f);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetFloatParm
+
+ public void SetDoubleParm(int i, double d) {
+ try {
+ pstmt.setDouble(i, d);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetDoubleParm
+
+ public void SetTimestampParm(int i, Timestamp t) {
+ try {
+ pstmt.setTimestamp(i, t);
+ } catch (Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ } // end of SetTimestampParm
+
+ public int ExecutePrep() {
+ int n = -3;
+
+ if (pstmt != null) try {
+ n = pstmt.executeUpdate();
+ } catch (SQLException se) {
+ System.out.println(se);
+ n = -1;
+ } catch (Exception e) {
+ System.out.println(e);
+ n = -2;
+ } //end try/catch
+
+ return n;
+ } // end of ExecutePrep
+
+ public boolean ClosePrepStmt() {
+ boolean b = false;
+
+ if (pstmt != null) try {
+ pstmt.close();
+ pstmt = null;
+ } catch (SQLException se) {
+ System.out.println(se);
+ b = true;
+ } catch (Exception e) {
+ System.out.println(e);
+ b = true;
+ } // end try/catch
+
+ return b;
+ } // end of ClosePrepStmt
+
+ public int JdbcDisconnect() {
+ int rc = 0;
+
+ // Cancel pending statement
+ if (stmt != null)
+ try {
+ System.out.println("Cancelling statement");
+ stmt.cancel();
+ } catch(SQLException se) {
+ System.out.println(se);
+ rc += 1;
+ } // nothing more we can do
+
+ // Close the statement and the connection
+ if (rs != null)
+ try {
+ System.out.println("Closing result set");
+ rs.close();
+ } catch(SQLException se) {
+ System.out.println(se);
+ rc = 2;
+ } // nothing more we can do
+
+ if (stmt != null)
+ try {
+ System.out.println("Closing statement");
+ stmt.close();
+ } catch(SQLException se) {
+ System.out.println(se);
+ rc += 4;
+ } // nothing more we can do
+
+ ClosePrepStmt();
+
+ if (conn != null)
+ try {
+ System.out.println("Closing connection");
+ conn.close();
+ } catch (SQLException se) {
+ System.out.println(se);
+ rc += 8;
+ } //end try/catch
+
+ System.out.println("All closed");
+ return rc;
+ } // end of JdbcDisconnect
+
+ public int GetMaxValue(int n) {
+ int m = 0;
+
+ try {
+ switch (n) {
+ case 1: // Max columns in table
+ m = dbmd.getMaxColumnsInTable();
+ break;
+ case 2: // Max catalog name length
+ m = dbmd.getMaxCatalogNameLength();
+ break;
+ case 3: // Max schema name length
+ m = dbmd.getMaxSchemaNameLength();
+ break;
+ case 4: // Max table name length
+ m = dbmd.getMaxTableNameLength();
+ break;
+ case 5: // Max column name length
+ m = dbmd.getMaxColumnNameLength();
+ break;
+ } // endswitch n
+
+ } catch(Exception e) {
+ System.out.println(e);
+ } // end try/catch
+
+ return m;
+ } // end of GetMaxValue
+
+ public int GetColumns(String[] parms) {
+ int ncol = 0;
+
+ try {
+ if (rs != null) rs.close();
+ rs = dbmd.getColumns(parms[0], parms[1], parms[2], parms[3]);
+
+ if (rs != null) {
+ rsmd = rs.getMetaData();
+ ncol = rsmd.getColumnCount();
+ } // endif rs
+
+ } catch(SQLException se) {
+ System.out.println(se);
+ } // end try/catch
+
+ return ncol;
+ } // end of GetColumns
+
+ public int GetTables(String[] parms) {
+ int ncol = 0;
+ String[] typ = null;
+
+ if (parms[3] != null) {
+ typ = new String[1];
+ typ[0] = parms[3];
+ } // endif parms
+
+ try {
+ if (rs != null) rs.close();
+ rs = dbmd.getTables(parms[0], parms[1], parms[2], typ);
+
+ if (rs != null) {
+ rsmd = rs.getMetaData();
+ ncol = rsmd.getColumnCount();
+ } // endif rs
+
+ } catch(SQLException se) {
+ System.out.println(se);
+ } // end try/catch
+
+ return ncol;
+ } // end of GetColumns
+
+ public int Execute(String query) {
+ int n = 0;
+
+ if (DEBUG)
+ System.out.println("Executing '" + query + "'");
+
+ try {
+ boolean b = stmt.execute(query);
+
+ if (b == false) {
+ n = stmt.getUpdateCount();
+ if (rs != null) rs.close();
+ } // endif b
+
+ if (DEBUG)
+ System.out.println("Query '" + query + "' executed: n = " + n);
+
+ } catch (SQLException se) {
+ System.out.println(se);
+ n = -1;
+ } catch (Exception e) {
+ System.out.println(e);
+ n = -2;
+ } //end try/catch
+
+ return n;
+ } // end of Execute
+
+ public int GetResult() {
+ int ncol = 0;
+
+ try {
+ rs = stmt.getResultSet();
+
+ if (rs != null) {
+ rsmd = rs.getMetaData();
+ ncol = rsmd.getColumnCount();
+
+ if (DEBUG)
+ System.out.println("Result set has " + rsmd.getColumnCount() + " column(s)");
+
+ } // endif rs
+
+ } catch (SQLException se) {
+ System.out.println(se);
+ ncol = -1;
+ } catch (Exception e) {
+ System.out.println(e);
+ ncol = -2;
+ } //end try/catch
+
+ return ncol;
+ } // end of GetResult
+
+ public int ExecuteQuery(String query) {
+ int ncol = 0;
+
+ if (DEBUG)
+ System.out.println("Executing query '" + query + "'");
+
+ try {
+ rs = stmt.executeQuery(query);
+ rsmd = rs.getMetaData();
+ ncol = rsmd.getColumnCount();
+
+ if (DEBUG) {
+ System.out.println("Query '" + query + "' executed successfully");
+ System.out.println("Result set has " + rsmd.getColumnCount() + " column(s)");
+ } // endif DEBUG
+
+ } catch (SQLException se) {
+ System.out.println(se);
+ ncol = -1;
+ } catch (Exception e) {
+ System.out.println(e);
+ ncol = -2;
+ } //end try/catch
+
+ return ncol;
+ } // end of ExecuteQuery
+
+ public int ExecuteUpdate(String query) {
+ int n = 0;
+
+ if (DEBUG)
+ System.out.println("Executing update query '" + query + "'");
+
+ try {
+ n = stmt.executeUpdate(query);
+
+ if (DEBUG)
+ System.out.println("Update Query '" + query + "' executed: n = " + n);
+
+ } catch (SQLException se) {
+ System.out.println(se);
+ n = -1;
+ } catch (Exception e) {
+ System.out.println(e);
+ n = -2;
+ } //end try/catch
+
+ return n;
+ } // end of ExecuteQuery
+
+ public int ReadNext() {
+ if (rs != null) {
+ try {
+ return rs.next() ? 1 : 0;
+ } catch (SQLException se) {
+ System.out.println(se);
+ return -1;
+ } //end try/catch
+
+ } else
+ return 0;
+
+ } // end of ReadNext
+
+ public boolean Fetch(int row) {
+ if (rs != null) {
+ try {
+ return rs.absolute(row);
+ } catch (SQLException se) {
+ System.out.println(se);
+ return false;
+ } //end try/catch
+
+ } else
+ return false;
+
+ } // end of Fetch
+
+ public String ColumnName(int n) {
+ if (rsmd == null) {
+ System.out.println("No result metadata");
+ } else try {
+ return rsmd.getColumnLabel(n);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return null;
+ } // end of ColumnName
+
+ public int ColumnType(int n, String name) {
+ if (rsmd == null) {
+ System.out.println("No result metadata");
+ } else try {
+ if (n == 0)
+ n = rs.findColumn(name);
+
+ return rsmd.getColumnType(n);
+ } catch (SQLException se) {
+ System.out.println("ColumnType: " + se);
+ } //end try/catch
+
+ return 0;
+ } // end of ColumnType
+
+ public String ColumnDesc(int n, int[] val) {
+ if (rsmd == null) {
+ System.out.println("No result metadata");
+ return null;
+ } else try {
+ val[0] = rsmd.getColumnType(n);
+ val[1] = rsmd.getPrecision(n);
+ val[2] = rsmd.getScale(n);
+ val[3] = rsmd.isNullable(n);
+ return rsmd.getColumnLabel(n);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return null;
+ } // end of ColumnType
+
+ public String StringField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getString(n) : rs.getString(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return null;
+ } // end of StringField
+
+ public int IntField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getInt(n) : rs.getInt(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return 0;
+ } // end of IntField
+
+ public long BigintField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ BigDecimal bigDecimal = (n > 0) ? rs.getBigDecimal(n) : rs.getBigDecimal(name);
+ return bigDecimal != null ? bigDecimal.longValue() : 0;
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return 0;
+ } // end of BiginttField
+
+ public double DoubleField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getDouble(n) : rs.getDouble(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return 0.;
+ } // end of DoubleField
+
+ public float FloatField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getFloat(n) : rs.getFloat(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return 0;
+ } // end of FloatField
+
+ public boolean BooleanField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getBoolean(n) : rs.getBoolean(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return false;
+ } // end of BooleanField
+
+ public Date DateField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getDate(n) : rs.getDate(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return null;
+ } // end of DateField
+
+ public Time TimeField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getTime(n) : rs.getTime(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return null;
+ } // end of TimeField
+
+ public Timestamp TimestampField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getTimestamp(n) : rs.getTimestamp(name);
+ } catch (SQLException se) {
+ System.out.println(se);
+ } //end try/catch
+
+ return null;
+ } // end of TimestampField
+
+ public int GetDrivers(String[] s, int mxs) {
+ int n = 0;
+ List<Driver> drivers = Collections.list(DriverManager.getDrivers());
+ int size = Math.min(mxs, drivers.size());
+
+ for (int i = 0; i < size; i++) {
+ Driver driver = (Driver)drivers.get(i);
+
+ // Get name of driver
+ s[n++] = driver.getClass().getName();
+
+ // Get version info
+ s[n++] = driver.getMajorVersion() + "." + driver.getMinorVersion();
+ s[n++] = driver.jdbcCompliant() ? "Yes" : "No";
+ s[n++] = driver.toString();
+ } // endfor i
+
+ return size;
+ } // end of GetDrivers
+
+} // end of class JdbcInterface
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index 2055b93927f..58a761f442d 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -21,8 +21,8 @@
based on external data. Principally they are based on plain files of many
different types, but also on collections of such files, collection of tables,
local or remote MySQL/MariaDB tables retrieved via MySQL API,
- ODBC tables retrieving data from other DBMS having an ODBC server, and even
- virtual tables.
+ ODBC/JDBC tables retrieving data from other DBMS having an ODBC/JDBC server,
+ and even virtual tables.
@details
ha_connect will let you create/open/delete tables, the created table can be
@@ -115,9 +115,6 @@
#include "sql_parse.h"
#include "sql_base.h"
#include <sys/stat.h>
-#if defined(NEW_WAY)
-#include "sql_table.h"
-#endif // NEW_WAY
#include "sql_partition.h"
#undef OFFSET
@@ -130,6 +127,10 @@
#if defined(ODBC_SUPPORT)
#include "odbccat.h"
#endif // ODBC_SUPPORT
+#if defined(JDBC_SUPPORT)
+#include "jdbccat.h"
+#include "jdbconn.h"
+#endif // JDBC_SUPPORT
#include "xtable.h"
#include "tabmysql.h"
#include "filamdbf.h"
@@ -169,7 +170,7 @@
#define JSONMAX 10 // JSON Default max grp size
extern "C" {
- char version[]= "Version 1.04.0006 March 12, 2016";
+ char version[]= "Version 1.04.0006 May 08, 2016";
#if defined(__WIN__)
char compver[]= "Version 1.04.0006 " __DATE__ " " __TIME__;
char slash= '\\';
@@ -190,6 +191,17 @@ extern "C" {
} // extern "C"
#endif // XMSG
+#if defined(JDBC_SUPPORT)
+ char *JvmPath;
+ char *ClassPath;
+#endif // JDBC_SUPPORT
+
+#if defined(__WIN__)
+CRITICAL_SECTION parsec; // Used calling the Flex parser
+#else // !__WIN__
+pthread_mutex_t parmut = PTHREAD_MUTEX_INITIALIZER;
+#endif // !__WIN__
+
/***********************************************************************/
/* Utility functions. */
/***********************************************************************/
@@ -197,6 +209,7 @@ PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
PQRYRES VirColumns(PGLOBAL g, bool info);
PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info);
PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info);
+int TranslateJDBCType(int stp, int prec, int& len, char& v);
void PushWarning(PGLOBAL g, THD *thd, int level);
bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
const char *db, char *tab, const char *src, int port);
@@ -633,6 +646,7 @@ static int connect_init_func(void *p)
#if defined(__WIN__)
sql_print_information("CONNECT: %s", compver);
+ InitializeCriticalSection((LPCRITICAL_SECTION)&parsec);
#else // !__WIN__
sql_print_information("CONNECT: %s", version);
#endif // !__WIN__
@@ -659,6 +673,9 @@ static int connect_init_func(void *p)
DTVAL::SetTimeShift(); // Initialize time zone shift once for all
BINCOL::SetEndian(); // Initialize host endian setting
+#if defined(JDBC_SUPPORT)
+ JDBConn::SetJVM();
+#endif // JDBC_SUPPORT
DBUG_RETURN(0);
} // end of connect_init_func
@@ -675,11 +692,17 @@ static int connect_done_func(void *)
#ifdef LIBXML2_SUPPORT
XmlCleanupParserLib();
-#endif // LIBXML2_SUPPORT
+#endif // LIBXML2_SUPPORT
+
+#ifdef JDBC_SUPPORT
+ JDBConn::ResetJVM();
+#endif // JDBC_SUPPORT
-#if !defined(__WIN__)
-//PROFILE_End(); Causes signal 11
-#endif // !__WIN__
+#if defined(__WIN__)
+ DeleteCriticalSection((LPCRITICAL_SECTION)&parsec);
+#else // !__WIN__
+ PROFILE_End();
+#endif // !__WIN__
for (pc= user_connect::to_users; pc; pc= pn) {
if (pc->g)
@@ -1758,9 +1781,10 @@ int ha_connect::OpenTable(PGLOBAL g, bool del)
break;
} // endswitch xmode
- if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_ODBC
- || tdbp->GetAmType() == TYPE_AM_MYSQL) {
- // Get the list of used fields (columns)
+ if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_MYSQL
+ || tdbp->GetAmType() == TYPE_AM_ODBC
+ || tdbp->GetAmType() == TYPE_AM_JDBC) {
+ // Get the list of used fields (columns)
char *p;
unsigned int k1, k2, n1, n2;
Field* *field;
@@ -2077,8 +2101,9 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *)
continue; // Is a virtual column possible here ???
if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
- && tdbp->GetAmType() != TYPE_AM_ODBC) ||
- bitmap_is_set(table->write_set, fp->field_index)) {
+ && tdbp->GetAmType() != TYPE_AM_ODBC
+ && tdbp->GetAmType() != TYPE_AM_JDBC) ||
+ bitmap_is_set(table->write_set, fp->field_index)) {
for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
if (!stricmp(colp->GetName(), fp->field_name))
break;
@@ -2627,7 +2652,7 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
} // end of CondFilter
/***********************************************************************/
-/* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */
+/* Check the WHERE condition and return a MYSQL/ODBC/JDBC/WQL filter. */
/***********************************************************************/
PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
{
@@ -2635,8 +2660,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
char *body= filp->Body;
unsigned int i;
bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
- bool nonul= (tty == TYPE_AM_ODBC && (tdbp->GetMode() == MODE_INSERT ||
- tdbp->GetMode() == MODE_DELETE));
+ bool nonul= ((tty == TYPE_AM_ODBC || tty == TYPE_AM_JDBC) &&
+ (tdbp->GetMode() == MODE_INSERT || tdbp->GetMode() == MODE_DELETE));
OPVAL vop= OP_XX;
if (!cond)
@@ -2958,7 +2983,7 @@ const COND *ha_connect::cond_push(const COND *cond)
bool x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
bool b= (tty == TYPE_AM_WMI || tty == TYPE_AM_ODBC ||
tty == TYPE_AM_TBL || tty == TYPE_AM_MYSQL ||
- tty == TYPE_AM_PLG || x);
+ tty == TYPE_AM_PLG || tty == TYPE_AM_JDBC || x);
// Save stack and allocation environment and prepare error return
if (g->jump_level == MAX_JUMP) {
@@ -4108,7 +4133,8 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
/* Fall through to check FILE_ACL */
case TAB_ODBC:
- case TAB_MYSQL:
+ case TAB_JDBC:
+ case TAB_MYSQL:
case TAB_DIR:
case TAB_MAC:
case TAB_WMI:
@@ -5124,11 +5150,17 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
int port= 0, hdr= 0, mxr= 0, mxe= 0, rc= 0;
int cop __attribute__((unused))= 0, lrecl= 0;
#if defined(ODBC_SUPPORT)
- POPARM sop = NULL;
- char *ucnc = NULL;
+ POPARM sop= NULL;
+ char *ucnc= NULL;
bool cnc= false;
int cto= -1, qto= -1;
#endif // ODBC_SUPPORT
+#if defined(JDBC_SUPPORT)
+ PJPARM sjp= NULL;
+ char *driver= NULL;
+ char *url= NULL;
+ char *tabtyp = NULL;
+#endif // JDBC_SUPPORT
uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
bool bif, ok= false, dbf= false;
TABTYPE ttp= TAB_UNDEF;
@@ -5139,15 +5171,10 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
PDBUSER dup= PlgGetUser(g);
PCATLG cat= (dup) ? dup->Catalog : NULL;
PTOS topt= table_s->option_struct;
-#if defined(NEW_WAY)
-//CHARSET_INFO *cs;
- Alter_info alter_info;
-#else // !NEW_WAY
char buf[1024];
String sql(buf, sizeof(buf), system_charset_info);
sql.copy(STRING_WITH_LEN("CREATE TABLE whatever ("), system_charset_info);
-#endif // !NEW_WAY
if (!g)
return HA_ERR_INTERNAL_ERROR;
@@ -5166,7 +5193,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
spc= (!sep) ? ',' : *sep;
qch= topt->qchar ? *topt->qchar : (signed)topt->quoted >= 0 ? '"' : 0;
hdr= (int)topt->header;
- tbl= topt->tablist;
+ tbl= topt->tablist;
col= topt->colist;
if (topt->oplist) {
@@ -5195,6 +5222,11 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
if ((ucnc= GetListOption(g, "UseDSN", topt->oplist)))
cnc= (!*ucnc || *ucnc == 'y' || *ucnc == 'Y' || atoi(ucnc) != 0);
#endif
+#if defined(JDBC_SUPPORT)
+ driver= GetListOption(g, "Driver", topt->oplist, NULL);
+ url= GetListOption(g, "URL", topt->oplist, NULL);
+ tabtyp = GetListOption(g, "Tabtype", topt->oplist, NULL);
+#endif // JDBC_SUPPORT
mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
#if defined(PROMPT_OK)
cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
@@ -5255,44 +5287,62 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
} else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
tab= table_s->table_name.str; // Default value
-#if defined(NEW_WAY)
-// add_option(thd, create_info, "tabname", tab);
-#endif // NEW_WAY
} // endif tab
- switch (ttp) {
+ switch (ttp) {
#if defined(ODBC_SUPPORT)
- case TAB_ODBC:
- dsn= strz(g, create_info->connect_string);
+ case TAB_ODBC:
+ dsn= strz(g, create_info->connect_string);
- if (fnc & (FNC_DSN | FNC_DRIVER)) {
- ok= true;
+ if (fnc & (FNC_DSN | FNC_DRIVER)) {
+ ok= true;
#if defined(PROMPT_OK)
- } else if (!stricmp(thd->main_security_ctx.host, "localhost")
- && cop == 1) {
- if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) {
- thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
- ok= true;
- } // endif dsn
+ } else if (!stricmp(thd->main_security_ctx.host, "localhost")
+ && cop == 1) {
+ if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) {
+ thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
+ ok= true;
+ } // endif dsn
#endif // PROMPT_OK
- } else if (!dsn) {
- sprintf(g->Message, "Missing %s connection string", topt->type);
- } else {
- // Store ODBC additional parameters
- sop= (POPARM)PlugSubAlloc(g, NULL, sizeof(ODBCPARM));
- sop->User= (char*)user;
- sop->Pwd= (char*)pwd;
- sop->Cto= cto;
- sop->Qto= qto;
- sop->UseCnc= cnc;
- ok= true;
- } // endif's
-
- supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
- break;
+ } else if (!dsn) {
+ sprintf(g->Message, "Missing %s connection string", topt->type);
+ } else {
+ // Store ODBC additional parameters
+ sop= (POPARM)PlugSubAlloc(g, NULL, sizeof(ODBCPARM));
+ sop->User= (char*)user;
+ sop->Pwd= (char*)pwd;
+ sop->Cto= cto;
+ sop->Qto= qto;
+ sop->UseCnc= cnc;
+ ok= true;
+ } // endif's
+
+ supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
+ break;
#endif // ODBC_SUPPORT
- case TAB_DBF:
+#if defined(JDBC_SUPPORT)
+ case TAB_JDBC:
+ if (fnc & FNC_DRIVER) {
+ ok= true;
+ } else if (!url && !(url= strz(g, create_info->connect_string))) {
+ strcpy(g->Message, "Missing URL");
+ } else {
+ // Store ODBC additional parameters
+ sjp= (PJPARM)PlugSubAlloc(g, NULL, sizeof(JDBCPARM));
+ sjp->Driver= driver;
+ sjp->Url= url;
+ sjp->User= (char*)user;
+ sjp->Pwd= (char*)pwd;
+ sjp->Fsize= 0;
+ sjp->Scrollable= false;
+ ok= true;
+ } // endif's
+
+ supfnc |= (FNC_DRIVER | FNC_TABLE);
+ break;
+#endif // JDBC_SUPPORT
+ case TAB_DBF:
dbf= true;
// Passthru
case TAB_CSV:
@@ -5411,7 +5461,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
dpath= SetPath(g, table_s->db.str);
- if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) {
+ if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC && ttp != TAB_JDBC) {
qrp= SrcColumns(g, host, db, user, pwd, src, port);
if (qrp && ttp == TAB_OCCUR)
@@ -5453,7 +5503,37 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
break;
#endif // ODBC_SUPPORT
- case TAB_MYSQL:
+#if defined(JDBC_SUPPORT)
+ case TAB_JDBC:
+ switch (fnc) {
+ case FNC_NO:
+ case FNC_COL:
+ if (src) {
+ qrp= JDBCSrcCols(g, (char*)src, sjp);
+ src= NULL; // for next tests
+ } else
+ qrp= JDBCColumns(g, shm, tab, NULL, mxr, fnc == FNC_COL, sjp);
+
+ break;
+ case FNC_TABLE:
+ qrp= JDBCTables(g, shm, tab, tabtyp, mxr, true, sjp);
+ break;
+#if 0
+ case FNC_DSN:
+ qrp= JDBCDataSources(g, mxr, true);
+ break;
+#endif // 0
+ case FNC_DRIVER:
+ qrp= JDBCDrivers(g, mxr, true);
+ break;
+ default:
+ sprintf(g->Message, "invalid catfunc %s", fncn);
+ break;
+ } // endswitch info
+
+ break;
+#endif // JDBC_SUPPORT
+ case TAB_MYSQL:
qrp= MyColumns(g, thd, host, db, user, pwd, tab,
NULL, port, fnc == FNC_COL);
break;
@@ -5526,14 +5606,9 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
len= 256; // STRBLK's have 0 length
// Now add the field
-#if defined(NEW_WAY)
- rc= add_fields(g, thd, &alter_info, cnm, typ, len, dec,
- tm, "", flg, dbf, v);
-#else // !NEW_WAY
if (add_field(&sql, cnm, typ, len, dec, NULL, tm,
NULL, NULL, NULL, NULL, flg, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
-#endif // !NEW_WAY
} // endfor crp
} else {
@@ -5556,12 +5631,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
cnm= (char*)"noname";
dft= xtra= key= fmt= NULL;
v= ' ';
-#if defined(NEW_WAY)
- rem= "";
-// cs= NULL;
-#else // !NEW_WAY
rem= NULL;
-#endif // !NEW_WAY
for (crp= qrp->Colresp; crp; crp= crp->Next)
switch (crp->Fld) {
@@ -5630,10 +5700,10 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
"Several %s tables found, specify DBNAME", tab);
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
goto err;
- } else if (!schem)
+ } else if (!schem)
schem= crp->Kdata->GetCharValue(i);
- } // endif ttp
+ } // endif ttp
#endif // ODBC_SUPPORT
default:
break; // Ignore
@@ -5682,7 +5752,42 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
} else
#endif // ODBC_SUPPORT
- // Make the arguments as required by add_fields
+#if defined(JDBC_SUPPORT)
+ if (ttp == TAB_JDBC) {
+ int plgtyp;
+
+ // typ must be PLG type, not SQL type
+ if (!(plgtyp= TranslateJDBCType(typ, dec, prec, v))) {
+ if (GetTypeConv() == TPC_SKIP) {
+ // Skip this column
+ sprintf(g->Message, "Column %s skipped (unsupported type %d)",
+ cnm, typ);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ continue;
+ } else {
+ sprintf(g->Message, "Unsupported SQL type %d", typ);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ goto err;
+ } // endif type_conv
+
+ } else
+ typ= plgtyp;
+
+ switch (typ) {
+ case TYPE_DOUBLE:
+ // Some data sources do not count dec in length (prec)
+ prec += (dec + 2); // To be safe
+ break;
+ case TYPE_DECIM:
+ prec= len;
+ break;
+ default:
+ dec= 0;
+ } // endswitch typ
+
+ } else
+#endif // ODBC_SUPPORT
+ // Make the arguments as required by add_fields
if (typ == TYPE_DOUBLE)
prec= len;
@@ -5690,25 +5795,15 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
prec= 0;
// Now add the field
-#if defined(NEW_WAY)
- rc= add_fields(g, thd, &alter_info, cnm, typ, prec, dec,
- tm, rem, 0, dbf, v);
-#else // !NEW_WAY
if (add_field(&sql, cnm, typ, prec, dec, key, tm, rem, dft, xtra,
fmt, 0, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
-#endif // !NEW_WAY
} // endfor i
} // endif fnc
-#if defined(NEW_WAY)
- rc= init_table_share(thd, table_s, create_info, &alter_info);
-#else // !NEW_WAY
if (!rc)
rc= init_table_share(thd, table_s, create_info, &sql);
-// rc= init_table_share(thd, table_s, create_info, dsn, &sql);
-#endif // !NEW_WAY
g->jump_level--;
return rc;
@@ -6750,6 +6845,21 @@ static MYSQL_SYSVAR_STR(errmsg_dir_path, msg_path,
"../../../../storage/connect/"); // for testing
#endif // XMSG
+#if defined(JDBC_SUPPORT)
+static MYSQL_SYSVAR_STR(jvm_path, JvmPath,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "Path to the directory where is the JVM lib",
+ // check_jvm_path, update_jvm_path,
+ NULL, NULL, NULL);
+
+static MYSQL_SYSVAR_STR(class_path, ClassPath,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "Java class path",
+ // check_class_path, update_class_path,
+ NULL, NULL, NULL);
+#endif // JDBC_SUPPORT
+
+
static struct st_mysql_sys_var* connect_system_variables[]= {
MYSQL_SYSVAR(xtrace),
MYSQL_SYSVAR(conv_size),
@@ -6767,7 +6877,11 @@ static struct st_mysql_sys_var* connect_system_variables[]= {
MYSQL_SYSVAR(errmsg_dir_path),
#endif // XMSG
MYSQL_SYSVAR(json_grp_size),
- NULL
+#if defined(JDBC_SUPPORT)
+ MYSQL_SYSVAR(jvm_path),
+ MYSQL_SYSVAR(class_path),
+#endif // JDBC_SUPPORT
+ NULL
};
maria_declare_plugin(connect)
diff --git a/storage/connect/inihandl.c b/storage/connect/inihandl.c
index 542b807f899..46102557b20 100644
--- a/storage/connect/inihandl.c
+++ b/storage/connect/inihandl.c
@@ -622,13 +622,16 @@ void PROFILE_End(void)
if (trace)
htrc("PROFILE_End: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
+ if (!CurProfile) // Sergey Vojtovich
+ return;
+
/* Close all opened files and free the cache structure */
for (i = 0; i < N_CACHED_PROFILES; i++) {
if (trace)
htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
- CurProfile = MRUProfile[i];
- PROFILE_ReleaseFile();
+// CurProfile = MRUProfile[i]; Sergey Vojtovich
+// PROFILE_ReleaseFile(); see MDEV-9997
free(MRUProfile[i]);
} // endfor i
diff --git a/storage/connect/jdbccat.h b/storage/connect/jdbccat.h
new file mode 100644
index 00000000000..37f33d7063d
--- /dev/null
+++ b/storage/connect/jdbccat.h
@@ -0,0 +1,29 @@
+// Timeout and net wait defaults
+#define DEFAULT_LOGIN_TIMEOUT -1 // means do not set
+#define DEFAULT_QUERY_TIMEOUT -1 // means do not set
+
+typedef struct jdbc_parms {
+ int CheckSize(int rows);
+ char *Driver; // JDBC driver
+ char *Url; // Driver URL
+ char *User; // User connect info
+ char *Pwd; // Password connect info
+//int Cto; // Connect timeout
+//int Qto; // Query timeout
+ int Fsize; // Fetch size
+ bool Scrollable; // Scrollable cursor
+} JDBCPARM, *PJPARM;
+
+/***********************************************************************/
+/* JDBC catalog function prototypes. */
+/***********************************************************************/
+#if defined(PROMPT_OK)
+char *JDBCCheckConnection(PGLOBAL g, char *dsn, int cop);
+#endif // PROMPT_OK
+//PQRYRES JDBCDataSources(PGLOBAL g, int maxres, bool info);
+PQRYRES JDBCColumns(PGLOBAL g, char *db, char *table,
+ char *colpat, int maxres, bool info, PJPARM sop);
+PQRYRES JDBCSrcCols(PGLOBAL g, char *src, PJPARM sop);
+PQRYRES JDBCTables(PGLOBAL g, char *db, char *tabpat,
+ char *tabtyp, int maxres, bool info, PJPARM sop);
+PQRYRES JDBCDrivers(PGLOBAL g, int maxres, bool info);
diff --git a/storage/connect/jdbconn.cpp b/storage/connect/jdbconn.cpp
new file mode 100644
index 00000000000..88557fdd420
--- /dev/null
+++ b/storage/connect/jdbconn.cpp
@@ -0,0 +1,2151 @@
+/************ Jdbconn C++ Functions Source Code File (.CPP) ************/
+/* Name: JDBCONN.CPP Version 1.0 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* */
+/* This file contains the JDBC connection classes functions. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include <my_global.h>
+#include <m_string.h>
+#if defined(__WIN__)
+//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 // !__WIN__
+#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 // !__WIN__
+
+/***********************************************************************/
+/* Required objects includes. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "xobject.h"
+#include "xtable.h"
+#include "jdbccat.h"
+#include "tabjdbc.h"
+//#include "jdbconn.h"
+//#include "plgcnx.h" // For DB types
+#include "resource.h"
+#include "valblk.h"
+#include "osutil.h"
+
+
+#if defined(__WIN__)
+extern "C" HINSTANCE s_hModule; // Saved module handle
+#else // !__WIN__
+#define nullptr 0
+#endif // !__WIN__
+
+int GetConvSize();
+extern char *JvmPath; // The connect_jvm_path global variable value
+extern char *ClassPath; // The connect_class_path global variable value
+
+/***********************************************************************/
+/* Static JDBConn objects. */
+/***********************************************************************/
+void *JDBConn::LibJvm = NULL;
+CRTJVM JDBConn::CreateJavaVM = NULL;
+GETJVM JDBConn::GetCreatedJavaVMs = NULL;
+
+/***********************************************************************/
+/* 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, 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, int prec, int& len, char& v)
+{
+ int type;
+
+ switch (stp) {
+ case -1: // LONGVARCHAR
+ len = MY_MIN(abs(len), GetConvSize());
+ case 12: // VARCHAR
+ v = 'V';
+ case 1: // CHAR
+ type = TYPE_STRING;
+ break;
+ case 2: // NUMERIC
+ case 3: // DECIMAL
+ type = TYPE_DECIM;
+ break;
+ case 4: // INTEGER
+ type = TYPE_INT;
+ break;
+ case 5: // SMALLINT
+ type = TYPE_SHORT;
+ break;
+ case -6: // TINYINT
+ case -7: // BIT
+ type = TYPE_TINY;
+ break;
+ case 6: // FLOAT
+ case 7: // REAL
+ case 8: // DOUBLE
+ type = TYPE_DOUBLE;
+ break;
+ case 93: // TIMESTAMP
+ type = TYPE_DATE;
+ len = 19 + ((prec) ? (prec+1) : 0);
+ v = 'S';
+ break;
+ case 91: // TYPE_DATE
+ type = TYPE_DATE;
+ len = 10;
+ v = 'D';
+ break;
+ case 92: // TYPE_TIME
+ type = TYPE_DATE;
+ len = 8 + ((prec) ? (prec+1) : 0);
+ v = 'T';
+ break;
+ case -5: // BIGINT
+ type = TYPE_BIGINT;
+ break;
+ case 0: // NULL
+ case -2: // BINARY
+ case -3: // VARBINARY
+ case -4: // LONGVARBINARY
+ default:
+ type = TYPE_ERROR;
+ len = 0;
+ } // endswitch type
+
+ return type;
+} // end of TranslateJDBCType
+
+/***********************************************************************/
+/* Allocate the structure used to refer to the result set. */
+/***********************************************************************/
+static JCATPARM *AllocCatInfo(PGLOBAL g, JCATINFO fid, char *db,
+ char *tab, PQRYRES qrp)
+{
+//size_t m, n;
+ JCATPARM *cap;
+
+#if defined(_DEBUG)
+ assert(qrp);
+#endif
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return NULL;
+ } // endif jump_level
+
+ if (setjmp(g->jumper[++g->jump_level]) != 0) {
+ printf("%s\n", g->Message);
+ cap = NULL;
+ goto fin;
+ } // endif rc
+
+//m = (size_t)qrp->Maxres;
+//n = (size_t)qrp->Nbcol;
+ cap = (JCATPARM *)PlugSubAlloc(g, NULL, sizeof(JCATPARM));
+ memset(cap, 0, sizeof(JCATPARM));
+ cap->Id = fid;
+ cap->Qrp = qrp;
+ cap->DB = (PUCHAR)db;
+ cap->Tab = (PUCHAR)tab;
+//cap->Vlen = (SQLLEN* *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN *));
+
+//for (i = 0; i < n; i++)
+// cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SQLLEN));
+
+//cap->Status = (UWORD *)PlugSubAlloc(g, NULL, m * sizeof(UWORD));
+
+fin:
+ g->jump_level--;
+ 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, char *db, char *table, char *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->Open(sjp) != RC_OK) // 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)
+ 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)
+ htrc("Getting col results ncol=%d\n", qrp->Nbcol);
+
+ if (!(cap = AllocCatInfo(g, CAT_COL, db, table, qrp)))
+ return NULL;
+
+ cap->Pat = (PUCHAR)colpat;
+
+ /************************************************************************/
+ /* Now get the results into blocks. */
+ /************************************************************************/
+ if ((n = jcp->GetCatInfo(cap)) >= 0) {
+ qrp->Nblin = n;
+ // ResetNullValues(cap);
+
+ if (trace)
+ 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, char *src, PJPARM sjp)
+{
+ JDBConn *jcp = new(g)JDBConn(g, NULL);
+
+ if (jcp->Open(sjp))
+ return NULL;
+
+ return jcp->GetMetaData(g, src);
+} // 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, char *db, char *tabpat, char *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->Open(sjp) == RC_FX)
+ 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) ? (n + 1) : 0;
+ n = jcp->GetMaxValue(3); // Max schema name length
+ length[1] = (n) ? (n + 1) : 0;
+ n = jcp->GetMaxValue(4); // Max table name length
+ length[2] = (n) ? (n + 1) : 128;
+ } else {
+ maxres = 0;
+ length[0] = 128;
+ length[1] = 128;
+ length[2] = 128;
+ length[4] = 255;
+ } // endif info
+
+ if (trace)
+ 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;
+
+ if (!(cap = AllocCatInfo(g, CAT_TAB, db, tabpat, qrp)))
+ return NULL;
+
+ cap->Pat = (PUCHAR)tabtyp;
+
+ if (trace)
+ 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)
+ 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(NULL) != RC_OK)
+ return NULL;
+
+ if (!maxres)
+ maxres = 256; // Estimated max number of drivers
+
+ } else
+ maxres = 0;
+
+ if (trace)
+ 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
+
+#if 0
+/*************************************************************************/
+/* JDBCDataSources: constructs the result blocks containing all JDBC */
+/* data sources available on the local host. */
+/* Called with info=true to have result column names. */
+/*************************************************************************/
+PQRYRES JDBCDataSources(PGLOBAL g, int maxres, bool info)
+{
+ int buftyp[] ={ TYPE_STRING, TYPE_STRING };
+ XFLD fldtyp[] ={ FLD_NAME, FLD_REM };
+ unsigned int length[] ={ 0, 256 };
+ bool b[] ={ false, true };
+ int i, n = 0, ncol = 2;
+ PCOLRES crp;
+ PQRYRES qrp;
+ JDBConn *jcp = NULL;
+
+ /************************************************************************/
+ /* Do an evaluation of the result size. */
+ /************************************************************************/
+ if (!info) {
+ jcp = new(g)JDBConn(g, NULL);
+ n = jcp->GetMaxValue(SQL_MAX_DSN_LENGTH);
+ length[0] = (n) ? (n + 1) : 256;
+
+ if (!maxres)
+ maxres = 512; // Estimated max number of data sources
+
+ } else {
+ length[0] = 256;
+ maxres = 0;
+ } // endif info
+
+ if (trace)
+ htrc("JDBCDataSources: max=%d len=%d\n", maxres, length[0]);
+
+ /************************************************************************/
+ /* Allocate the structures used to refer to the result set. */
+ /************************************************************************/
+ qrp = PlgAllocResult(g, ncol, maxres, IDS_DSRC,
+ buftyp, fldtyp, length, false, true);
+
+ for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
+ if (b[i])
+ crp->Kdata->SetNullable(true);
+
+ /************************************************************************/
+ /* Now get the results into blocks. */
+ /************************************************************************/
+ if (!info && qrp && jcp->GetDataSources(qrp))
+ qrp = NULL;
+
+ /************************************************************************/
+ /* Return the result pointer for use by GetData routines. */
+ /************************************************************************/
+ return qrp;
+} // end of JDBCDataSources
+
+/**************************************************************************/
+/* PrimaryKeys: constructs the result blocks containing all the */
+/* JDBC catalog information concerning primary keys. */
+/**************************************************************************/
+PQRYRES JDBCPrimaryKeys(PGLOBAL g, JDBConn *op, char *dsn, char *table)
+{
+ static int buftyp[] ={ TYPE_STRING, TYPE_STRING, TYPE_STRING,
+ TYPE_STRING, TYPE_SHORT, TYPE_STRING };
+ static unsigned int length[] ={ 0, 0, 0, 0, 6, 128 };
+ int n, ncol = 5;
+ int maxres;
+ PQRYRES qrp;
+ JCATPARM *cap;
+ JDBConn *jcp = op;
+
+ if (!op) {
+ /**********************************************************************/
+ /* Open the connection with the JDBC data source. */
+ /**********************************************************************/
+ jcp = new(g)JDBConn(g, NULL);
+
+ if (jcp->Open(dsn, 2) < 1) // 2 is openReadOnly
+ return NULL;
+
+ } // endif op
+
+ /************************************************************************/
+ /* Do an evaluation of the result size. */
+ /************************************************************************/
+ n = jcp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
+ maxres = (n) ? (int)n : 250;
+ n = jcp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
+ length[0] = (n) ? (n + 1) : 128;
+ n = jcp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
+ length[1] = (n) ? (n + 1) : 128;
+ n = jcp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
+ length[2] = (n) ? (n + 1) : 128;
+ n = jcp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
+ length[3] = (n) ? (n + 1) : 128;
+
+ if (trace)
+ htrc("JDBCPrimaryKeys: max=%d len=%d,%d,%d\n",
+ maxres, length[0], length[1], length[2]);
+
+ /************************************************************************/
+ /* Allocate the structure used to refer to the result set. */
+ /************************************************************************/
+ qrp = PlgAllocResult(g, ncol, maxres, IDS_PKEY,
+ buftyp, NULL, length, false, true);
+
+ if (trace)
+ htrc("Getting pkey results ncol=%d\n", qrp->Nbcol);
+
+ cap = AllocCatInfo(g, CAT_KEY, NULL, table, qrp);
+
+ /************************************************************************/
+ /* Now get the results into blocks. */
+ /************************************************************************/
+ if ((n = jcp->GetCatInfo(cap)) >= 0) {
+ qrp->Nblin = n;
+ // ResetNullValues(cap);
+
+ if (trace)
+ htrc("PrimaryKeys: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
+
+ } else
+ qrp = NULL;
+
+ /************************************************************************/
+ /* Close any local connection. */
+ /************************************************************************/
+ if (!op)
+ jcp->Close();
+
+ /************************************************************************/
+ /* Return the result pointer for use by GetData routines. */
+ /************************************************************************/
+ return qrp;
+} // end of JDBCPrimaryKeys
+#endif // 0
+
+/***********************************************************************/
+/* JDBConn construction/destruction. */
+/***********************************************************************/
+JDBConn::JDBConn(PGLOBAL g, TDBJDBC *tdbp)
+{
+ m_G = g;
+ m_Tdb = tdbp;
+ jvm = nullptr; // Pointer to the JVM (Java Virtual Machine)
+ env= nullptr; // Pointer to native interface
+ jdi = nullptr; // Pointer to the JdbcInterface class
+ job = nullptr; // The JdbcInterface class object
+ xqid = xuid = xid = grs = readid = fetchid = typid = nullptr;
+ prepid = xpid = pcid = nullptr;
+//m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT;
+//m_QueryTimeout = DEFAULT_QUERY_TIMEOUT;
+//m_UpdateOptions = 0;
+ m_Driver = NULL;
+ m_Url = NULL;
+ m_User = NULL;
+ m_Pwd = NULL;
+ 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;
+ //*m_ErrMsg = '\0';
+} // end of JDBConn
+
+//JDBConn::~JDBConn()
+// {
+//if (Connected())
+// EndCom();
+
+// } // end of ~JDBConn
+
+/***********************************************************************/
+/* Screen for errors. */
+/***********************************************************************/
+char *JDBConn::Check(void)
+{
+ if (env->ExceptionCheck()) {
+ char *msg;
+ jthrowable exc = env->ExceptionOccurred();
+ jmethodID tid = env->GetMethodID(env->FindClass("java/lang/Object"),
+ "toString", "()Ljava/lang/String;");
+
+ if (exc != nullptr && tid != nullptr) {
+ jstring s = (jstring)env->CallObjectMethod(exc, tid);
+ const char *utf = env->GetStringUTFChars(s, (jboolean)false);
+ env->DeleteLocalRef(s);
+ msg = PlugDup(m_G, utf);
+ } else
+ msg = "Exception occured";
+
+ env->ExceptionClear();
+ return msg;
+ } // endif Check
+
+ return NULL;
+} // end of Check
+
+#if 0
+/***********************************************************************/
+/* Utility routine. */
+/***********************************************************************/
+PSZ JDBConn::GetStringInfo(ushort infotype)
+{
+ //ASSERT(m_hdbc != SQL_NULL_HDBC);
+ char *p, buffer[MAX_STRING_INFO];
+ SWORD result;
+ RETCODE rc;
+
+ rc = SQLGetInfo(m_hdbc, infotype, buffer, sizeof(buffer), &result);
+
+ if (!Check(rc)) {
+ ThrowDJX(rc, "SQLGetInfo"); // Temporary
+ // *buffer = '\0';
+ } // endif rc
+
+ p = PlugDup(m_G, buffer);
+ return p;
+} // end of GetStringInfo
+
+/***********************************************************************/
+/* Utility routines. */
+/***********************************************************************/
+void JDBConn::OnSetOptions(HSTMT hstmt)
+{
+ RETCODE rc;
+ ASSERT(m_hdbc != SQL_NULL_HDBC);
+
+ if ((signed)m_QueryTimeout != -1) {
+ // Attempt to set query timeout. Ignore failure
+ rc = SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT, m_QueryTimeout);
+
+ if (!Check(rc))
+ // don't attempt it again
+ m_QueryTimeout = (DWORD)-1;
+
+ } // endif m_QueryTimeout
+
+ if (m_RowsetSize > 0) {
+ // Attempt to set rowset size.
+ // In case of failure reset it to 0 to use Fetch.
+ rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
+
+ if (!Check(rc))
+ // don't attempt it again
+ m_RowsetSize = 0;
+
+ } // endif m_RowsetSize
+
+} // end of OnSetOptions
+#endif // 0
+
+/***********************************************************************/
+/* Utility routine. */
+/***********************************************************************/
+int JDBConn::GetMaxValue(int n)
+{
+ jmethodID maxid = env->GetMethodID(jdi, "GetMaxValue", "(I)I");
+
+ if (maxid == nullptr) {
+ strcpy(m_G->Message, "ERROR: method GetMaxValue not found !");
+ return -1;
+ } // endif maxid
+
+ // call method
+ return (int)env->CallIntMethod(job, maxid, n);
+} // end of GetMaxValue
+
+/***********************************************************************/
+/* Reset the JVM library. */
+/***********************************************************************/
+void JDBConn::ResetJVM(void)
+{
+ if (LibJvm) {
+#if defined(__WIN__)
+ FreeLibrary((HMODULE)LibJvm);
+#else // !__WIN__
+ dlclose(LibJvm);
+#endif // !__WIN__
+ LibJvm = NULL;
+ CreateJavaVM = NULL;
+ GetCreatedJavaVMs = NULL;
+ } // endif LibJvm
+
+} // end of ResetJVM
+
+/***********************************************************************/
+/* Dynamically link the JVM library. */
+/* The purpose of this function is to allow using the CONNECT plugin */
+/* for other table types when the Java JDK is not installed. */
+/***********************************************************************/
+bool JDBConn::GetJVM(PGLOBAL g)
+{
+ if (!LibJvm) {
+ char soname[512];
+
+#if defined(__WIN__)
+ if (JvmPath)
+ strcat(strcpy(soname, JvmPath), "\\jvm.dll");
+ else
+ strcpy(soname, "jvm.dll");
+
+ // Load the desired shared library
+ if (!(LibJvm = LoadLibrary(soname))) {
+ char buf[256];
+ DWORD rc = GetLastError();
+
+ sprintf(g->Message, MSG(DLL_LOAD_ERROR), rc, soname);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ strcat(strcat(g->Message, ": "), buf);
+ } else if (!(CreateJavaVM = (CRTJVM)GetProcAddress((HINSTANCE)LibJvm,
+ "JNI_CreateJavaVM"))) {
+ sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), "JNI_CreateJavaVM");
+ FreeLibrary((HMODULE)LibJvm);
+ LibJvm = NULL;
+ } else if (!(GetCreatedJavaVMs = (GETJVM)GetProcAddress((HINSTANCE)LibJvm,
+ "JNI_GetCreatedJavaVMs"))) {
+ sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), "JNI_GetCreatedJavaVMs");
+ FreeLibrary((HMODULE)LibJvm);
+ LibJvm = NULL;
+ } // endif LibJvm
+#else // !__WIN__
+ const char *error = NULL;
+
+ if (JvmPath)
+ strcat(strcpy(soname, JvmPath), "/libjvm.so");
+ else
+ strcpy(soname, "libjvm.so");
+
+ // Load the desired shared library
+ if (!(LibJvm = dlopen(soname, RTLD_LAZY))) {
+ error = dlerror();
+ sprintf(g->Message, MSG(SHARED_LIB_ERR), soname, SVP(error));
+ } else if (!(CreateJavaVM = (CRTJVM)dlsym(LibJvm, "JNI_CreateJavaVM"))) {
+ error = dlerror();
+ sprintf(g->Message, MSG(GET_FUNC_ERR), "JNI_CreateJavaVM", SVP(error));
+ dlclose(LibJvm);
+ LibJvm = NULL;
+ } else if (!(GetCreatedJavaVMs = (GETJVM)dlsym(LibJvm, "JNI_GetCreatedJavaVMs"))) {
+ error = dlerror();
+ sprintf(g->Message, MSG(GET_FUNC_ERR), "JNI_GetCreatedJavaVMs", SVP(error));
+ dlclose(LibJvm);
+ LibJvm = NULL;
+ } // endif LibJvm
+#endif // !__WIN__
+
+ } // endif LibJvm
+
+ return LibJvm == NULL;
+} // end of GetJVM
+
+/***********************************************************************/
+/* Open: connect to a data source. */
+/***********************************************************************/
+int JDBConn::Open(PJPARM sop)
+{
+ PGLOBAL& g = m_G;
+
+ // Link or check whether jvm library was linked
+ if (GetJVM(g))
+ return RC_FX;
+
+ // Firstly check whether the jvm was already created
+ JavaVM* jvms[1];
+ jsize jsz;
+ jint rc = GetCreatedJavaVMs(jvms, 1, &jsz);
+
+ if (rc == JNI_OK && jsz == 1) {
+ // jvm already existing
+ jvm = jvms[0];
+ rc = jvm->AttachCurrentThread((void**)&env, nullptr);
+
+ if (rc != JNI_OK) {
+ strcpy(g->Message, "Cannot attach jvm to the current thread");
+ return RC_FX;
+ } // endif rc
+
+ } else {
+ // Create a new jvm
+ PSTRG jpop = new(g)STRING(g, 512, "-Djava.class.path=");
+ char *cp = NULL;
+ char sep;
+
+#if defined(__WIN__)
+ sep = ';';
+#else
+ sep = ':';
+#endif
+
+ //================== prepare loading of Java VM ============================
+ JavaVMInitArgs vm_args; // Initialization arguments
+ JavaVMOption* options = new JavaVMOption[1]; // JVM invocation options
+
+ // where to find java .class
+ if ((cp = PlugDup(m_G, getenv("CLASSPATH"))))
+ jpop->Append(cp);
+
+ if (trace) {
+ htrc("CLASSPATH=%s\n", getenv("CLASSPATH"));
+ htrc("ClassPath=%s\n", ClassPath);
+ } // endif trace
+
+ if (ClassPath && *ClassPath) {
+ if (cp)
+ jpop->Append(sep);
+
+ jpop->Append(ClassPath);
+ } // endif ClassPath
+
+ if (trace)
+ htrc("%s\n", jpop->GetStr());
+
+ options[0].optionString = jpop->GetStr();
+ //options[1].optionString = "-verbose:jni";
+ vm_args.version = JNI_VERSION_1_6; // minimum Java version
+ vm_args.nOptions = 1; // number of options
+ vm_args.options = options;
+ vm_args.ignoreUnrecognized = false; // invalid options make the JVM init fail
+
+ //=============== load and initialize Java VM and JNI interface =============
+ rc = CreateJavaVM(&jvm, (void**)&env, &vm_args); // YES !!
+ delete options; // we then no longer need the initialisation options.
+
+ switch (rc) {
+ case JNI_OK:
+ strcpy(g->Message, "VM successfully created");
+ break;
+ case JNI_ERR:
+ strcpy(g->Message, "Initialising JVM failed: unknown error");
+ return RC_FX;
+ case JNI_EDETACHED:
+ strcpy(g->Message, "Thread detached from the VM");
+ return RC_FX;
+ case JNI_EVERSION:
+ strcpy(g->Message, "JNI version error");
+ return RC_FX;
+ case JNI_ENOMEM:
+ strcpy(g->Message, "Not enough memory");
+ return RC_FX;
+ case JNI_EEXIST:
+ strcpy(g->Message, "VM already created");
+ return RC_FX;
+ case JNI_EINVAL:
+ strcpy(g->Message, "Invalid arguments");
+ return RC_FX;
+ default:
+ sprintf(g->Message, "Unknown return code %d", rc);
+ return RC_FX;
+ } // endswitch rc
+
+ } // endif rc
+
+ //=============== Display JVM version =======================================
+//jint ver = env->GetVersion();
+//cout << ((ver>>16)&0x0f) << "."<<(ver&0x0f) << endl;
+
+ // try to find the JdbcInterface class
+ jdi = env->FindClass("JdbcInterface");
+
+ if (jdi == nullptr) {
+ strcpy(g->Message, "ERROR: class JdbcInterface not found !");
+ return RC_FX;
+ } // endif jdi
+
+#if 0 // Suppressed because it does not make any usable change
+ if (b && jpath && *jpath) {
+ // Try to add that path the the jvm class path
+ jmethodID alp = env->GetStaticMethodID(jdi, "addLibraryPath",
+ "(Ljava/lang/String;)I");
+
+ if (alp == nullptr) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ } else {
+ char *msg;
+ jstring path = env->NewStringUTF(jpath);
+ rc = env->CallStaticIntMethod(jdi, alp, path);
+
+ if ((msg = Check())) {
+ strcpy(g->Message, msg);
+ env->DeleteLocalRef(path);
+ return RC_FX;
+ } else switch (rc) {
+ case JNI_OK:
+ printf("jpath added\n");
+ break;
+ case JNI_EEXIST:
+ printf("jpath already exist\n");
+ break;
+ case JNI_ERR:
+ default:
+ strcpy(g->Message, "Error adding jpath");
+ env->DeleteLocalRef(path);
+ return RC_FX;
+ } // endswitch rc
+
+ env->DeleteLocalRef(path);
+ } // endif alp
+
+ } // endif jpath
+#endif // 0
+
+ // if class found, continue
+ jmethodID ctor = env->GetMethodID(jdi, "<init>", "()V");
+
+ if (ctor == nullptr) {
+ strcpy(g->Message, "ERROR: JdbcInterface constructor not found !");
+ return RC_FX;
+ } else
+ job = env->NewObject(jdi, ctor);
+
+ // If the object is successfully constructed,
+ // we can then search for the method we want to call,
+ // and invoke it for the object:
+ if (job == nullptr) {
+ strcpy(g->Message, "JdbcInterface class object not constructed !");
+ return RC_FX;
+ } // endif job
+
+ if (!sop) // DRIVER catalog table
+ return RC_OK;
+
+ jmethodID cid = env->GetMethodID(jdi, "JdbcConnect", "([Ljava/lang/String;IZ)I");
+
+ if (env->ExceptionCheck()) {
+ strcpy(g->Message, "ERROR: method JdbcConnect() not found!");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return RC_FX;
+ } // endif Check
+
+ // Build the java string array
+ jobjectArray parms = env->NewObjectArray(4, // constructs java array of 4
+ env->FindClass("java/lang/String"), NULL); // Strings
+
+ if (sop) {
+ m_Driver = sop->Driver;
+ m_Url = sop->Url;
+ m_User = sop->User;
+ m_Pwd = sop->Pwd;
+ m_Scrollable = sop->Scrollable;
+ m_RowsetSize = sop->Fsize;
+ //m_LoginTimeout = sop->Cto;
+ //m_QueryTimeout = sop->Qto;
+ //m_UseCnc = sop->UseCnc;
+ } // endif sop
+
+ // change some elements
+ if (m_Driver)
+ env->SetObjectArrayElement(parms, 0, env->NewStringUTF(m_Driver));
+
+ if (m_Url)
+ env->SetObjectArrayElement(parms, 1, env->NewStringUTF(m_Url));
+
+ if (m_User)
+ env->SetObjectArrayElement(parms, 2, env->NewStringUTF(m_User));
+
+ if (m_Pwd)
+ env->SetObjectArrayElement(parms, 3, env->NewStringUTF(m_Pwd));
+
+ // call method
+ rc = env->CallIntMethod(job, cid, parms, m_RowsetSize, m_Scrollable);
+
+ // Not used anymore
+ env->DeleteLocalRef(parms);
+
+ if (rc != (jint)0) {
+ strcpy(g->Message, "Connection failed");
+ return RC_FX;
+ } // endif rc
+
+ typid = env->GetMethodID(jdi, "ColumnType", "(ILjava/lang/String;)I");
+
+ if (env->ExceptionCheck()) {
+ strcpy(g->Message, "ERROR: method ColumnType() not found!");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return RC_FX;
+ } else
+ m_Opened = true;
+
+ return RC_OK;
+} // end of Open
+
+/***********************************************************************/
+/* Execute an SQL command. */
+/***********************************************************************/
+int JDBConn::ExecSQLcommand(char *sql)
+{
+ int rc = RC_NF;
+ jint n;
+ jstring qry;
+ PGLOBAL& g = m_G;
+
+ if (xid == nullptr) {
+ // Get the methods used to execute a query and get the result
+ xid = env->GetMethodID(jdi, "Execute", "(Ljava/lang/String;)I");
+
+ if (xid == nullptr) {
+ strcpy(g->Message, "Cannot find method Execute");
+ return RC_FX;
+ } else
+ grs = env->GetMethodID(jdi, "GetResult", "()I");
+
+ if (grs == nullptr) {
+ strcpy(g->Message, "Cannot find method GetResult");
+ return RC_FX;
+ } // endif grs
+
+ } // endif xid
+
+ qry = env->NewStringUTF(sql);
+ n = env->CallIntMethod(job, xid, qry);
+ env->DeleteLocalRef(qry);
+
+ if (n < 0) {
+ sprintf(g->Message, "Error executing %s", sql);
+ return RC_FX;
+ } // endif n
+
+ if ((m_Ncol = env->CallIntMethod(job, grs))) {
+ strcpy(g->Message, "Result set column number");
+ rc = RC_OK; // A result set was returned
+ } else {
+ m_Aff = (int)n; // Affected rows
+ strcpy(g->Message, "Affected rows");
+ } // endif ncol
+
+ return rc;
+} // end of ExecSQLcommand
+
+/***********************************************************************/
+/* Fetch next row. */
+/***********************************************************************/
+int JDBConn::Fetch(int pos)
+{
+ jint rc;
+
+ if (m_Full) // Result set has one row
+ return 1;
+
+ if (pos) {
+ if (!m_Scrollable) {
+ strcpy(m_G->Message, "Cannot fetch(pos) if FORWARD ONLY");
+ return -1;
+ } else if (fetchid == nullptr) {
+ fetchid = env->GetMethodID(jdi, "Fetch", "(I)Z");
+
+ if (fetchid == nullptr) {
+ strcpy(m_G->Message, "Cannot find method Fetch");
+ return -1;
+ } // endif fetchid
+
+ } // endif's
+
+ if (env->CallBooleanMethod(job, fetchid, pos))
+ rc = m_Rows;
+ else
+ rc = -1;
+
+ } else {
+ if (readid == nullptr) {
+ readid = env->GetMethodID(jdi, "ReadNext", "()I");
+
+ if (readid == nullptr) {
+ strcpy(m_G->Message, "Cannot find method ReadNext");
+ return -1;
+ } // endif readid
+
+ } // endif readid
+
+ rc = env->CallBooleanMethod(job, readid);
+
+ if (rc >= 0) {
+ if (rc == 0)
+ m_Full = (m_Fetch == 1);
+ else
+ m_Fetch++;
+
+ m_Rows += (int)rc;
+ } else
+ strcpy(m_G->Message, "Error fetching next row");
+
+ } // endif pos
+
+ return (int)rc;
+} // end of Fetch
+
+/***********************************************************************/
+/* Restart from beginning of result set */
+/***********************************************************************/
+int JDBConn::Rewind(char *sql)
+{
+ int rbuf = -1;
+
+ if (m_Full)
+ rbuf = m_Rows; // No need to "rewind"
+ else if (m_Scrollable) {
+ if (fetchid == nullptr) {
+ fetchid = env->GetMethodID(jdi, "Fetch", "(I)Z");
+
+ if (fetchid == nullptr) {
+ strcpy(m_G->Message, "Cannot find method Fetch");
+ return -1;
+ } // endif readid
+
+ } // endif readid
+
+ jboolean b = env->CallBooleanMethod(job, fetchid, 0);
+
+ rbuf = m_Rows;
+ } else if (ExecSQLcommand(sql) != RC_FX)
+ rbuf = 0;
+
+ return rbuf;
+} // end of Rewind
+
+/***********************************************************************/
+/* Disconnect connection */
+/***********************************************************************/
+void JDBConn::Close()
+{
+ if (m_Opened) {
+ jint rc;
+ jmethodID did = env->GetMethodID(jdi, "JdbcDisconnect", "()I");
+
+ if (did == nullptr)
+ printf("ERROR: method JdbcDisconnect() not found !");
+ else if ((rc = env->CallIntMethod(job, did)))
+ printf("jdbcDisconnect: rc=%d", (int)rc);
+
+ if ((rc = jvm->DetachCurrentThread()))
+ printf("DetachCurrentThread: rc = %d", (int)rc);
+
+ //rc = jvm->DestroyJavaVM();
+ m_Opened = false;
+ } // endif m_Opened
+
+} // end of Close
+
+/***********************************************************************/
+/* Retrieve and set the column value from the result set. */
+/***********************************************************************/
+void JDBConn::SetColumnValue(int rank, PSZ name, PVAL val)
+{
+ PGLOBAL& g = m_G;
+ char *msg;
+ jint ctyp;
+ jlong dtv;
+ jstring cn, jn = nullptr;
+ jobject dob;
+ jmethodID fldid = nullptr;
+
+ if (rank == 0)
+ if (!name || (jn = env->NewStringUTF(name)) == nullptr) {
+ sprintf(g->Message, "Fail to allocate jstring %s", SVP(name));
+ longjmp(g->jumper[g->jump_level], TYPE_AM_JDBC);
+ } // endif name
+
+ ctyp = env->CallIntMethod(job, typid, rank, jn);
+
+ if ((msg = Check())) {
+ sprintf(g->Message, "Getting ctyp: %s", msg);
+ longjmp(g->jumper[g->jump_level], TYPE_AM_JDBC);
+ } // endif Check
+
+ switch (ctyp) {
+ case 12: // VARCHAR
+ case -1: // LONGVARCHAR
+ case 1: // CHAR
+ fldid = env->GetMethodID(jdi, "StringField",
+ "(ILjava/lang/String;)Ljava/lang/String;");
+
+ if (fldid != nullptr) {
+ cn = (jstring)env->CallObjectMethod(job, fldid, (jint)rank, jn);
+
+ if (cn) {
+ const char *field = env->GetStringUTFChars(cn, (jboolean)false);
+ val->SetValue_psz((PSZ)field);
+ } else {
+ val->Reset();
+ val->SetNull(true);
+ } // endif cn
+
+ } else
+ val->Reset();
+
+ break;
+ case 4: // INTEGER
+ case 5: // SMALLINT
+ case -6: // TINYINT
+ fldid = env->GetMethodID(jdi, "IntField", "(ILjava/lang/String;)I");
+
+ if (fldid != nullptr)
+ val->SetValue((int)env->CallIntMethod(job, fldid, rank, jn));
+ else
+ val->Reset();
+
+ break;
+ case 8: // DOUBLE
+ case 3: // DECIMAL
+ fldid = env->GetMethodID(jdi, "DoubleField", "(ILjava/lang/String;)D");
+
+ if (fldid != nullptr)
+ val->SetValue((double)env->CallDoubleMethod(job, fldid, rank, jn));
+ else
+ val->Reset();
+
+ break;
+ case 7: // REAL
+ case 6: // FLOAT
+ fldid = env->GetMethodID(jdi, "FloatField", "(ILjava/lang/String;)F");
+
+ if (fldid != nullptr)
+ val->SetValue((float)env->CallFloatMethod(job, fldid, rank, jn));
+ else
+ val->Reset();
+
+ break;
+ case 91: // DATE
+ case 92: // TIME
+ case 93: // TIMESTAMP
+ fldid = env->GetMethodID(jdi, "TimestampField",
+ "(ILjava/lang/String;)Ljava/sql/Timestamp;");
+
+ if (fldid != nullptr) {
+ dob = env->CallObjectMethod(job, fldid, (jint)rank, jn);
+
+ if (dob) {
+ jclass jts = env->FindClass("java/sql/Timestamp");
+
+ if (env->ExceptionCheck()) {
+ val->Reset();
+ } else {
+ jmethodID getTime = env->GetMethodID(jts, "getTime", "()J");
+
+ if (getTime != nullptr) {
+ dtv = env->CallLongMethod(dob, getTime);
+ val->SetValue((int)(dtv / 1000));
+ } else
+ val->Reset();
+
+ } // endif check
+
+ } else
+ val->Reset();
+
+ } else
+ val->Reset();
+
+ break;
+ case -5: // BIGINT
+ fldid = env->GetMethodID(jdi, "BigintField", "(ILjava/lang/String;)J");
+
+ if (fldid != nullptr)
+ val->SetValue((long long)env->CallLongMethod(job, fldid, (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)); */
+ default:
+ val->Reset();
+ } // endswitch Type
+
+ if ((msg = Check())) {
+ if (rank == 0)
+ env->DeleteLocalRef(jn);
+
+ sprintf(g->Message, "SetColumnValue: %s rank=%d ctyp=%d", msg, rank, (int)ctyp);
+ longjmp(g->jumper[g->jump_level], TYPE_AM_JDBC);
+ } // endif Check
+
+ if (rank == 0)
+ env->DeleteLocalRef(jn);
+
+} // end of SetColumnValue
+
+/***********************************************************************/
+/* Prepare an SQL statement for insert. */
+/***********************************************************************/
+bool JDBConn::PrepareSQL(char *sql)
+{
+ if (prepid == nullptr) {
+ prepid = env->GetMethodID(jdi, "CreatePrepStmt", "(Ljava/lang/String;)Z");
+
+ if (prepid == nullptr) {
+ strcpy(m_G->Message, "Cannot find method CreatePrepStmt");
+ return true;
+ } // endif prepid
+
+ } // endif prepid
+
+ // Create the prepared statement
+ jstring qry = env->NewStringUTF(sql);
+ jboolean b = env->CallBooleanMethod(job, prepid, qry);
+ env->DeleteLocalRef(qry);
+ return (bool)b;
+} // end of PrepareSQL
+
+/***********************************************************************/
+/* Execute an SQL query that returns a result set. */
+/***********************************************************************/
+int JDBConn::ExecuteQuery(char *sql)
+{
+ jint ncol;
+ jstring qry;
+ PGLOBAL& g = m_G;
+
+ if (xqid == nullptr) {
+ // Get the methods used to execute a query and get the result
+ xqid = env->GetMethodID(jdi, "ExecuteQuery", "(Ljava/lang/String;)I");
+
+ if (xqid == nullptr) {
+ strcpy(g->Message, "Cannot find method ExecuteQuery");
+ return RC_FX;
+ } // endif !xqid
+
+ } // endif xqid
+
+ qry = env->NewStringUTF(sql);
+ ncol = env->CallIntMethod(job, xqid, qry);
+ env->DeleteLocalRef(qry);
+
+ if (ncol < 0) {
+ sprintf(g->Message, "Error executing %s: ncol = %d", sql, ncol);
+ return RC_FX;
+ } else {
+ m_Ncol = (int)ncol;
+ m_Aff = 0; // Affected rows
+ } // endif ncol
+
+ return RC_OK;
+} // end of ExecuteQuery
+
+/***********************************************************************/
+/* Execute an SQL query and get the affected rows. */
+/***********************************************************************/
+int JDBConn::ExecuteUpdate(char *sql)
+{
+ jint n;
+ jstring qry;
+ PGLOBAL& g = m_G;
+
+ if (xuid == nullptr) {
+ // Get the methods used to execute a query and get the affected rows
+ xuid = env->GetMethodID(jdi, "ExecuteUpdate", "(Ljava/lang/String;)I");
+
+ if (xuid == nullptr) {
+ strcpy(g->Message, "Cannot find method ExecuteUpdate");
+ return RC_FX;
+ } // endif !xuid
+
+ } // endif xuid
+
+ qry = env->NewStringUTF(sql);
+ n = env->CallIntMethod(job, xuid, qry);
+ env->DeleteLocalRef(qry);
+
+ if (n < 0) {
+ sprintf(g->Message, "Error executing %s: n = %d", sql, n);
+ return RC_FX;
+ } else {
+ m_Ncol = 0;
+ m_Aff = (int)n; // Affected rows
+ } // endif n
+
+ return RC_OK;
+} // end of ExecuteUpdate
+
+/***********************************************************************/
+/* Get the number of lines of the result set. */
+/***********************************************************************/
+int JDBConn::GetResultSize(char *sql, JDBCCOL *colp)
+{
+ int rc, n = 0;
+
+ if ((rc = ExecuteQuery(sql)) != RC_OK)
+ return -1;
+
+ if ((rc = Fetch()) > 0)
+ SetColumnValue(1, NULL, colp->GetValue());
+ 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;
+
+ if (xpid == nullptr) {
+ // Get the methods used to execute a prepared statement
+ xpid = env->GetMethodID(jdi, "ExecutePrep", "()I");
+
+ if (xpid == nullptr) {
+ strcpy(g->Message, "Cannot find method ExecutePrep");
+ return rc;
+ } // endif xpid
+
+ } // endif xpid
+
+ jint n = env->CallIntMethod(job, xpid);
+
+ switch ((int)n) {
+ case -1:
+ case -2:
+ strcpy(g->Message, "Exception error thrown while executing SQL");
+ break;
+ case -3:
+ strcpy(g->Message, "SQL statement is not prepared");
+ break;
+ default:
+ m_Aff = (int)n;
+ rc = RC_OK;
+ } // endswitch n
+
+ return rc;
+} // end of ExecuteSQL
+
+/***********************************************************************/
+/* Set a parameter for inserting. */
+/***********************************************************************/
+bool JDBConn::SetParam(JDBCCOL *colp)
+{
+ PGLOBAL& g = m_G;
+ char *msg;
+ int rc = false;
+ PVAL val = colp->GetValue();
+ jint n, i = (jint)colp->GetRank();
+ jshort s;
+ jlong lg;
+//jfloat f;
+ jdouble d;
+ jclass dat;
+ jobject datobj;
+ jstring jst = nullptr;
+ jmethodID dtc, setid = nullptr;
+
+ switch (val->GetType()) {
+ case TYPE_STRING:
+ setid = env->GetMethodID(jdi, "SetStringParm", "(ILjava/lang/String;)V");
+
+ if (setid == nullptr) {
+ strcpy(g->Message, "Cannot fing method SetStringParm");
+ return true;
+ } // endif setid
+
+ jst = env->NewStringUTF(val->GetCharValue());
+ env->CallVoidMethod(job, setid, i, jst);
+ break;
+ case TYPE_INT:
+ setid = env->GetMethodID(jdi, "SetIntParm", "(II)V");
+
+ if (setid == nullptr) {
+ strcpy(g->Message, "Cannot fing method SetIntParm");
+ return true;
+ } // endif setid
+
+ n = (jint)val->GetIntValue();
+ env->CallVoidMethod(job, setid, i, n);
+ break;
+ case TYPE_TINY:
+ case TYPE_SHORT:
+ setid = env->GetMethodID(jdi, "SetShortParm", "(IS)V");
+
+ if (setid == nullptr) {
+ strcpy(g->Message, "Cannot fing method SetShortParm");
+ return true;
+ } // endif setid
+
+ s = (jshort)val->GetShortValue();
+ env->CallVoidMethod(job, setid, i, s);
+ break;
+ case TYPE_BIGINT:
+ setid = env->GetMethodID(jdi, "SetBigintParm", "(IJ)V");
+
+ if (setid == nullptr) {
+ strcpy(g->Message, "Cannot fing method SetBigintParm");
+ return true;
+ } // endif setid
+
+ lg = (jlong)val->GetBigintValue();
+ env->CallVoidMethod(job, setid, i, lg);
+ break;
+ case TYPE_DOUBLE:
+ case TYPE_DECIM:
+ setid = env->GetMethodID(jdi, "SetDoubleParm", "(ID)V");
+
+ if (setid == nullptr) {
+ strcpy(g->Message, "Cannot find method SetDoubleParm");
+ return true;
+ } // endif setid
+
+ d = (jdouble)val->GetFloatValue();
+ env->CallVoidMethod(job, setid, i, d);
+ break;
+ case TYPE_DATE:
+ if ((dat = env->FindClass("java/sql/Timestamp")) == nullptr) {
+ strcpy(g->Message, "Cannot find Timestamp class");
+ return true;
+ } else if (!(dtc = env->GetMethodID(dat, "<init>", "(J)V"))) {
+ strcpy(g->Message, "Cannot find Timestamp class constructor");
+ return true;
+ } // endif's
+
+ lg = (jlong)val->GetBigintValue() * 1000;
+
+ if ((datobj = env->NewObject(dat, dtc, lg)) == nullptr) {
+ strcpy(g->Message, "Cannot make Timestamp object");
+ return true;
+ } else if ((setid = env->GetMethodID(jdi, "SetTimestampParm",
+ "(ILjava/sql/Timestamp;)V")) == nullptr) {
+ strcpy(g->Message, "Cannot find method SetTimestampParm");
+ return true;
+ } // endif setid
+
+ env->CallVoidMethod(job, setid, i, datobj);
+ break;
+ default:
+ sprintf(g->Message, "Parm type %d not supported", val->GetType());
+ return true;
+ } // endswitch Type
+
+ if ((msg = Check())) {
+ sprintf(g->Message, "SetParam: col=%s msg=%s", colp->GetName(), msg);
+ rc = true;
+ } // endif msg
+
+ if (jst)
+ env->DeleteLocalRef(jst);
+
+ return rc;
+ } // end of SetParam
+
+#if 0
+ /***********************************************************************/
+ /* Get the list of Data Sources and set it in qrp. */
+ /***********************************************************************/
+ bool JDBConn::GetDataSources(PQRYRES qrp)
+ {
+ bool rv = false;
+ UCHAR *dsn, *des;
+ UWORD dir = SQL_FETCH_FIRST;
+ SWORD n1, n2, p1, p2;
+ PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
+ RETCODE rc;
+
+ n1 = crp1->Clen;
+ n2 = crp2->Clen;
+
+ try {
+ rc = SQLAllocEnv(&m_henv);
+
+ if (!Check(rc))
+ ThrowDJX(rc, "SQLAllocEnv"); // Fatal
+
+ for (int i = 0; i < qrp->Maxres; i++) {
+ dsn = (UCHAR*)crp1->Kdata->GetValPtr(i);
+ des = (UCHAR*)crp2->Kdata->GetValPtr(i);
+ rc = SQLDataSources(m_henv, dir, dsn, n1, &p1, des, n2, &p2);
+
+ if (rc == SQL_NO_DATA_FOUND)
+ break;
+ else if (!Check(rc))
+ ThrowDJX(rc, "SQLDataSources");
+
+ qrp->Nblin++;
+ dir = SQL_FETCH_NEXT;
+ } // endfor i
+
+ }
+ catch (DJX *x) {
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
+ rv = true;
+ } // end try/catch
+
+ Close();
+ return rv;
+ } // end of GetDataSources
+#endif // 0
+
+ /***********************************************************************/
+ /* 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 = env->GetMethodID(jdi, "GetDrivers", "([Ljava/lang/String;I)I");
+
+ if (env->ExceptionCheck()) {
+ strcpy(m_G->Message, "ERROR: method GetDrivers() not found!");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return true;
+ } // endif Check
+
+ // 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 = (PSZ)env->GetStringUTFChars(js, 0);
+ crp->Kdata->SetValue(sval, i);
+ crp = crp->Next;
+ js = (jstring)env->GetObjectArrayElement(s, n++);
+ sval = (PSZ)env->GetStringUTFChars(js, 0);
+ crp->Kdata->SetValue(sval, i);
+ crp = crp->Next;
+ js = (jstring)env->GetObjectArrayElement(s, n++);
+ sval = (PSZ)env->GetStringUTFChars(js, 0);
+ crp->Kdata->SetValue(sval, i);
+ crp = crp->Next;
+ js = (jstring)env->GetObjectArrayElement(s, n++);
+ sval = (PSZ)env->GetStringUTFChars(js, 0);
+ 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, char *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;
+ int rc = ExecSQLcommand(src);
+
+ if (rc == RC_NF) {
+ strcpy(g->Message, "Srcdef is not returning a result set");
+ return NULL;
+ } else if ((rc) == RC_FX) {
+ return NULL;
+ } else if (m_Ncol == 0) {
+ strcpy(g->Message, "Invalid Srcdef");
+ return NULL;
+ } // endif's
+
+ colid = env->GetMethodID(jdi, "ColumnDesc", "(I[I)Ljava/lang/String;");
+
+ if (colid == nullptr) {
+ strcpy(m_G->Message, "ERROR: method ColumnDesc() not found!");
+ return NULL;
+ } // endif colid
+
+ // Build the java string array
+ jintArray val = env->NewIntArray(4);
+
+ if (val == nullptr) {
+ strcpy(m_G->Message, "Cannot allocate jint array");
+ return NULL;
+ } // endif colid
+
+ // 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
+
+ /************************************************************************/
+ /* Now get the results into blocks. */
+ /************************************************************************/
+ for (i = 0; i < m_Ncol; i++) {
+ label = (jstring)env->CallObjectMethod(job, colid, i + 1, val);
+ name = env->GetStringUTFChars(label, (jboolean)false);
+ 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);
+ Close();
+
+ /************************************************************************/
+ /* Return the result pointer for use by GetData routines. */
+ /************************************************************************/
+ return qrp;
+ } // end of GetMetaData
+
+ /***********************************************************************/
+ /* 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 recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */
+ /***********************************************************************/
+ int JDBConn::GetCatInfo(JCATPARM *cap)
+ {
+ PGLOBAL& g = m_G;
+// void *buffer;
+ int i;
+ PSZ fnc = "Unknown";
+ uint n, ncol;
+ short len, tp;
+ int crow = 0;
+ 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;
+
+ 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)));
+
+ if (cap->Pat)
+ env->SetObjectArrayElement(parms, 3, env->NewStringUTF((const char*)cap->Pat));
+
+ // Now do call the proper JDBC API
+ switch (cap->Id) {
+ case CAT_COL:
+ fnc = "GetColumns";
+ break;
+ case CAT_TAB:
+ fnc = "GetTables";
+ break;
+#if 0
+ case CAT_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;
+ case CAT_STAT:
+ fnc = "SQLStatistics";
+ rc = SQLStatistics(hstmt, name.ptr(2), name.length(2),
+ name.ptr(1), name.length(1),
+ name.ptr(0), name.length(0),
+ cap->Unique, cap->Accuracy);
+ break;
+ case CAT_SPC:
+ ThrowDJX("SQLSpecialColumns not available yet");
+#endif // 0
+ default:
+ sprintf(g->Message, "Invalid SQL function id");
+ return -1;
+ } // endswitch infotype
+
+ catid = env->GetMethodID(jdi, fnc, "([Ljava/lang/String;)I");
+
+ if (catid == nullptr) {
+ sprintf(g->Message, "ERROR: method %s not found !", fnc);
+ return -1;
+ } // endif maxid
+
+ // call method
+ ncol = env->CallIntMethod(job, catid, parms);
+
+ // Not used anymore
+ env->DeleteLocalRef(parms);
+
+ if (trace)
+ htrc("Method %s returned %d columns\n", fnc, ncol);
+
+ // n because we no more ignore the first column
+ if ((n = qrp->Nbcol) > (uint)ncol) {
+ strcpy(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))) {
+ sprintf(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);
+
+ 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 ((rc = Fetch(0)) == 0) {
+ if (trace)
+ htrc("End of fetches i=%d\n", i);
+
+ break;
+ } else if (rc < 0)
+ return -1;
+
+ 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)
+ {
+ bool uns;
+ PJDBCCOL colp;
+ PCOLRES *pcrp, crp;
+ PQRYRES qrp;
+
+ if (!m_Rows) {
+ strcpy(g->Message, "Void result");
+ return NULL;
+ } // endif m_Res
+
+ /*********************************************************************/
+ /* 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 = (PJDBCCOL)m_Tdb->Columns; colp;
+ colp = (PJDBCCOL)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))) {
+ sprintf(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
+
+ colp->SetCrp(crp);
+ } // endif colp
+
+ *pcrp = NULL;
+ //qrp->Nblin = n;
+ return qrp;
+ } // end of AllocateResult
diff --git a/storage/connect/jdbconn.h b/storage/connect/jdbconn.h
new file mode 100644
index 00000000000..4014a52cdcc
--- /dev/null
+++ b/storage/connect/jdbconn.h
@@ -0,0 +1,171 @@
+/***********************************************************************/
+/* JDBConn.h : header file for the JDBC connection classes. */
+/***********************************************************************/
+//nclude <windows.h> /* Windows include file */
+//nclude <windowsx.h> /* Message crackers */
+
+/***********************************************************************/
+/* Included C-definition files required by the interface. */
+/***********************************************************************/
+#include "block.h"
+
+/***********************************************************************/
+/* JDBC interface. */
+/***********************************************************************/
+#include <jni.h>
+
+/***********************************************************************/
+/* Constants and defines. */
+/***********************************************************************/
+// Miscellaneous sizing info
+#define MAX_NUM_OF_MSG 10 // Max number of error messages
+//efine MAX_CURRENCY 30 // Max size of Currency($) string
+#define MAX_TNAME_LEN 32 // Max size of table names
+//efine MAX_FNAME_LEN 256 // Max size of field names
+//efine MAX_STRING_INFO 256 // Max size of string from SQLGetInfo
+//efine MAX_DNAME_LEN 256 // Max size of Recordset names
+#define MAX_CONNECT_LEN 512 // Max size of Connect string
+//efine MAX_CURSOR_NAME 18 // Max size of a cursor name
+#define DEFAULT_FIELD_TYPE 0 // TYPE_NULL
+
+#if !defined(__WIN__)
+typedef unsigned char *PUCHAR;
+#endif // !__WIN__
+
+enum JCATINFO {
+ CAT_TAB = 1, // JDBC Tables
+ CAT_COL = 2, // JDBC Columns
+ CAT_KEY = 3, // JDBC PrimaryKeys
+//CAT_STAT = 4, // SQLStatistics
+//CAT_SPC = 5 // SQLSpecialColumns
+};
+
+/***********************************************************************/
+/* This structure is used to control the catalog functions. */
+/***********************************************************************/
+typedef struct tagJCATPARM {
+ JCATINFO Id; // Id to indicate function
+ PQRYRES Qrp; // Result set pointer
+ PUCHAR DB; // Database (Schema)
+ PUCHAR Tab; // Table name or pattern
+ PUCHAR Pat; // Table type or column pattern
+} JCATPARM;
+
+typedef jint(JNICALL *CRTJVM) (JavaVM **, void **, void *);
+typedef jint(JNICALL *GETJVM) (JavaVM **, jsize, jsize *);
+
+// JDBC connection to a data source
+class TDBJDBC;
+class JDBCCOL;
+class JDBConn;
+class TDBXJDC;
+
+/***********************************************************************/
+/* JDBConn class. */
+/***********************************************************************/
+class JDBConn : public BLOCK {
+ friend class TDBJDBC;
+ friend class TDBXJDC;
+//friend PQRYRES GetColumnInfo(PGLOBAL, char*&, char *, int, PVBLK&);
+private:
+ JDBConn(); // Standard (unused) constructor
+
+public:
+ JDBConn(PGLOBAL g, TDBJDBC *tdbp);
+
+ int Open(PJPARM sop);
+ int Rewind(char *sql);
+ void Close(void);
+ PQRYRES AllocateResult(PGLOBAL g);
+
+ // Attributes
+public:
+ char *GetQuoteChar(void) { return m_IDQuoteChar; }
+ // Database successfully opened?
+ bool IsOpen(void) { return m_Opened; }
+//PSZ GetStringInfo(ushort infotype);
+ int GetMaxValue(int infotype);
+//PSZ GetConnect(void) { return m_Connect; }
+
+public:
+ // Operations
+ //void SetLoginTimeout(DWORD sec) {m_LoginTimeout = sec;}
+ //void SetQueryTimeout(DWORD sec) {m_QueryTimeout = sec;}
+ //void SetUserName(PSZ user) {m_User = user;}
+ //void SetUserPwd(PSZ pwd) {m_Pwd = pwd;}
+ int GetResultSize(char *sql, JDBCCOL *colp);
+ int ExecuteQuery(char *sql);
+ int ExecuteUpdate(char *sql);
+ int Fetch(int pos = 0);
+ bool PrepareSQL(char *sql);
+ int ExecuteSQL(void);
+ bool SetParam(JDBCCOL *colp);
+ int ExecSQLcommand(char *sql);
+ void SetColumnValue(int rank, PSZ name, PVAL val);
+ int GetCatInfo(JCATPARM *cap);
+ //bool GetDataSources(PQRYRES qrp);
+ bool GetDrivers(PQRYRES qrp);
+ PQRYRES GetMetaData(PGLOBAL g, char *src);
+
+public:
+ // Set static variables
+ static void SetJVM(void)
+ { LibJvm = NULL; CreateJavaVM = NULL; GetCreatedJavaVMs = NULL; }
+ static void ResetJVM(void);
+ static bool GetJVM(PGLOBAL g);
+
+ // Implementation
+public:
+ //virtual ~JDBConn();
+
+ // JDBC operations
+protected:
+ char *Check(void);
+//void ThrowDJX(int rc, PSZ msg/*, HSTMT hstmt = SQL_NULL_HSTMT*/);
+//void ThrowDJX(PSZ msg);
+//void Free(void);
+
+protected:
+ // Members
+#if defined(__WIN__)
+ static HANDLE LibJvm; // Handle to the jvm DLL
+#else // !__WIN__
+ static void *LibJvm; // Handle for the jvm shared library
+#endif // !__WIN__
+ static CRTJVM CreateJavaVM;
+ static GETJVM GetCreatedJavaVMs;
+ PGLOBAL m_G;
+ TDBJDBC *m_Tdb;
+ JavaVM *jvm; // Pointer to the JVM (Java Virtual Machine)
+ JNIEnv *env; // Pointer to native interface
+ jclass jdi; // Pointer to the JdbcInterface class
+ jobject job; // The JdbcInterface class object
+ jmethodID xqid; // The ExecuteQuery method ID
+ jmethodID xuid; // The ExecuteUpdate method ID
+ jmethodID xid; // The Execute method ID
+ jmethodID grs; // The GetResult method ID
+ jmethodID readid; // The ReadNext method ID
+ jmethodID fetchid; // The Fetch method ID
+ jmethodID typid; // The ColumnType method ID
+ jmethodID prepid; // The CreatePrepStmt method ID
+ jmethodID xpid; // The ExecutePrep method ID
+ jmethodID pcid; // The ClosePrepStmt method ID
+ //DWORD m_LoginTimeout;
+//DWORD m_QueryTimeout;
+//DWORD m_UpdateOptions;
+ char m_IDQuoteChar[2];
+ PSZ m_Driver;
+ PSZ m_Url;
+ PSZ m_User;
+ PSZ m_Pwd;
+ int m_Ncol;
+ int m_Aff;
+ int m_Rows;
+ int m_Fetch;
+ int m_RowsetSize;
+ jboolean m_Updatable;
+ jboolean m_Transact;
+ jboolean m_Scrollable;
+ bool m_Opened;
+ bool m_Full;
+}; // end of JDBConn class definition
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp
index eae872b81cc..d377b7d4b63 100644
--- a/storage/connect/jsonudf.cpp
+++ b/storage/connect/jsonudf.cpp
@@ -3538,37 +3538,9 @@ void jsoncontains_path_deinit(UDF_INIT* initid)
} // end of jsoncontains_path_deinit
/*********************************************************************************/
-/* Set Json items of a Json document according to path. */
+/* This function is used by the json_set/insert/update_item functions. */
/*********************************************************************************/
-my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
-{
- unsigned long reslen, memlen;
- int n = IsJson(args, 0);
-
- if (!(args->arg_count % 2)) {
- strcpy(message, "This function must have an odd number of arguments");
- return true;
- } else if (!n && args->arg_type[0] != STRING_RESULT) {
- strcpy(message, "First argument must be a json item");
- return true;
- } else
- CalcLen(args, false, reslen, memlen);
-
- if (n == 2 && args->args[0]) {
- char fn[_MAX_PATH];
- long fl;
-
- memcpy(fn, args->args[0], args->lengths[0]);
- fn[args->lengths[0]] = 0;
- fl = GetFileLength(fn);
- memlen += fl * 3;
- } else if (n != 3)
- memlen += args->lengths[0] * 3;
-
- return JsonInit(initid, args, message, true, reslen, memlen);
-} // end of json_set_item_init
-
-char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+char *handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *error)
{
char *p, *path, *str = NULL;
@@ -3586,12 +3558,16 @@ char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
} else if (initid->const_item)
g->N = 1;
- if (!strcmp(result, "$insert"))
+ if (!strcmp(result, "$set"))
+ w = 0;
+ else if (!strcmp(result, "$insert"))
w = 1;
else if (!strcmp(result, "$update"))
w = 2;
- else
- w = 0;
+ else {
+ PUSH_WARNING("Logical error, please contact CONNECT developer");
+ goto err;
+ } // endelse
// Save stack and allocation environment and prepare error return
if (g->jump_level == MAX_JUMP) {
@@ -3660,10 +3636,10 @@ char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
// Keep result of constant function
g->Activityp = (PACTIVITY)str;
- err:
+err:
g->jump_level--;
- fin:
+fin:
if (!str) {
*is_null = 1;
*res_length = 0;
@@ -3671,6 +3647,44 @@ char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
*res_length = strlen(str);
return str;
+} // end of handle_item
+
+/*********************************************************************************/
+/* Set Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+ int n = IsJson(args, 0);
+
+ if (!(args->arg_count % 2)) {
+ strcpy(message, "This function must have an odd number of arguments");
+ return true;
+ } else if (!n && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ if (n == 2 && args->args[0]) {
+ char fn[_MAX_PATH];
+ long fl;
+
+ memcpy(fn, args->args[0], args->lengths[0]);
+ fn[args->lengths[0]] = 0;
+ fl = GetFileLength(fn);
+ memlen += fl * 3;
+ } else if (n != 3)
+ memlen += args->lengths[0] * 3;
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_set_item_init
+
+char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *p)
+{
+ strcpy(result, "$set");
+ return handle_item(initid, args, result, res_length, is_null, p);
} // end of json_set_item
void json_set_item_deinit(UDF_INIT* initid)
@@ -3690,7 +3704,7 @@ char *json_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *p)
{
strcpy(result, "$insert");
- return json_set_item(initid, args, result, res_length, is_null, p);
+ return handle_item(initid, args, result, res_length, is_null, p);
} // end of json_insert_item
void json_insert_item_deinit(UDF_INIT* initid)
@@ -3710,7 +3724,7 @@ char *json_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *p)
{
strcpy(result, "$update");
- return json_set_item(initid, args, result, res_length, is_null, p);
+ return handle_item(initid, args, result, res_length, is_null, p);
} // end of json_update_item
void json_update_item_deinit(UDF_INIT* initid)
@@ -4706,14 +4720,9 @@ void jbin_item_merge_deinit(UDF_INIT* initid)
} // end of jbin_item_merge_deinit
/*********************************************************************************/
-/* Set Json items of a Json document according to path. */
+/* This function is used by the jbin_set/insert/update functions. */
/*********************************************************************************/
-my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
-{
- return json_set_item_init(initid, args, message);
-} // end of jbin_set_item_init
-
-char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+char *bin_handle_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *error)
{
char *p, *path;
@@ -4732,12 +4741,16 @@ char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
} else if (initid->const_item)
g->N = 1;
- if (!strcmp(result, "$insert"))
+ if (!strcmp(result, "$set"))
+ w = 0;
+ else if (!strcmp(result, "$insert"))
w = 1;
else if (!strcmp(result, "$update"))
w = 2;
- else
- w = 0;
+ else {
+ PUSH_WARNING("Logical error, please contact CONNECT developer");
+ goto fin;
+ } // endelse
if (!g->Xchk) {
if (CheckMemory(g, initid, args, 1, true, false, true)) {
@@ -4791,7 +4804,7 @@ char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
// Keep result of constant function
g->Activityp = (PACTIVITY)bsp;
- fin:
+fin:
if (!bsp) {
*is_null = 1;
*res_length = 0;
@@ -4799,6 +4812,21 @@ char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
*res_length = sizeof(BSON);
return (char*)bsp;
+} // end of bin_handle_item
+
+/*********************************************************************************/
+/* Set Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_set_item_init(initid, args, message);
+} // end of jbin_set_item_init
+
+char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *p)
+{
+ strcpy(result, "$set");
+ return bin_handle_item(initid, args, result, res_length, is_null, p);
} // end of jbin_set_item
void jbin_set_item_deinit(UDF_INIT* initid)
@@ -4818,7 +4846,7 @@ char *jbin_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *p)
{
strcpy(result, "$insert");
- return jbin_set_item(initid, args, result, res_length, is_null, p);
+ return bin_handle_item(initid, args, result, res_length, is_null, p);
} // end of jbin_insert_item
void jbin_insert_item_deinit(UDF_INIT* initid)
@@ -4838,7 +4866,7 @@ char *jbin_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *p)
{
strcpy(result, "$update");
- return jbin_set_item(initid, args, result, res_length, is_null, p);
+ return bin_handle_item(initid, args, result, res_length, is_null, p);
} // end of jbin_update_item
void jbin_update_item_deinit(UDF_INIT* initid)
@@ -4999,3 +5027,30 @@ void json_serialize_deinit(UDF_INIT* initid)
{
JsonFreeMem((PGLOBAL)initid->ptr);
} // end of json_serialize_deinit
+
+/*********************************************************************************/
+/* Utility function returning an environment variable value. */
+/*********************************************************************************/
+my_bool envar_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ if (args->arg_count != 1) {
+ strcpy(message, "Unique argument must be an environment variable name");
+ return true;
+ } else
+ return false;
+
+} // end of envar_init
+
+char *envar(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *, char *)
+{
+ char *str, name[256];
+ int n = MY_MIN(args->lengths[0], sizeof(name) - 1);
+
+ memcpy(name, args->args[0], n);
+ name[n] = 0;
+ str = getenv(name);
+ *res_length = (str) ? strlen(str) : 0;
+ return str;
+} // end of envar
+
diff --git a/storage/connect/jsonudf.h b/storage/connect/jsonudf.h
index b7e9b8ecabb..1406d9f2f2e 100644
--- a/storage/connect/jsonudf.h
+++ b/storage/connect/jsonudf.h
@@ -218,8 +218,12 @@ extern "C" {
DllExport my_bool json_serialize_init(UDF_INIT*, UDF_ARGS*, char*);
DllExport char *json_serialize(UDF_EXEC_ARGS);
DllExport void json_serialize_deinit(UDF_INIT*);
+
+ DllExport my_bool envar_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *envar(UDF_EXEC_ARGS);
} // extern "C"
+
/*********************************************************************************/
/* Structure JPN. Used to make the locate path. */
/*********************************************************************************/
diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp
index 1ec1108c639..13c0dfd1e18 100644
--- a/storage/connect/plgdbutl.cpp
+++ b/storage/connect/plgdbutl.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -46,9 +46,9 @@
#else // !__WIN__
#include <unistd.h>
#include <fcntl.h>
-#if defined(THREAD)
+//#if defined(THREAD)
#include <pthread.h>
-#endif // THREAD
+//#endif // THREAD
#include <stdarg.h>
#define BIGMEM 2147483647 // Max int value
#endif // !__WIN__
@@ -70,17 +70,6 @@
#include "rcmsg.h"
/***********************************************************************/
-/* Macro or external routine definition */
-/***********************************************************************/
-#if defined(THREAD)
-#if defined(__WIN__)
-extern CRITICAL_SECTION parsec; // Used calling the Flex parser
-#else // !__WIN__
-extern pthread_mutex_t parmut;
-#endif // !__WIN__
-#endif // THREAD
-
-/***********************************************************************/
/* DB static variables. */
/***********************************************************************/
bool Initdone = false;
@@ -90,6 +79,12 @@ extern "C" {
extern char version[];
} // extern "C"
+#if defined(__WIN__)
+extern CRITICAL_SECTION parsec; // Used calling the Flex parser
+#else // !__WIN__
+extern pthread_mutex_t parmut;
+#endif // !__WIN__
+
// The debug trace used by the main thread
FILE *pfile = NULL;
@@ -702,21 +697,21 @@ PDTP MakeDateFormat(PGLOBAL g, PSZ dfmt, bool in, bool out, int flag)
/* Call the FLEX generated parser. In multi-threading mode the next */
/* instruction is included in an Enter/LeaveCriticalSection bracket. */
/*********************************************************************/
-#if defined(THREAD)
+ //#if defined(THREAD)
#if defined(__WIN__)
EnterCriticalSection((LPCRITICAL_SECTION)&parsec);
#else // !__WIN__
pthread_mutex_lock(&parmut);
#endif // !__WIN__
-#endif // THREAD
+//#endif // THREAD
rc = fmdflex(pdp);
-#if defined(THREAD)
+//#if defined(THREAD)
#if defined(__WIN__)
LeaveCriticalSection((LPCRITICAL_SECTION)&parsec);
#else // !__WIN__
pthread_mutex_unlock(&parmut);
#endif // !__WIN__
-#endif // THREAD
+//#endif // THREAD
if (trace)
htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
@@ -1102,7 +1097,8 @@ char *GetAmName(PGLOBAL g, AMT am, void *memp)
case TYPE_AM_DOM: strcpy(amn, "DOM"); break;
case TYPE_AM_DIR: strcpy(amn, "DIR"); break;
case TYPE_AM_ODBC: strcpy(amn, "ODBC"); break;
- case TYPE_AM_MAC: strcpy(amn, "MAC"); break;
+ case TYPE_AM_JDBC: strcpy(amn, "JDBC"); break;
+ case TYPE_AM_MAC: strcpy(amn, "MAC"); break;
case TYPE_AM_OEM: strcpy(amn, "OEM"); break;
case TYPE_AM_OUT: strcpy(amn, "OUT"); break;
default: sprintf(amn, "OEM(%d)", am);
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index e455bc8f1a5..2c8ada52e6f 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -305,7 +305,7 @@ int TABDEF::GetColCatInfo(PGLOBAL g)
case TAB_OEM:
poff = 0; // Offset represents an independant flag
break;
- default: // VCT PLG ODBC MYSQL WMI...
+ default: // VCT PLG ODBC JDBC MYSQL WMI...
poff = 0; // NA
break;
} // endswitch tc
@@ -514,10 +514,11 @@ PTABDEF OEMDEF::GetXdef(PGLOBAL g)
} // endif getdef
#else // !__WIN__
const char *error = NULL;
- Dl_info dl_info;
#if 0 // Don't know what all this stuff does
- // The OEM lib must retrieve exported CONNECT variables
+ Dl_info dl_info;
+
+ // The OEM lib must retrieve exported CONNECT variables
if (dladdr(&connect_hton, &dl_info)) {
if (dlopen(dl_info.dli_fname, RTLD_NOLOAD | RTLD_NOW | RTLD_GLOBAL) == 0) {
error = dlerror();
diff --git a/storage/connect/tabjdbc.cpp b/storage/connect/tabjdbc.cpp
new file mode 100644
index 00000000000..37850ce6358
--- /dev/null
+++ b/storage/connect/tabjdbc.cpp
@@ -0,0 +1,1707 @@
+/************* TabJDBC C++ Program Source Code File (.CPP) *************/
+/* PROGRAM NAME: TABJDBC */
+/* ------------- */
+/* Version 1.0 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the TABJDBC class DB execution routines. */
+/* */
+/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
+/* -------------------------------------- */
+/* */
+/* REQUIRED FILES: */
+/* --------------- */
+/* TABJDBC.CPP - Source code */
+/* PLGDBSEM.H - DB application declaration file */
+/* TABJDBC.H - TABJDBC classes declaration file */
+/* GLOBAL.H - Global declaration file */
+/* */
+/* REQUIRED LIBRARIES: */
+/* ------------------- */
+/* Large model C library */
+/* */
+/* REQUIRED PROGRAMS: */
+/* ------------------ */
+/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+#include "sql_class.h"
+#if defined(__WIN__)
+#include <io.h>
+#include <fcntl.h>
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif
+//#include <windows.h>
+#include <sqltypes.h>
+#else
+#if defined(UNIX)
+#include <errno.h>
+#define NODW
+#include "osutil.h"
+#else
+#include <io.h>
+#endif
+#include <fcntl.h>
+#endif
+
+/***********************************************************************/
+/* Include application header files: */
+/* global.h is header containing all global declarations. */
+/* plgdbsem.h is header containing the DB application declarations. */
+/* kindex.h is kindex header that also includes tabdos.h. */
+/* tabJDBC.h is header containing the TABJDBC class declarations. */
+/* JDBConn.h is header containing JDBC connection declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "mycat.h"
+#include "xtable.h"
+#include "jdbccat.h"
+#include "tabjdbc.h"
+#include "tabmul.h"
+#include "reldef.h"
+#include "tabcol.h"
+#include "valblk.h"
+#include "ha_connect.h"
+
+#include "sql_string.h"
+
+/***********************************************************************/
+/* DB static variables. */
+/***********************************************************************/
+// int num_read, num_there, num_eq[2], num_nf; // Statistics
+extern int num_read, num_there, num_eq[2]; // Statistics
+
+/***********************************************************************/
+/* External function. */
+/***********************************************************************/
+bool ExactInfo(void);
+
+/* -------------------------- Class JDBCDEF -------------------------- */
+
+/***********************************************************************/
+/* Constructor. */
+/***********************************************************************/
+JDBCDEF::JDBCDEF(void)
+{
+ Driver = Url = Tabname = Tabschema = Username = NULL;
+ Password = Tabcat = Tabtype = Srcdef = Qchar = Qrystr = Sep = NULL;
+ Options = Quoted = Maxerr = Maxres = Memory = 0;
+ Scrollable = Xsrc = false;
+} // end of JDBCDEF constructor
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values from JDBC file. */
+/***********************************************************************/
+bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+{
+ Driver = GetStringCatInfo(g, "Driver", NULL);
+ Desc = Url = GetStringCatInfo(g, "Connect", NULL);
+
+ if (!Url && !Catfunc) {
+ // Look in the option list (deprecated)
+ Url = GetStringCatInfo(g, "Url", NULL);
+
+ if (!Url) {
+ sprintf(g->Message, "Missing URL for JDBC table %s", Name);
+ return true;
+ } // endif Url
+
+ } // endif Connect
+
+ Tabname = GetStringCatInfo(g, "Name",
+ (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
+ Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+ Tabschema = GetStringCatInfo(g, "Dbname", NULL);
+ Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
+ Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
+ Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
+ Tabtype = GetStringCatInfo(g, "Tabtype", NULL);
+ Username = GetStringCatInfo(g, "User", NULL);
+ Password = GetStringCatInfo(g, "Password", NULL);
+
+ if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+ Read_Only = true;
+
+ Qrystr = GetStringCatInfo(g, "Query_String", "?");
+ Sep = GetStringCatInfo(g, "Separator", NULL);
+ Xsrc = GetBoolCatInfo("Execsrc", FALSE);
+ Maxerr = GetIntCatInfo("Maxerr", 0);
+ Maxres = GetIntCatInfo("Maxres", 0);
+ Quoted = GetIntCatInfo("Quoted", 0);
+//Options = JDBConn::noJDBCDialog;
+//Options = JDBConn::noJDBCDialog | JDBConn::useCursorLib;
+//Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
+//Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
+ Scrollable = GetBoolCatInfo("Scrollable", false);
+ Memory = GetIntCatInfo("Memory", 0);
+ Pseudo = 2; // FILID is Ok but not ROWID
+ return false;
+} // end of DefineAM
+
+/***********************************************************************/
+/* GetTable: makes a new Table Description Block. */
+/***********************************************************************/
+PTDB JDBCDEF::GetTable(PGLOBAL g, MODE m)
+{
+ PTDBASE tdbp = NULL;
+
+ /*********************************************************************/
+ /* Allocate a TDB of the proper type. */
+ /* Column blocks will be allocated only when needed. */
+ /*********************************************************************/
+ if (Xsrc)
+ tdbp = new(g)TDBXJDC(this);
+ else switch (Catfunc) {
+ case FNC_COL:
+ tdbp = new(g)TDBJDBCL(this);
+ break;
+#if 0
+ case FNC_DSN:
+ tdbp = new(g)TDBJSRC(this);
+ break;
+#endif // 0
+ case FNC_TABLE:
+ tdbp = new(g)TDBJTB(this);
+ break;
+ case FNC_DRIVER:
+ tdbp = new(g)TDBJDRV(this);
+ break;
+ default:
+ tdbp = new(g)TDBJDBC(this);
+
+ if (Multiple == 1)
+ tdbp = new(g)TDBMUL(tdbp);
+ else if (Multiple == 2)
+ strcpy(g->Message, "NO_JDBC_MUL");
+
+ } // endswitch Catfunc
+
+ return tdbp;
+} // end of GetTable
+
+/***********************************************************************/
+/* The MySQL and MariaDB JDBC drivers return by default a result set */
+/* containing the entire result of the executed query. This can be an */
+/* issue for big tables and memory error can occur. An alternative is */
+/* to use streaming (reading one row at a time) but to specify this, */
+/* a fech size of the integer min value must be send to the driver. */
+/***********************************************************************/
+int JDBCPARM::CheckSize(int rows)
+{
+ if (Url && rows == 1) {
+ // Are we connected to a MySQL JDBC connector?
+ bool b = (!strncmp(Url, "jdbc:mysql:", 11) ||
+ !strncmp(Url, "jdbc:mariadb:", 13));
+ return b ? INT_MIN32 : rows;
+ } else
+ return rows;
+
+} // end of CheckSize
+
+/* -------------------------- Class TDBJDBC -------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBJDBC class. */
+/***********************************************************************/
+TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp)
+{
+ Jcp = NULL;
+ Cnp = NULL;
+
+ if (tdp) {
+ Ops.Driver = tdp->Driver;
+ Ops.Url = tdp->Url;
+ TableName = tdp->Tabname;
+ Schema = tdp->Tabschema;
+ Ops.User = tdp->Username;
+ Ops.Pwd = tdp->Password;
+ Catalog = tdp->Tabcat;
+ Srcdef = tdp->Srcdef;
+ Qrystr = tdp->Qrystr;
+ Sep = tdp->GetSep();
+ Options = tdp->Options;
+// Ops.Cto = tdp->Cto;
+// Ops.Qto = tdp->Qto;
+ Quoted = MY_MAX(0, tdp->GetQuoted());
+ Rows = tdp->GetElemt();
+ Memory = tdp->Memory;
+ Ops.Scrollable = tdp->Scrollable;
+ } else {
+ TableName = NULL;
+ Schema = NULL;
+ Ops.Driver = NULL;
+ Ops.Url = NULL;
+ Ops.User = NULL;
+ Ops.Pwd = NULL;
+ Catalog = NULL;
+ Srcdef = NULL;
+ Qrystr = NULL;
+ Sep = 0;
+ Options = 0;
+// Ops.Cto = DEFAULT_LOGIN_TIMEOUT;
+// Ops.Qto = DEFAULT_QUERY_TIMEOUT;
+ Quoted = 0;
+ Rows = 0;
+ Memory = 0;
+ Ops.Scrollable = false;
+ } // endif tdp
+
+ Quote = NULL;
+ Query = NULL;
+ Count = NULL;
+//Where = NULL;
+ MulConn = NULL;
+ DBQ = NULL;
+ Qrp = NULL;
+ Fpos = 0;
+ Curpos = 0;
+ AftRows = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ BufSize = 0;
+ Ncol = 0;
+ Nparm = 0;
+ Placed = false;
+ Werr = false;
+ Rerr = false;
+ Ops.Fsize = Ops.CheckSize(Rows);
+} // end of TDBJDBC standard constructor
+
+TDBJDBC::TDBJDBC(PTDBJDBC tdbp) : TDBASE(tdbp)
+{
+ Jcp = tdbp->Jcp; // is that right ?
+ Cnp = tdbp->Cnp;
+ TableName = tdbp->TableName;
+ Schema = tdbp->Schema;
+ Ops = tdbp->Ops;
+ Catalog = tdbp->Catalog;
+ Srcdef = tdbp->Srcdef;
+ Qrystr = tdbp->Qrystr;
+ Memory = tdbp->Memory;
+//Scrollable = tdbp->Scrollable;
+ Quote = tdbp->Quote;
+ Query = tdbp->Query;
+ Count = tdbp->Count;
+//Where = tdbp->Where;
+ MulConn = tdbp->MulConn;
+ DBQ = tdbp->DBQ;
+ Options = tdbp->Options;
+ Quoted = tdbp->Quoted;
+ Rows = tdbp->Rows;
+ Fpos = 0;
+ Curpos = 0;
+ AftRows = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ BufSize = tdbp->BufSize;
+ Nparm = tdbp->Nparm;
+ Qrp = tdbp->Qrp;
+ Placed = false;
+} // end of TDBJDBC copy constructor
+
+// Method
+PTDB TDBJDBC::CopyOne(PTABS t)
+{
+ PTDB tp;
+ PJDBCCOL cp1, cp2;
+ PGLOBAL g = t->G; // Is this really useful ???
+
+ tp = new(g)TDBJDBC(this);
+
+ for (cp1 = (PJDBCCOL)Columns; cp1; cp1 = (PJDBCCOL)cp1->GetNext()) {
+ cp2 = new(g)JDBCCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+} // end of CopyOne
+
+/***********************************************************************/
+/* Allocate JDBC column description block. */
+/***********************************************************************/
+PCOL TDBJDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+{
+ return new(g)JDBCCOL(cdp, this, cprec, n);
+} // end of MakeCol
+
+/******************************************************************/
+/* Convert an UTF-8 string to latin characters. */
+/******************************************************************/
+int TDBJDBC::Decode(char *txt, char *buf, size_t n)
+{
+ uint dummy_errors;
+ uint32 len= copy_and_convert(buf, n, &my_charset_latin1,
+ txt, strlen(txt),
+ &my_charset_utf8_general_ci,
+ &dummy_errors);
+ buf[len]= '\0';
+ return 0;
+} // end of Decode
+
+/***********************************************************************/
+/* MakeSQL: make the SQL statement use with JDBC connection. */
+/* TODO: when implementing EOM filtering, column only used in local */
+/* filter should be removed from column list. */
+/***********************************************************************/
+bool TDBJDBC::MakeSQL(PGLOBAL g, bool cnt)
+{
+ char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
+ int len;
+ bool oom = false, first = true;
+ PTABLE tablep = To_Table;
+ PCOL colp;
+
+ if (Srcdef) {
+ Query = new(g)STRING(g, 0, Srcdef);
+ return false;
+ } // endif Srcdef
+
+ // Allocate the string used to contain the Query
+ Query = new(g)STRING(g, 1023, "SELECT ");
+
+ if (!cnt) {
+ if (Columns) {
+ // Normal SQL statement to retrieve results
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (!colp->IsSpecial()) {
+ if (!first)
+ oom |= Query->Append(", ");
+ else
+ first = false;
+
+ // Column name can be encoded in UTF-8
+ Decode(colp->GetName(), buf, sizeof(buf));
+
+ if (Quote) {
+ // Put column name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ ((PJDBCCOL)colp)->Rank = ++Ncol;
+ } // endif colp
+
+ } else
+ // !Columns can occur for queries such that sql count(*) from...
+ // for which we will count the rows from sql * from...
+ oom |= Query->Append('*');
+
+ } else
+ // SQL statement used to retrieve the size of the result
+ oom |= Query->Append("count(*)");
+
+ oom |= Query->Append(" FROM ");
+
+ if (Catalog && *Catalog)
+ catp = Catalog;
+
+ if (tablep->GetSchema())
+ schmp = (char*)tablep->GetSchema();
+ else if (Schema && *Schema)
+ schmp = Schema;
+
+ if (catp) {
+ oom |= Query->Append(catp);
+
+ if (schmp) {
+ oom |= Query->Append('.');
+ oom |= Query->Append(schmp);
+ } // endif schmp
+
+ oom |= Query->Append('.');
+ } else if (schmp) {
+ oom |= Query->Append(schmp);
+ oom |= Query->Append('.');
+ } // endif schmp
+
+ // Table name can be encoded in UTF-8
+ Decode(TableName, buf, sizeof(buf));
+
+ if (Quote) {
+ // Put table name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ len = Query->GetLength();
+
+ if (To_CondFil) {
+ if (Mode == MODE_READ) {
+ oom |= Query->Append(" WHERE ");
+ oom |= Query->Append(To_CondFil->Body);
+ len = Query->GetLength() + 1;
+ } else
+ len += (strlen(To_CondFil->Body) + 256);
+
+ } else
+ len += ((Mode == MODE_READX) ? 256 : 1);
+
+ if (oom || Query->Resize(len)) {
+ strcpy(g->Message, "MakeSQL: Out of memory");
+ return true;
+ } // endif oom
+
+ if (trace)
+ htrc("Query=%s\n", Query->GetStr());
+
+ return false;
+} // end of MakeSQL
+
+/***********************************************************************/
+/* MakeInsert: make the Insert statement used with JDBC connection. */
+/***********************************************************************/
+bool TDBJDBC::MakeInsert(PGLOBAL g)
+{
+ char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
+ int len = 0;
+ uint pos;
+ bool b = false, oom = false;
+ PTABLE tablep = To_Table;
+ PCOL colp;
+
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (colp->IsSpecial()) {
+ strcpy(g->Message, "No JDBC special columns");
+ return true;
+ } else {
+ // Column name can be encoded in UTF-8
+ Decode(colp->GetName(), buf, sizeof(buf));
+ len += (strlen(buf) + 6); // comma + quotes + valist
+ ((PJDBCCOL)colp)->Rank = ++Nparm;
+ } // endif colp
+
+ // Below 32 is enough to contain the fixed part of the query
+ if (Catalog && *Catalog)
+ catp = Catalog;
+
+ if (catp)
+ len += strlen(catp) + 1;
+
+ if (tablep->GetSchema())
+ schmp = (char*)tablep->GetSchema();
+ else if (Schema && *Schema)
+ schmp = Schema;
+
+ if (schmp)
+ len += strlen(schmp) + 1;
+
+ // Table name can be encoded in UTF-8
+ Decode(TableName, buf, sizeof(buf));
+ len += (strlen(buf) + 32);
+ Query = new(g)STRING(g, len, "INSERT INTO ");
+
+ if (catp) {
+ oom |= Query->Append(catp);
+
+ if (schmp) {
+ oom |= Query->Append('.');
+ oom |= Query->Append(schmp);
+ } // endif schmp
+
+ oom |= Query->Append('.');
+ } else if (schmp) {
+ oom |= Query->Append(schmp);
+ oom |= Query->Append('.');
+ } // endif schmp
+
+ if (Quote) {
+ // Put table name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ oom |= Query->Append('(');
+
+ for (colp = Columns; colp; colp = colp->GetNext()) {
+ if (b)
+ oom |= Query->Append(", ");
+ else
+ b = true;
+
+ // Column name can be in UTF-8 encoding
+ Decode(colp->GetName(), buf, sizeof(buf));
+
+ if (Quote) {
+ // Put column name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ } // endfor colp
+
+ if ((oom |= Query->Append(") VALUES ("))) {
+ strcpy(g->Message, "MakeInsert: Out of memory");
+ return true;
+ } else // in case prepared statement fails
+ pos = Query->GetLength();
+
+ // Make prepared statement
+ for (int i = 0; i < Nparm; i++)
+ oom |= Query->Append("?,");
+
+ if (oom) {
+ strcpy(g->Message, "MakeInsert: Out of memory");
+ return true;
+ } else
+ Query->RepLast(')');
+
+ // Now see if we can use prepared statement
+ if (Jcp->PrepareSQL(Query->GetStr()))
+ Query->Truncate(pos); // Restore query to not prepared
+ else
+ Prepared = true;
+
+ return false;
+} // end of MakeInsert
+
+/***********************************************************************/
+/* JDBC Set Parameter function. */
+/***********************************************************************/
+bool TDBJDBC::SetParameters(PGLOBAL g)
+{
+ PJDBCCOL colp;
+
+ for (colp = (PJDBCCOL)Columns; colp; colp = (PJDBCCOL)colp->Next)
+ if (Jcp->SetParam(colp))
+ return true;
+
+ return false;
+} // end of SetParameters
+
+/***********************************************************************/
+/* MakeCommand: make the Update or Delete statement to send to the */
+/* MySQL server. Limited to remote values and filtering. */
+/***********************************************************************/
+bool TDBJDBC::MakeCommand(PGLOBAL g)
+{
+ char *p, *stmt, name[68], *body = NULL, *qc = Jcp->GetQuoteChar();
+ char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
+ bool qtd = Quoted > 0;
+ int i = 0, k = 0;
+
+ // Make a lower case copy of the originale query and change
+ // back ticks to the data source identifier quoting character
+ do {
+ qrystr[i] = (Qrystr[i] == '`') ? *qc : tolower(Qrystr[i]);
+ } while (Qrystr[i++]);
+
+ if (To_CondFil && (p = strstr(qrystr, " where "))) {
+ p[7] = 0; // Remove where clause
+ Qrystr[(p - qrystr) + 7] = 0;
+ body = To_CondFil->Body;
+ stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
+ + strlen(body) + 64);
+ } else
+ stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+
+ // Check whether the table name is equal to a keyword
+ // If so, it must be quoted in the original query
+ strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
+
+ if (!strstr(" update delete low_priority ignore quick from ", name))
+ strlwr(strcpy(name, Name)); // Not a keyword
+ else
+ strlwr(strcat(strcat(strcpy(name, qc), Name), qc));
+
+ if ((p = strstr(qrystr, name))) {
+ for (i = 0; i < p - qrystr; i++)
+ stmt[i] = (Qrystr[i] == '`') ? *qc : Qrystr[i];
+
+ stmt[i] = 0;
+ k = i + (int)strlen(Name);
+
+ if (qtd && *(p-1) == ' ')
+ strcat(strcat(strcat(stmt, qc), TableName), qc);
+ else
+ strcat(stmt, TableName);
+
+ i = (int)strlen(stmt);
+
+ do {
+ stmt[i++] = (Qrystr[k] == '`') ? *qc : Qrystr[k];
+ } while (Qrystr[k++]);
+
+ if (body)
+ strcat(stmt, body);
+
+ } else {
+ sprintf(g->Message, "Cannot use this %s command",
+ (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
+ return NULL;
+ } // endif p
+
+ Query = new(g)STRING(g, 0, stmt);
+ return (!Query->GetSize());
+} // end of MakeCommand
+
+/***********************************************************************/
+/* ResetSize: call by TDBMUL when calculating size estimate. */
+/***********************************************************************/
+void TDBJDBC::ResetSize(void)
+{
+ MaxSize = -1;
+
+ if (Jcp && Jcp->IsOpen())
+ Jcp->Close();
+
+} // end of ResetSize
+
+/***********************************************************************/
+/* JDBC Cardinality: returns table size in number of rows. */
+/***********************************************************************/
+int TDBJDBC::Cardinality(PGLOBAL g)
+{
+ if (!g)
+ return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
+
+#if 0
+ if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
+ // Info command, we must return the exact table row number
+ char qry[96], tbn[64];
+ JDBConn *jcp = new(g)JDBConn(g, this);
+
+ if (jcp->Open(&Ops) == RC_FX)
+ return -1;
+
+ // Table name can be encoded in UTF-8
+ Decode(TableName, tbn, sizeof(tbn));
+ strcpy(qry, "SELECT COUNT(*) FROM ");
+
+ if (Quote)
+ strcat(strcat(strcat(qry, Quote), tbn), Quote);
+ else
+ strcat(qry, tbn);
+
+ // Allocate a Count(*) column (must not use the default constructor)
+ Cnp = new(g)JDBCCOL;
+ Cnp->InitValue(g);
+
+ if ((Cardinal = jcp->GetResultSize(qry, Cnp)) < 0)
+ return -3;
+
+ jcp->Close();
+ } else
+#endif // 0
+ Cardinal = 10; // To make MariaDB happy
+
+ return Cardinal;
+} // end of Cardinality
+
+/***********************************************************************/
+/* JDBC GetMaxSize: returns table size estimate in number of lines. */
+/***********************************************************************/
+int TDBJDBC::GetMaxSize(PGLOBAL g)
+{
+ if (MaxSize < 0) {
+ if (Mode == MODE_DELETE)
+ // Return 0 in mode DELETE in case of delete all.
+ MaxSize = 0;
+ else if (!Cardinality(NULL))
+ MaxSize = 10; // To make MySQL happy
+ else if ((MaxSize = Cardinality(g)) < 0)
+ MaxSize = 12; // So we can see an error occured
+
+ } // endif MaxSize
+
+ return MaxSize;
+} // end of GetMaxSize
+
+/***********************************************************************/
+/* Return max size value. */
+/***********************************************************************/
+int TDBJDBC::GetProgMax(PGLOBAL g)
+{
+ return GetMaxSize(g);
+} // end of GetProgMax
+
+/***********************************************************************/
+/* JDBC Access Method opening routine. */
+/* New method now that this routine is called recursively (last table */
+/* first in reverse order): index blocks are immediately linked to */
+/* join block of next table if it exists or else are discarted. */
+/***********************************************************************/
+bool TDBJDBC::OpenDB(PGLOBAL g)
+{
+ bool rc = true;
+
+ if (trace)
+ htrc("JDBC OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
+ this, Tdb_No, Use, Mode);
+
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open, just replace it at its beginning. */
+ /*******************************************************************/
+ if (Memory == 1) {
+ if ((Qrp = Jcp->AllocateResult(g)))
+ Memory = 2; // Must be filled
+ else
+ Memory = 0; // Allocation failed, don't use it
+
+ } else if (Memory == 2)
+ Memory = 3; // Ok to use memory result
+
+ if (Memory < 3) {
+ // Method will depend on cursor type
+ if ((Rbuf = Jcp->Rewind(Query->GetStr())) < 0)
+ if (Mode != MODE_READX) {
+ Jcp->Close();
+ return true;
+ } else
+ Rbuf = 0;
+
+ } else
+ Rbuf = Qrp->Nblin;
+
+ CurNum = 0;
+ Fpos = 0;
+ Curpos = 1;
+ return false;
+ } // endif use
+
+ /*********************************************************************/
+ /* Open an JDBC connection for this table. */
+ /* Note: this may not be the proper way to do. Perhaps it is better */
+ /* to test whether a connection is already open for this datasource */
+ /* and if so to allocate just a new result set. But this only for */
+ /* drivers allowing concurency in getting results ??? */
+ /*********************************************************************/
+ if (!Jcp)
+ Jcp = new(g)JDBConn(g, this);
+ else if (Jcp->IsOpen())
+ Jcp->Close();
+
+ if (Jcp->Open(&Ops) == RC_FX)
+ return true;
+ else if (Quoted)
+ Quote = Jcp->GetQuoteChar();
+
+ Use = USE_OPEN; // Do it now in case we are recursively called
+
+ /*********************************************************************/
+ /* Make the command and allocate whatever is used for getting results. */
+ /*********************************************************************/
+ if (Mode == MODE_READ || Mode == MODE_READX) {
+ if (Memory > 1 && !Srcdef) {
+ int n;
+
+ if (!MakeSQL(g, true)) {
+ // Allocate a Count(*) column
+ Cnp = new(g)JDBCCOL;
+ Cnp->InitValue(g);
+
+ if ((n = Jcp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
+ sprintf(g->Message, "Cannot get result size rc=%d", n);
+ return true;
+ } else if (n) {
+ Jcp->m_Rows = n;
+
+ if ((Qrp = Jcp->AllocateResult(g)))
+ Memory = 2; // Must be filled
+ else {
+ strcpy(g->Message, "Result set memory allocation failed");
+ return true;
+ } // endif n
+
+ } else // Void result
+ Memory = 0;
+
+ Jcp->m_Rows = 0;
+ } else
+ return true;
+
+ } // endif Memory
+
+ if (!(rc = MakeSQL(g, false))) {
+// for (PJDBCCOL colp = (PJDBCCOL)Columns; colp; colp = (PJDBCCOL)colp->GetNext())
+// if (!colp->IsSpecial())
+// colp->AllocateBuffers(g, Rows);
+
+ rc = (Mode == MODE_READ)
+ ? (Jcp->ExecuteQuery(Query->GetStr()) != RC_OK)
+ : false;
+ } // endif rc
+
+ } else if (Mode == MODE_INSERT) {
+#if 0
+ if (!(rc = MakeInsert(g))) {
+ if (Nparm != Jcp->PrepareSQL(Query->GetStr())) {
+ strcpy(g->Message, MSG(PARM_CNT_MISS));
+ rc = true;
+ } else
+ rc = BindParameters(g);
+
+ } // endif rc
+#endif // 0
+ rc = MakeInsert(g);
+ } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ rc = false; // wait for CheckCond before calling MakeCommand(g);
+ } else
+ sprintf(g->Message, "Invalid mode %d", Mode);
+
+ if (rc) {
+ Jcp->Close();
+ return true;
+ } // endif rc
+
+ /*********************************************************************/
+ /* Reset statistics values. */
+ /*********************************************************************/
+ num_read = num_there = num_eq[0] = num_eq[1] = 0;
+ return false;
+} // end of OpenDB
+
+/***********************************************************************/
+/* GetRecpos: return the position of last read record. */
+/***********************************************************************/
+int TDBJDBC::GetRecpos(void)
+{
+ return Fpos;
+} // end of GetRecpos
+
+/***********************************************************************/
+/* SetRecpos: set the position of next read record. */
+/***********************************************************************/
+bool TDBJDBC::SetRecpos(PGLOBAL g, int recpos)
+{
+ if (Jcp->m_Full) {
+ Fpos = 0;
+// CurNum = 0;
+ CurNum = 1;
+ } else if (Memory == 3) {
+// Fpos = recpos;
+// CurNum = -1;
+ Fpos = 0;
+ CurNum = recpos;
+ } else if (Ops.Scrollable) {
+ // Is new position in the current row set?
+// if (recpos >= Curpos && recpos < Curpos + Rbuf) {
+// CurNum = recpos - Curpos;
+// Fpos = 0;
+ if (recpos > 0 && recpos <= Rbuf) {
+ CurNum = recpos;
+ Fpos = recpos;
+ } else {
+ strcpy(g->Message, "Scrolling out of row set NIY");
+ return true;
+ } // endif recpos
+
+ } else {
+ strcpy(g->Message, "This action requires a scrollable cursor");
+ return true;
+ } // endif's
+
+ // Indicate the table position was externally set
+ Placed = true;
+ return false;
+} // end of SetRecpos
+
+/***********************************************************************/
+/* Data Base indexed read routine for JDBC access method. */
+/***********************************************************************/
+bool TDBJDBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
+{
+ char c = Quote ? *Quote : 0;
+ int rc, oldlen = Query->GetLength();
+ PHC hc = To_Def->GetHandler();
+
+ if (!(kr || hc->end_range) || op == OP_NEXT ||
+ Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ if (!kr && Mode == MODE_READX) {
+ // This is a false indexed read
+ rc = Jcp->ExecuteQuery((char*)Query->GetStr());
+ Mode = MODE_READ;
+ Rows = 1; // ???
+ return (rc != RC_OK);
+ } // endif key
+
+ return false;
+ } else {
+ if (hc->MakeKeyWhere(g, Query, op, c, kr))
+ return true;
+
+ if (To_CondFil) {
+ if (To_CondFil->Idx != hc->active_index) {
+ To_CondFil->Idx = hc->active_index;
+ To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
+ *To_CondFil->Body= 0;
+
+ if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond)))
+ PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
+
+ } // endif active_index
+
+ if (To_CondFil)
+ if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
+ strcpy(g->Message, "Readkey: Out of memory");
+ return true;
+ } // endif Append
+
+ } // endif To_Condfil
+
+ Mode = MODE_READ;
+ } // endif's op
+
+ if (trace)
+ htrc("JDBC ReadKey: Query=%s\n", Query->GetStr());
+
+ rc = Jcp->ExecuteQuery((char*)Query->GetStr());
+ Query->Truncate(oldlen);
+ Rows = 1; // ???
+ return (rc != RC_OK);
+} // end of ReadKey
+
+/***********************************************************************/
+/* Data Base read routine for JDBC access method. */
+/***********************************************************************/
+int TDBJDBC::ReadDB(PGLOBAL g)
+{
+ int rc;
+
+ if (trace > 1)
+ htrc("JDBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
+ GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
+
+ if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ if (!Query && MakeCommand(g))
+ return RC_FX;
+
+ // Send the UPDATE/DELETE command to the remote table
+ rc = Jcp->ExecuteUpdate(Query->GetStr());
+
+ if (rc == RC_OK) {
+ AftRows = Jcp->m_Aff;
+ return RC_EF; // Nothing else to do
+ } else {
+ Werr = true;
+ return RC_FX;
+ } // endif rc
+
+ } // endif Mode
+
+ if (To_Kindex) {
+ // Direct access of JDBC tables is not implemented
+ strcpy(g->Message, "No JDBC direct access");
+ return RC_FX;
+ } // endif To_Kindex
+
+ /*********************************************************************/
+ /* Now start the reading process. */
+ /* Here is the place to fetch the line(s). */
+ /*********************************************************************/
+ if (Placed) {
+ if (Fpos && CurNum >= 0)
+ Rbuf = Jcp->Fetch((Curpos = Fpos));
+ else
+ Fpos = CurNum;
+
+ rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
+ Placed = false;
+ } else {
+ if (Memory != 3) {
+ if (++CurNum >= Rbuf) {
+ Rbuf = Jcp->Fetch();
+ Curpos = Fpos + 1;
+ CurNum = 0;
+ } // endif CurNum
+
+ rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
+ } else // Getting result from memory
+ rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
+
+ if (rc == RC_OK) {
+ if (Memory == 2)
+ Qrp->Nblin++;
+
+ Fpos++; // Used for memory and pos
+ } // endif rc
+
+ } // endif placed
+
+ if (trace > 1)
+ htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
+
+ return rc;
+} // end of ReadDB
+
+/***********************************************************************/
+/* Data Base Insert write routine for JDBC access method. */
+/***********************************************************************/
+int TDBJDBC::WriteDB(PGLOBAL g)
+{
+ int rc;
+
+ if (Prepared) {
+ if (SetParameters(g)) {
+ Werr = true;
+ rc = RC_FX;
+ } else if ((rc = Jcp->ExecuteSQL()) == RC_OK)
+ AftRows += Jcp->m_Aff;
+ else
+ Werr = true;
+
+ return rc;
+ } // endif Prepared
+
+ // Statement was not prepared, we must construct and execute
+ // an insert query for each line to insert
+ uint len = Query->GetLength();
+ char buf[64];
+ bool oom = false;
+
+ // Make the Insert command value list
+ for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
+ if (!colp->GetValue()->IsNull()) {
+ char *s = colp->GetValue()->GetCharString(buf);
+
+ if (colp->GetResultType() == TYPE_STRING)
+ oom |= Query->Append_quoted(s);
+ else if (colp->GetResultType() == TYPE_DATE) {
+ DTVAL *dtv = (DTVAL*)colp->GetValue();
+
+ if (dtv->IsFormatted())
+ oom |= Query->Append_quoted(s);
+ else
+ oom |= Query->Append(s);
+
+ } else
+ oom |= Query->Append(s);
+
+ } else
+ oom |= Query->Append("NULL");
+
+ oom |= Query->Append(',');
+ } // endfor colp
+
+ if (unlikely(oom)) {
+ strcpy(g->Message, "WriteDB: Out of memory");
+ return RC_FX;
+ } // endif oom
+
+ Query->RepLast(')');
+ rc = Jcp->ExecuteUpdate(Query->GetStr());
+ Query->Truncate(len); // Restore query
+
+ if (rc == RC_OK)
+ AftRows += Jcp->m_Aff;
+ else
+ Werr = true;
+
+ return rc;
+} // end of WriteDB
+
+/***********************************************************************/
+/* Data Base delete line routine for JDBC access method. */
+/***********************************************************************/
+int TDBJDBC::DeleteDB(PGLOBAL g, int irc)
+{
+ if (irc == RC_FX) {
+ if (!Query && MakeCommand(g))
+ return RC_FX;
+
+ // Send the DELETE (all) command to the remote table
+ if (Jcp->ExecuteUpdate(Query->GetStr()) == RC_OK) {
+ AftRows = Jcp->m_Aff;
+ sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ PushWarning(g, this, 0); // 0 means a Note
+ return RC_OK; // This is a delete all
+ } else
+ return RC_FX; // Error
+
+ } else
+ return RC_OK; // Ignore
+
+} // end of DeleteDB
+
+/***********************************************************************/
+/* Data Base close routine for JDBC access method. */
+/***********************************************************************/
+void TDBJDBC::CloseDB(PGLOBAL g)
+{
+ //if (To_Kindex) {
+ // To_Kindex->Close();
+ // To_Kindex = NULL;
+ // } // endif
+
+ if (Jcp)
+ Jcp->Close();
+
+ if (trace)
+ htrc("JDBC CloseDB: closing %s\n", Name);
+
+ if (!Werr &&
+ (Mode == MODE_INSERT || Mode == MODE_UPDATE || Mode == MODE_DELETE)) {
+ sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ PushWarning(g, this, 0); // 0 means a Note
+ } // endif Mode
+
+ Prepared = false;
+} // end of CloseDB
+
+/* --------------------------- JDBCCOL ------------------------------- */
+
+/***********************************************************************/
+/* JDBCCOL public constructor. */
+/***********************************************************************/
+JDBCCOL::JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : COLBLK(cdp, tdbp, i)
+{
+ if (cprec) {
+ Next = cprec->GetNext();
+ cprec->SetNext(this);
+ } else {
+ Next = tdbp->GetColumns();
+ tdbp->SetColumns(this);
+ } // endif cprec
+
+ // Set additional JDBC access method information for column.
+ Crp = NULL;
+ //Long = cdp->GetLong();
+ Long = Precision;
+ //strcpy(F_Date, cdp->F_Date);
+ To_Val = NULL;
+//Slen = 0;
+//StrLen = &Slen;
+//Sqlbuf = NULL;
+ Bufp = NULL;
+ Blkp = NULL;
+ Rank = 0; // Not known yet
+
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+} // end of JDBCCOL constructor
+
+/***********************************************************************/
+/* JDBCCOL private constructor. */
+/***********************************************************************/
+JDBCCOL::JDBCCOL(void) : COLBLK()
+{
+ Crp = NULL;
+ Buf_Type = TYPE_INT; // This is a count(*) column
+ // Set additional Dos access method information for column.
+ Long = sizeof(int);
+ To_Val = NULL;
+//Slen = 0;
+//StrLen = &Slen;
+//Sqlbuf = NULL;
+ Bufp = NULL;
+ Blkp = NULL;
+ Rank = 1;
+} // end of JDBCCOL constructor
+
+/***********************************************************************/
+/* JDBCCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
+{
+ Crp = col1->Crp;
+ Long = col1->Long;
+ //strcpy(F_Date, col1->F_Date);
+ To_Val = col1->To_Val;
+//Slen = col1->Slen;
+//StrLen = col1->StrLen;
+//Sqlbuf = col1->Sqlbuf;
+ Bufp = col1->Bufp;
+ Blkp = col1->Blkp;
+ Rank = col1->Rank;
+} // end of JDBCCOL copy constructor
+
+/***********************************************************************/
+/* SetBuffer: prepare a column block for write operation. */
+/***********************************************************************/
+bool JDBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
+{
+ if (!(To_Val = value)) {
+ sprintf(g->Message, MSG(VALUE_ERROR), Name);
+ return true;
+ } else if (Buf_Type == value->GetType()) {
+ // Values are of the (good) column type
+ if (Buf_Type == TYPE_DATE) {
+ // If any of the date values is formatted
+ // output format must be set for the receiving table
+ if (GetDomain() || ((DTVAL *)value)->IsFormatted())
+ goto newval; // This will make a new value;
+
+ } else if (Buf_Type == TYPE_DOUBLE)
+ // Float values must be written with the correct (column) precision
+ // Note: maybe this should be forced by ShowValue instead of this ?
+ value->SetPrec(GetScale());
+
+ Value = value; // Directly access the external value
+ } else {
+ // Values are not of the (good) column type
+ if (check) {
+ sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
+ GetTypeName(Buf_Type), GetTypeName(value->GetType()));
+ return true;
+ } // endif check
+
+ newval:
+ if (InitValue(g)) // Allocate the matching value block
+ return true;
+
+ } // endif's Value, Buf_Type
+
+ // Because Colblk's have been made from a copy of the original TDB in
+ // case of Update, we must reset them to point to the original one.
+ if (To_Tdb->GetOrig())
+ To_Tdb = (PTDB)To_Tdb->GetOrig();
+
+ // Set the Column
+ Status = (ok) ? BUF_EMPTY : BUF_NO;
+ return false;
+} // end of SetBuffer
+
+/***********************************************************************/
+/* ReadColumn: when SQLFetch is used there is nothing to do as the */
+/* column buffer was bind to the record set. This is also the case */
+/* when calculating MaxSize (Bufp is NULL even when Rows is not). */
+/***********************************************************************/
+void JDBCCOL::ReadColumn(PGLOBAL g)
+{
+ PTDBJDBC tdbp = (PTDBJDBC)To_Tdb;
+ int i = tdbp->Fpos - 1, n = tdbp->CurNum;
+
+ if (tdbp->Memory == 3) {
+ // Get the value from the stored memory
+ if (Crp->Nulls && Crp->Nulls[i] == '*') {
+ Value->Reset();
+ Value->SetNull(true);
+ } else {
+ Value->SetValue_pvblk(Crp->Kdata, i);
+ Value->SetNull(false);
+ } // endif Nulls
+
+ return;
+ } // endif Memory
+
+ /*********************************************************************/
+ /* Get the column value. */
+ /*********************************************************************/
+ tdbp->Jcp->SetColumnValue(Rank, Name, Value);
+
+ if (tdbp->Memory != 2)
+ return;
+
+ /*********************************************************************/
+ /* Fill the allocated result structure. */
+ /*********************************************************************/
+ if (Value->IsNull()) {
+ if (Crp->Nulls)
+ Crp->Nulls[i] = '*'; // Null value
+
+ Crp->Kdata->Reset(i);
+ } else
+ Crp->Kdata->SetValue(Value, i);
+
+} // end of ReadColumn
+
+#if 0
+/***********************************************************************/
+/* AllocateBuffers: allocate the extended buffer for SQLExtendedFetch */
+/* or Fetch. Note: we use Long+1 here because JDBC must have space */
+/* for the ending null character. */
+/***********************************************************************/
+void JDBCCOL::AllocateBuffers(PGLOBAL g, int rows)
+{
+ if (Buf_Type == TYPE_DATE)
+ Sqlbuf = (TIMESTAMP_STRUCT*)PlugSubAlloc(g, NULL,
+ sizeof(TIMESTAMP_STRUCT));
+
+ if (!rows)
+ return;
+
+ if (Buf_Type == TYPE_DATE)
+ Bufp = PlugSubAlloc(g, NULL, rows * sizeof(TIMESTAMP_STRUCT));
+ else {
+ Blkp = AllocValBlock(g, NULL, Buf_Type, rows, GetBuflen(),
+ GetScale(), true, false, false);
+ Bufp = Blkp->GetValPointer();
+ } // endelse
+
+ if (rows > 1)
+ StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(SQLLEN));
+
+} // end of AllocateBuffers
+
+/***********************************************************************/
+/* Returns the buffer to use for Fetch or Extended Fetch. */
+/***********************************************************************/
+void *JDBCCOL::GetBuffer(DWORD rows)
+{
+ if (rows && To_Tdb) {
+ assert(rows == (DWORD)((TDBJDBC*)To_Tdb)->Rows);
+ return Bufp;
+ } else
+ return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val();
+
+} // end of GetBuffer
+
+/***********************************************************************/
+/* Returns the buffer length to use for Fetch or Extended Fetch. */
+/***********************************************************************/
+SWORD JDBCCOL::GetBuflen(void)
+{
+ SWORD flen;
+
+ switch (Buf_Type) {
+ case TYPE_DATE:
+ flen = (SWORD)sizeof(TIMESTAMP_STRUCT);
+ break;
+ case TYPE_STRING:
+ case TYPE_DECIM:
+ flen = (SWORD)Value->GetClen() + 1;
+ break;
+ default:
+ flen = (SWORD)Value->GetClen();
+ } // endswitch Buf_Type
+
+ return flen;
+} // end of GetBuflen
+#endif // 0
+
+/***********************************************************************/
+/* WriteColumn: make sure the bind buffer is updated. */
+/***********************************************************************/
+void JDBCCOL::WriteColumn(PGLOBAL g)
+{
+ /*********************************************************************/
+ /* Do convert the column value if necessary. */
+ /*********************************************************************/
+ if (Value != To_Val)
+ Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
+
+#if 0
+ if (Buf_Type == TYPE_DATE) {
+ struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
+
+ Sqlbuf->second = dbtime->tm_sec;
+ Sqlbuf->minute = dbtime->tm_min;
+ Sqlbuf->hour = dbtime->tm_hour;
+ Sqlbuf->day = dbtime->tm_mday;
+ Sqlbuf->month = dbtime->tm_mon + 1;
+ Sqlbuf->year = dbtime->tm_year + 1900;
+ Sqlbuf->fraction = 0;
+ } else if (Buf_Type == TYPE_DECIM) {
+ // Some data sources require local decimal separator
+ char *p, sep = ((PTDBJDBC)To_Tdb)->Sep;
+
+ if (sep && (p = strchr(Value->GetCharValue(), '.')))
+ *p = sep;
+
+ } // endif Buf_Type
+
+ if (Nullable)
+ *StrLen = (Value->IsNull()) ? SQL_NULL_DATA :
+ (IsTypeChar(Buf_Type)) ? SQL_NTS : 0;
+#endif // 0
+} // end of WriteColumn
+
+/* -------------------------- Class TDBXJDC -------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBXJDC class. */
+/***********************************************************************/
+TDBXJDC::TDBXJDC(PJDBCDEF tdp) : TDBJDBC(tdp)
+{
+ Cmdlist = NULL;
+ Cmdcol = NULL;
+ Mxr = tdp->Maxerr;
+ Nerr = 0;
+} // end of TDBXJDC constructor
+
+/***********************************************************************/
+/* Allocate XSRC column description block. */
+/***********************************************************************/
+PCOL TDBXJDC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+{
+ PJSRCCOL colp = new(g)JSRCCOL(cdp, this, cprec, n);
+
+ if (!colp->Flag)
+ Cmdcol = colp->GetName();
+
+ return colp;
+} // end of MakeCol
+
+/***********************************************************************/
+/* MakeCMD: make the SQL statement to send to JDBC connection. */
+/***********************************************************************/
+PCMD TDBXJDC::MakeCMD(PGLOBAL g)
+{
+ PCMD xcmd = NULL;
+
+ if (To_CondFil) {
+ if (Cmdcol) {
+ if (!stricmp(Cmdcol, To_CondFil->Body) &&
+ (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
+ xcmd = To_CondFil->Cmds;
+ } else
+ strcpy(g->Message, "Invalid command specification filter");
+
+ } else
+ strcpy(g->Message, "No command column in select list");
+
+ } else if (!Srcdef)
+ strcpy(g->Message, "No Srcdef default command");
+ else
+ xcmd = new(g) CMD(g, Srcdef);
+
+ return xcmd;
+} // end of MakeCMD
+
+#if 0
+/***********************************************************************/
+/* JDBC Bind Parameter function. */
+/***********************************************************************/
+bool TDBXJDC::BindParameters(PGLOBAL g)
+{
+ PJDBCCOL colp;
+
+ for (colp = (PJDBCCOL)Columns; colp; colp = (PJDBCCOL)colp->Next) {
+ colp->AllocateBuffers(g, 0);
+
+ if (Jcp->BindParam(colp))
+ return true;
+
+ } // endfor colp
+
+ return false;
+} // end of BindParameters
+#endif // 0
+
+/***********************************************************************/
+/* XDBC GetMaxSize: returns table size (not always one row). */
+/***********************************************************************/
+int TDBXJDC::GetMaxSize(PGLOBAL g)
+{
+ if (MaxSize < 0)
+ MaxSize = 2; // Just a guess
+
+ return MaxSize;
+} // end of GetMaxSize
+
+/***********************************************************************/
+/* JDBC Access Method opening routine. */
+/* New method now that this routine is called recursively (last table */
+/* first in reverse order): index blocks are immediately linked to */
+/* join block of next table if it exists or else are discarted. */
+/***********************************************************************/
+bool TDBXJDC::OpenDB(PGLOBAL g)
+{
+ bool rc = false;
+
+ if (trace)
+ htrc("JDBC OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
+ this, Tdb_No, Use, Mode);
+
+ if (Use == USE_OPEN) {
+ strcpy(g->Message, "Multiple execution is not allowed");
+ return true;
+ } // endif use
+
+ /*********************************************************************/
+ /* Open an JDBC connection for this table. */
+ /* Note: this may not be the proper way to do. Perhaps it is better */
+ /* to test whether a connection is already open for this datasource */
+ /* and if so to allocate just a new result set. But this only for */
+ /* drivers allowing concurency in getting results ??? */
+ /*********************************************************************/
+ if (!Jcp) {
+ Jcp = new(g) JDBConn(g, this);
+ } else if (Jcp->IsOpen())
+ Jcp->Close();
+
+ if (Jcp->Open(&Ops) == RC_FX)
+ return true;
+
+ Use = USE_OPEN; // Do it now in case we are recursively called
+
+ if (Mode != MODE_READ && Mode != MODE_READX) {
+ strcpy(g->Message, "No INSERT/DELETE/UPDATE of XJDBC tables");
+ return true;
+ } // endif Mode
+
+ /*********************************************************************/
+ /* Get the command to execute. */
+ /*********************************************************************/
+ if (!(Cmdlist = MakeCMD(g))) {
+ Jcp->Close();
+ return true;
+ } // endif Query
+
+ Rows = 1;
+ return false;
+} // end of OpenDB
+
+/***********************************************************************/
+/* ReadDB: Data Base read routine for xdbc access method. */
+/***********************************************************************/
+int TDBXJDC::ReadDB(PGLOBAL g)
+{
+ if (Cmdlist) {
+ int rc;
+
+ if (!Query)
+ Query = new(g) STRING(g, 0, Cmdlist->Cmd);
+ else
+ Query->Set(Cmdlist->Cmd);
+
+ if ((rc = Jcp->ExecSQLcommand(Query->GetStr())) == RC_FX)
+ Nerr++;
+
+ if (rc == RC_NF)
+ AftRows = Jcp->m_Aff;
+ else if (rc == RC_OK)
+ AftRows = Jcp->m_Ncol;
+
+ Fpos++; // Used for progress info
+ Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
+ return RC_OK;
+ } else
+ return RC_EF;
+
+} // end of ReadDB
+
+/***********************************************************************/
+/* Data Base write line routine for JDBC access method. */
+/***********************************************************************/
+int TDBXJDC::WriteDB(PGLOBAL g)
+{
+ strcpy(g->Message, "Execsrc tables are read only");
+ return RC_FX;
+} // end of DeleteDB
+
+/***********************************************************************/
+/* Data Base delete line routine for JDBC access method. */
+/***********************************************************************/
+int TDBXJDC::DeleteDB(PGLOBAL g, int irc)
+{
+ strcpy(g->Message, "NO_XJDBC_DELETE");
+ return RC_FX;
+} // end of DeleteDB
+
+/* --------------------------- JSRCCOL ------------------------------- */
+
+/***********************************************************************/
+/* JSRCCOL public constructor. */
+/***********************************************************************/
+JSRCCOL::JSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : JDBCCOL(cdp, tdbp, cprec, i, am)
+{
+ // Set additional JDBC access method information for column.
+ Flag = cdp->GetOffset();
+} // end of JSRCCOL constructor
+
+/***********************************************************************/
+/* ReadColumn: set column value according to Flag. */
+/***********************************************************************/
+void JSRCCOL::ReadColumn(PGLOBAL g)
+{
+ PTDBXJDC tdbp = (PTDBXJDC)To_Tdb;
+
+ switch (Flag) {
+ case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
+ case 1: Value->SetValue(tdbp->AftRows); break;
+ case 2: Value->SetValue_psz(g->Message); break;
+ default: Value->SetValue_psz("Invalid Flag"); break;
+ } // endswitch Flag
+
+} // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: Should never be called. */
+/***********************************************************************/
+void JSRCCOL::WriteColumn(PGLOBAL g)
+{
+ // Should never be called
+} // end of WriteColumn
+
+/* ---------------------------TDBJDRV class -------------------------- */
+
+/***********************************************************************/
+/* GetResult: Get the list of JDBC drivers. */
+/***********************************************************************/
+PQRYRES TDBJDRV::GetResult(PGLOBAL g)
+{
+ return JDBCDrivers(g, Maxres, false);
+} // end of GetResult
+
+/* ---------------------------TDBJTB class --------------------------- */
+
+/***********************************************************************/
+/* TDBJTB class constructor. */
+/***********************************************************************/
+TDBJTB::TDBJTB(PJDBCDEF tdp) : TDBJDRV(tdp)
+{
+ Schema = tdp->Tabschema;
+ Tab = tdp->Tabname;
+ Tabtype = tdp->Tabtype;
+ Ops.Driver = tdp->Driver;
+ Ops.Url = tdp->Url;
+ Ops.User = tdp->Username;
+ Ops.Pwd = tdp->Password;
+ Ops.Fsize = 0;
+ Ops.Scrollable = false;
+} // end of TDBJTB constructor
+
+/***********************************************************************/
+/* GetResult: Get the list of JDBC tables. */
+/***********************************************************************/
+PQRYRES TDBJTB::GetResult(PGLOBAL g)
+{
+ return JDBCTables(g, Schema, Tab, Tabtype, Maxres, false, &Ops);
+} // end of GetResult
+
+/* --------------------------TDBJDBCL class -------------------------- */
+
+/***********************************************************************/
+/* GetResult: Get the list of JDBC table columns. */
+/***********************************************************************/
+PQRYRES TDBJDBCL::GetResult(PGLOBAL g)
+{
+ return JDBCColumns(g, Schema, Tab, NULL, Maxres, false, &Ops);
+} // end of GetResult
+
+#if 0
+/* ---------------------------TDBJSRC class -------------------------- */
+
+/***********************************************************************/
+/* GetResult: Get the list of JDBC data sources. */
+/***********************************************************************/
+PQRYRES TDBJSRC::GetResult(PGLOBAL g)
+{
+ return JDBCDataSources(g, Maxres, false);
+} // end of GetResult
+
+/* ------------------------ End of TabJDBC --------------------------- */
+#endif // 0
diff --git a/storage/connect/tabjdbc.h b/storage/connect/tabjdbc.h
new file mode 100644
index 00000000000..1624b903610
--- /dev/null
+++ b/storage/connect/tabjdbc.h
@@ -0,0 +1,343 @@
+/*************** Tabjdbc H Declares Source Code File (.H) **************/
+/* Name: TABJDBC.H Version 1.0 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* */
+/* This file contains the TDBJDBC classes declares. */
+/***********************************************************************/
+#include "colblk.h"
+#include "resource.h"
+
+typedef class JDBCDEF *PJDBCDEF;
+typedef class TDBJDBC *PTDBJDBC;
+typedef class JDBCCOL *PJDBCCOL;
+typedef class TDBXJDC *PTDBXJDC;
+typedef class JSRCCOL *PJSRCCOL;
+//typedef class TDBOIF *PTDBOIF;
+//typedef class OIFCOL *POIFCOL;
+//typedef class TDBJSRC *PTDBJSRC;
+
+/***********************************************************************/
+/* JDBC table. */
+/***********************************************************************/
+class DllExport JDBCDEF : public TABDEF { /* Logical table description */
+ friend class TDBJDBC;
+ friend class TDBXJDC;
+ friend class TDBJDRV;
+ friend class TDBJTB;
+public:
+ // Constructor
+ JDBCDEF(void);
+
+ // Implementation
+ virtual const char *GetType(void) { return "JDBC"; }
+ PSZ GetTabname(void) { return Tabname; }
+ PSZ GetTabschema(void) { return Tabschema; }
+ PSZ GetTabcat(void) { return Tabcat; }
+ PSZ GetSrcdef(void) { return Srcdef; }
+ char GetSep(void) { return (Sep) ? *Sep : 0; }
+ int GetQuoted(void) { return Quoted; }
+//int GetCatver(void) { return Catver; }
+ int GetOptions(void) { return Options; }
+
+ // Methods
+ virtual int Indexable(void) { return 2; }
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual PTDB GetTable(PGLOBAL g, MODE m);
+
+protected:
+ // Members
+ PSZ Driver; /* JDBC driver */
+ PSZ Url; /* JDBC driver URL */
+ PSZ Tabname; /* External table name */
+ PSZ Tabschema; /* External table schema */
+ PSZ Username; /* User connect name */
+ PSZ Password; /* Password connect info */
+ PSZ Tabcat; /* External table catalog */
+ PSZ Tabtype; /* External table type */
+ PSZ Srcdef; /* The source table SQL definition */
+ PSZ Qchar; /* Identifier quoting character */
+ PSZ Qrystr; /* The original query */
+ PSZ Sep; /* Decimal separator */
+ int Options; /* Open connection options */
+//int Cto; /* Open connection timeout */
+//int Qto; /* Query (command) timeout */
+ int Quoted; /* Identifier quoting level */
+ int Maxerr; /* Maxerr for an Exec table */
+ int Maxres; /* Maxres for a catalog table */
+ int Memory; /* Put result set in memory */
+ bool Scrollable; /* Use scrollable cursor */
+ bool Xsrc; /* Execution type */
+}; // end of JDBCDEF
+
+#if !defined(NJDBC)
+#include "jdbconn.h"
+
+/***********************************************************************/
+/* This is the JDBC Access Method class declaration for files from */
+/* other DB drivers to be accessed via JDBC. */
+/***********************************************************************/
+class TDBJDBC : public TDBASE {
+ friend class JDBCCOL;
+ friend class JDBConn;
+public:
+ // Constructor
+ TDBJDBC(PJDBCDEF tdp = NULL);
+ TDBJDBC(PTDBJDBC tdbp);
+
+ // Implementation
+ virtual AMT GetAmType(void) { return TYPE_AM_JDBC; }
+ virtual PTDB Duplicate(PGLOBAL g) { return (PTDB)new(g)TDBJDBC(this); }
+
+ // Methods
+ virtual PTDB CopyOne(PTABS t);
+ virtual int GetRecpos(void);
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
+//virtual PSZ GetFile(PGLOBAL g);
+//virtual void SetFile(PGLOBAL g, PSZ fn);
+ virtual void ResetSize(void);
+ //virtual int GetAffectedRows(void) {return AftRows;}
+ virtual PSZ GetServer(void) { return "JDBC"; }
+ virtual int Indexable(void) { return 2; }
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ virtual int Cardinality(PGLOBAL g);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual int GetProgMax(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ virtual void CloseDB(PGLOBAL g);
+ virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr);
+
+protected:
+ // Internal functions
+ int Decode(char *utf, char *buf, size_t n);
+ bool MakeSQL(PGLOBAL g, bool cnt);
+ bool MakeInsert(PGLOBAL g);
+ bool MakeCommand(PGLOBAL g);
+ //bool MakeFilter(PGLOBAL g, bool c);
+ bool SetParameters(PGLOBAL g);
+ //char *MakeUpdate(PGLOBAL g);
+ //char *MakeDelete(PGLOBAL g);
+
+ // Members
+ JDBConn *Jcp; // Points to a JDBC connection class
+ JDBCCOL *Cnp; // Points to count(*) column
+ JDBCPARM Ops; // Additional parameters
+ PSTRG Query; // Constructed SQL query
+ char *TableName; // Points to JDBC table name
+ char *Schema; // Points to JDBC table Schema
+ char *User; // User connect info
+ char *Pwd; // Password connect info
+ char *Catalog; // Points to JDBC table Catalog
+ char *Srcdef; // The source table SQL definition
+ char *Count; // Points to count(*) SQL statement
+//char *Where; // Points to local where clause
+ char *Quote; // The identifier quoting character
+ char *MulConn; // Used for multiple JDBC tables
+ char *DBQ; // The address part of Connect string
+ char *Qrystr; // The original query
+ char Sep; // The decimal separator
+ int Options; // Connect options
+//int Cto; // Connect timeout
+//int Qto; // Query timeout
+ int Quoted; // The identifier quoting level
+ int Fpos; // Position of last read record
+ int Curpos; // Cursor position of last fetch
+ int AftRows; // The number of affected rows
+ int Rows; // Rowset size
+ int CurNum; // Current buffer line number
+ int Rbuf; // Number of lines read in buffer
+ int BufSize; // Size of connect string buffer
+ int Ncol; // The column number
+ int Nparm; // The number of statement parameters
+ int Memory; // 0: No 1: Alloc 2: Put 3: Get
+//bool Scrollable; // Use scrollable cursor --> in Ops
+ bool Placed; // True for position reading
+ bool Prepared; // True when using prepared statement
+ bool Werr; // Write error
+ bool Rerr; // Rewind error
+ PQRYRES Qrp; // Points to storage result
+}; // end of class TDBJDBC
+
+/***********************************************************************/
+/* Class JDBCCOL: JDBC access method column descriptor. */
+/* This A.M. is used for JDBC tables. */
+/***********************************************************************/
+class JDBCCOL : public COLBLK {
+ friend class TDBJDBC;
+public:
+ // Constructors
+ JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "JDBC");
+ JDBCCOL(JDBCCOL *colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ virtual int GetAmType(void) { return TYPE_AM_JDBC; }
+//SQLLEN *GetStrLen(void) { return StrLen; }
+ int GetRank(void) { return Rank; }
+//PVBLK GetBlkp(void) {return Blkp;}
+ void SetCrp(PCOLRES crp) { Crp = crp; }
+
+ // Methods
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+//void AllocateBuffers(PGLOBAL g, int rows);
+//void *GetBuffer(DWORD rows);
+//SWORD GetBuflen(void);
+ // void Print(PGLOBAL g, FILE *, uint);
+
+protected:
+ // Constructor used by GetMaxSize
+ JDBCCOL(void);
+
+ // Members
+ //TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
+ PCOLRES Crp; // To storage result
+ void *Bufp; // To extended buffer
+ PVBLK Blkp; // To Value Block
+ //char F_Date[12]; // Internal Date format
+ PVAL To_Val; // To value used for Insert
+//SQLLEN *StrLen; // As returned by JDBC
+//SQLLEN Slen; // Used with Fetch
+ int Rank; // Rank (position) number in the query
+}; // end of class JDBCCOL
+
+/***********************************************************************/
+/* This is the JDBC Access Method class declaration that send */
+/* commands to be executed by other DB JDBC drivers. */
+/***********************************************************************/
+class TDBXJDC : public TDBJDBC {
+ friend class JSRCCOL;
+ friend class JDBConn;
+public:
+ // Constructors
+ TDBXJDC(PJDBCDEF tdp = NULL);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_XDBC;}
+
+ // Methods
+ //virtual int GetRecpos(void);
+ //virtual PSZ GetFile(PGLOBAL g);
+ //virtual void SetFile(PGLOBAL g, PSZ fn);
+ //virtual void ResetSize(void);
+ //virtual int GetAffectedRows(void) {return AftRows;}
+ //virtual PSZ GetServer(void) {return "JDBC";}
+
+ // Database routines
+ virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
+ //virtual int GetProgMax(PGLOBAL g);
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual bool OpenDB(PGLOBAL g);
+ virtual int ReadDB(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
+ virtual int DeleteDB(PGLOBAL g, int irc);
+ //virtual void CloseDB(PGLOBAL g);
+
+protected:
+ // Internal functions
+ PCMD MakeCMD(PGLOBAL g);
+ //bool BindParameters(PGLOBAL g);
+
+ // Members
+ PCMD Cmdlist; // The commands to execute
+ char *Cmdcol; // The name of the Xsrc command column
+ int Mxr; // Maximum errors before closing
+ int Nerr; // Number of errors so far
+}; // end of class TDBXJDC
+
+/***********************************************************************/
+/* Used by table in source execute mode. */
+/***********************************************************************/
+class JSRCCOL : public JDBCCOL {
+ friend class TDBXJDC;
+public:
+ // Constructors
+ JSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "JDBC");
+
+ // Implementation
+ //virtual int GetAmType(void) {return TYPE_AM_JDBC;}
+
+ // Methods
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
+ // void Print(PGLOBAL g, FILE *, uint);
+
+protected:
+ // Members
+ char *Buffer; // To get returned message
+ int Flag; // Column content desc
+}; // end of class JSRCCOL
+
+/***********************************************************************/
+/* This is the class declaration for the Drivers catalog table. */
+/***********************************************************************/
+class TDBJDRV : public TDBCAT {
+public:
+ // Constructor
+ TDBJDRV(PJDBCDEF tdp) : TDBCAT(tdp) {Maxres = tdp->Maxres;}
+
+protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // Members
+ int Maxres; // Returned lines limit
+}; // end of class TDBJDRV
+
+/***********************************************************************/
+/* This is the class declaration for the tables catalog table. */
+/***********************************************************************/
+class TDBJTB : public TDBJDRV {
+public:
+ // Constructor
+ TDBJTB(PJDBCDEF tdp);
+
+protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // Members
+ char *Schema; // Points to schema name or NULL
+ char *Tab; // Points to JDBC table name or pattern
+ char *Tabtype; // Points to JDBC table type
+ JDBCPARM Ops; // Additional parameters
+}; // end of class TDBJTB
+
+/***********************************************************************/
+/* This is the class declaration for the columns catalog table. */
+/***********************************************************************/
+class TDBJDBCL : public TDBJTB {
+public:
+ // Constructor
+ TDBJDBCL(PJDBCDEF tdp) : TDBJTB(tdp) {}
+
+protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // No additional Members
+}; // end of class TDBJCL
+
+#if 0
+/***********************************************************************/
+/* This is the class declaration for the Data Sources catalog table. */
+/***********************************************************************/
+class TDBJSRC : public TDBJDRV {
+public:
+ // Constructor
+ TDBJSRC(PJDBCDEF tdp) : TDBJDRV(tdp) {}
+
+protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // No additional Members
+}; // end of class TDBJSRC
+#endif // 0
+
+#endif // !NJDBC