diff options
author | Olivier Bertrand <bertrandop@gmail.com> | 2016-05-12 23:08:22 +0200 |
---|---|---|
committer | Olivier Bertrand <bertrandop@gmail.com> | 2016-05-12 23:08:22 +0200 |
commit | 5af076e564602559158db38a69a9cb4a09537986 (patch) | |
tree | 4acb32d38b0bcc1c73556e4078d3f45b46a7532d /storage | |
parent | 26adbb2dd50db0d62e6f7f5d12bdab0cd3a0bf31 (diff) | |
download | mariadb-git-5af076e564602559158db38a69a9cb4a09537986.tar.gz |
Add all changes made on 10.1
Diffstat (limited to 'storage')
-rw-r--r-- | storage/connect/CMakeLists.txt | 28 | ||||
-rw-r--r-- | storage/connect/JdbcInterface.class | bin | 0 -> 15117 bytes | |||
-rw-r--r-- | storage/connect/JdbcInterface.java | 639 | ||||
-rw-r--r-- | storage/connect/ha_connect.cc | 282 | ||||
-rw-r--r-- | storage/connect/inihandl.c | 7 | ||||
-rw-r--r-- | storage/connect/jdbccat.h | 29 | ||||
-rw-r--r-- | storage/connect/jdbconn.cpp | 2151 | ||||
-rw-r--r-- | storage/connect/jdbconn.h | 171 | ||||
-rw-r--r-- | storage/connect/jsonudf.cpp | 155 | ||||
-rw-r--r-- | storage/connect/jsonudf.h | 4 | ||||
-rw-r--r-- | storage/connect/plgdbutl.cpp | 34 | ||||
-rw-r--r-- | storage/connect/reldef.cpp | 7 | ||||
-rw-r--r-- | storage/connect/tabjdbc.cpp | 1707 | ||||
-rw-r--r-- | storage/connect/tabjdbc.h | 343 |
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 Binary files differnew file mode 100644 index 00000000000..816f575212b --- /dev/null +++ b/storage/connect/JdbcInterface.class 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 |