summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--storage/connect/.gitattributes25
-rw-r--r--storage/connect/.gitignore264
-rw-r--r--storage/connect/CMakeLists.txt32
-rw-r--r--storage/connect/JdbcApacheInterface.classbin0 -> 15357 bytes
-rw-r--r--storage/connect/JdbcApacheInterface.java709
-rw-r--r--storage/connect/JdbcDSInterface.classbin0 -> 16175 bytes
-rw-r--r--storage/connect/JdbcDSInterface.java743
-rw-r--r--storage/connect/JdbcInterface.classbin0 -> 15215 bytes
-rw-r--r--storage/connect/JdbcInterface.java712
-rw-r--r--storage/connect/ha_connect.cc324
-rw-r--r--storage/connect/inihandl.c7
-rw-r--r--storage/connect/jdbccat.h29
-rw-r--r--storage/connect/jdbconn.cpp2137
-rw-r--r--storage/connect/jdbconn.h193
-rw-r--r--storage/connect/jsonudf.cpp442
-rw-r--r--storage/connect/jsonudf.h4
-rw-r--r--storage/connect/mycat.cc16
-rw-r--r--storage/connect/myconn.cpp32
-rw-r--r--storage/connect/myconn.h6
-rw-r--r--storage/connect/mysql-test/connect/disabled.def16
-rw-r--r--storage/connect/mysql-test/connect/r/jdbc.result269
-rw-r--r--storage/connect/mysql-test/connect/r/jdbc_new.result216
-rw-r--r--storage/connect/mysql-test/connect/r/jdbc_oracle.result70
-rw-r--r--storage/connect/mysql-test/connect/r/jdbc_postgresql.result65
-rw-r--r--storage/connect/mysql-test/connect/std_data/girls.txt5
-rw-r--r--storage/connect/mysql-test/connect/t/jdbc.test143
-rw-r--r--storage/connect/mysql-test/connect/t/jdbc_new.test179
-rw-r--r--storage/connect/mysql-test/connect/t/jdbc_oracle.test56
-rw-r--r--storage/connect/mysql-test/connect/t/jdbc_postgresql.test53
-rw-r--r--storage/connect/mysql-test/connect/t/jdbconn.inc31
-rw-r--r--storage/connect/mysql-test/connect/t/jdbconn_cleanup.inc6
-rw-r--r--storage/connect/odbconn.cpp2
-rw-r--r--storage/connect/plgdbsem.h12
-rw-r--r--storage/connect/plgdbutl.cpp34
-rw-r--r--storage/connect/plugutil.c4
-rw-r--r--storage/connect/reldef.cpp7
-rw-r--r--storage/connect/tabcol.cpp2
-rw-r--r--storage/connect/tabjdbc.cpp1810
-rw-r--r--storage/connect/tabjdbc.h346
-rw-r--r--storage/connect/tabodbc.cpp8
-rw-r--r--storage/connect/tabtbl.cpp8
41 files changed, 8781 insertions, 236 deletions
diff --git a/storage/connect/.gitattributes b/storage/connect/.gitattributes
new file mode 100644
index 00000000000..d21fdf8f212
--- /dev/null
+++ b/storage/connect/.gitattributes
@@ -0,0 +1,25 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.c text
+*.cc text
+*.cpp text
+*.h text
+*.test text
+
+# Declare files that will always have LF line endings on checkout.
+*.result text eol=lf
+mysql-test/connect/std_data/*.txt text eol=lf
+mysql-test/connect/std_data/*.dat text eol=lf
+
+# Denote all files that are truly binary and should not be modified.
+*.png binary
+*.jpg binary
+
+*.c diff=cpp
+*.h diff=cpp
+*.cc diff=cpp
+*.ic diff=cpp
+*.cpp diff=cpp
diff --git a/storage/connect/.gitignore b/storage/connect/.gitignore
new file mode 100644
index 00000000000..e2fa07ee143
--- /dev/null
+++ b/storage/connect/.gitignore
@@ -0,0 +1,264 @@
+# Edited by Olivier Bertrand
+*-t
+*.a
+*.ctest
+*.o
+*.reject
+*.so
+*.so.*
+*.spec
+*~
+*.bak
+*.log
+*.cmake
+*.tgz
+*.msg
+.*.swp
+*.ninja
+.ninja_*
+.gdb_history
+
+CMakeFiles/
+connect.dir/
+connect.dir-Copie/
+Debug/
+MinSizeRel/
+Release/
+RelWithDebInfo/
+
+# C and C++
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.ko
+*.obj
+*.elf
+*.exp
+*.manifest
+*.dep
+*.idb
+*.res
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Static libraries
+*.lib
+*.a
+*.la
+*.lai
+*.lo
+
+# Compiled Dynamic libraries
+*.so
+*.so.*
+*.dylib
+*.dll
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+*.ncb
+*.sln
+
+*.vcproj
+*.vcproj.*
+*.vcproj.*.*
+*.vcproj.*.*.*
+*.vcxproj
+*.vcxproj.*
+*.vcxproj.*.*
+*.vcxproj.*.*.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# If using the old MSBuild-Integrated Package Restore, uncomment this:
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+# sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index 6ef8b45daf9..254d074612a 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -37,7 +37,7 @@ user_connect.h valblk.h value.h xindex.h xobject.h xtable.h)
#
# Definitions that are shared for all OSes
#
-add_definitions( -DMARIADB -DFORCE_INIT_OF_VARS )
+add_definitions( -DMARIADB -DFORCE_INIT_OF_VARS -Dconnect_EXPORTS)
add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT -DPIVOT_SUPPORT )
@@ -232,6 +232,34 @@ 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 java wrapper 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
+ JdbcDSInterface.java JdbcDSInterface.class
+ JdbcApacheInterface.java JdbcApacheInterface.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
@@ -252,5 +280,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/JdbcApacheInterface.class b/storage/connect/JdbcApacheInterface.class
new file mode 100644
index 00000000000..acd4258e3d3
--- /dev/null
+++ b/storage/connect/JdbcApacheInterface.class
Binary files differ
diff --git a/storage/connect/JdbcApacheInterface.java b/storage/connect/JdbcApacheInterface.java
new file mode 100644
index 00000000000..fdbc5bff203
--- /dev/null
+++ b/storage/connect/JdbcApacheInterface.java
@@ -0,0 +1,709 @@
+import java.math.*;
+import java.sql.*;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.commons.dbcp2.BasicDataSource;
+
+public class JdbcApacheInterface {
+ boolean DEBUG = false;
+ String Errmsg = "No error";
+ Connection conn = null;
+ DatabaseMetaData dbmd = null;
+ Statement stmt = null;
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ ResultSetMetaData rsmd = null;
+ static Hashtable<String,BasicDataSource> pool = new Hashtable<String, BasicDataSource>();
+
+ // === Constructors/finalize =========================================
+ public JdbcApacheInterface() {
+ this(true);
+ } // end of default constructor
+
+ public JdbcApacheInterface(boolean b) {
+ DEBUG = b;
+ } // end of constructor
+
+ private void SetErrmsg(Exception e) {
+ if (DEBUG)
+ System.out.println(e.getMessage());
+
+ Errmsg = e.toString();
+ } // end of SetErrmsg
+
+ private void SetErrmsg(String s) {
+ if (DEBUG)
+ System.out.println(s);
+
+ Errmsg = s;
+ } // end of SetErrmsg
+
+ public String GetErrmsg() {
+ String err = Errmsg;
+
+ Errmsg = "No error";
+ return err;
+ } // end of GetErrmsg
+
+ public int JdbcConnect(String[] parms, int fsize, boolean scrollable) {
+ int rc = 0;
+ String url = parms[1];
+ BasicDataSource ds = null;
+
+ if (url == null) {
+ SetErrmsg("URL cannot be null");
+ return -1;
+ } // endif url
+
+ try {
+ if ((ds = pool.get(url)) == null) {
+ ds = new BasicDataSource();
+ ds.setDriverClassName(parms[0]);
+ ds.setUrl(url);
+ ds.setUsername(parms[2]);
+ ds.setPassword(parms[3]);
+ pool.put(url, ds);
+ } // endif ds
+
+ // Get a connection from the data source
+ conn = ds.getConnection();
+
+ // 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 (SQLException se) {
+ SetErrmsg(se);
+ rc = -2;
+ } catch( Exception e ) {
+ SetErrmsg(e);
+ rc = -3;
+ } // end try/catch
+
+ return rc;
+ } // end of JdbcConnect
+
+ public int CreatePrepStmt(String sql) {
+ int rc = 0;
+
+ try {
+ pstmt = conn.prepareStatement(sql);
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc = -1;
+ } catch (Exception e) {
+ SetErrmsg(e);
+ rc = -2;
+ } // end try/catch
+
+ return rc;
+ } // end of CreatePrepStmt
+
+ public void SetStringParm(int i, String s) {
+ try {
+ pstmt.setString(i, s);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetStringParm
+
+ public void SetIntParm(int i, int n) {
+ try {
+ pstmt.setInt(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetIntParm
+
+ public void SetShortParm(int i, short n) {
+ try {
+ pstmt.setShort(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetShortParm
+
+ public void SetBigintParm(int i, long n) {
+ try {
+ pstmt.setLong(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetBigintParm
+
+ public void SetFloatParm(int i, float f) {
+ try {
+ pstmt.setFloat(i, f);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetFloatParm
+
+ public void SetDoubleParm(int i, double d) {
+ try {
+ pstmt.setDouble(i, d);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetDoubleParm
+
+ public void SetTimestampParm(int i, Timestamp t) {
+ try {
+ pstmt.setTimestamp(i, t);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetTimestampParm
+
+ public int ExecutePrep() {
+ int n = -3;
+
+ if (pstmt != null) try {
+ n = pstmt.executeUpdate();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ b = true;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ rc += 1;
+ } // nothing more we can do
+
+ // Close the statement and the connection
+ if (rs != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing result set");
+
+ rs.close();
+ } catch(SQLException se) {
+ SetErrmsg(se);
+ rc = 2;
+ } // nothing more we can do
+
+ if (stmt != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing statement");
+
+ stmt.close();
+ } catch(SQLException se) {
+ SetErrmsg(se);
+ rc += 4;
+ } // nothing more we can do
+
+ ClosePrepStmt();
+
+ if (conn != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing connection");
+
+ conn.close();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc += 8;
+ } //end try/catch
+
+ if (DEBUG)
+ 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) {
+ SetErrmsg(e);
+ m = -1;
+ } // 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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ ncol = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ ncol = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(e);
+ n = -2;
+ } //end try/catch
+
+ return n;
+ } // end of ExecuteUpdate
+
+ public int ReadNext() {
+ if (rs != null) {
+ try {
+ return rs.next() ? 1 : 0;
+ } catch (SQLException se) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return 666; // Not a type
+ } // 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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of ColumnDesc
+
+ 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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of TimestampField
+
+ public String ObjectField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getObject(n).toString() : rs.getObject(name).toString();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of ObjectField
+
+ 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
+
+ /**
+ * Adds the specified path to the java library path
+ * from Fahd Shariff blog
+ *
+ * @param pathToAdd the path to add
+ static public int addLibraryPath(String pathToAdd) {
+ System.out.println("jpath = " + pathToAdd);
+
+ try {
+ Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
+ usrPathsField.setAccessible(true);
+
+ //get array of paths
+ String[] paths = (String[])usrPathsField.get(null);
+
+ //check if the path to add is already present
+ for (String path : paths) {
+ System.out.println("path = " + path);
+
+ if (path.equals(pathToAdd))
+ return -5;
+
+ } // endfor path
+
+ //add the new path
+ String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
+ newPaths[paths.length] = pathToAdd;
+ usrPathsField.set(null, newPaths);
+ System.setProperty("java.library.path",
+ System.getProperty("java.library.path") + File.pathSeparator + pathToAdd);
+ Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
+ fieldSysPath.setAccessible(true);
+ fieldSysPath.set(null, null);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ return -1;
+ } // end try/catch
+
+ return 0;
+ } // end of addLibraryPath
+ */
+
+} // end of class JdbcApacheInterface
diff --git a/storage/connect/JdbcDSInterface.class b/storage/connect/JdbcDSInterface.class
new file mode 100644
index 00000000000..d56c04bd81f
--- /dev/null
+++ b/storage/connect/JdbcDSInterface.class
Binary files differ
diff --git a/storage/connect/JdbcDSInterface.java b/storage/connect/JdbcDSInterface.java
new file mode 100644
index 00000000000..09f545bfb74
--- /dev/null
+++ b/storage/connect/JdbcDSInterface.java
@@ -0,0 +1,743 @@
+import java.math.*;
+import java.sql.*;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import org.mariadb.jdbc.MariaDbDataSource;
+import org.postgresql.jdbc2.optional.PoolingDataSource;
+import com.mysql.cj.jdbc.MysqlDataSource;
+import oracle.jdbc.pool.OracleDataSource;
+
+public class JdbcDSInterface {
+ boolean DEBUG = false;
+ String Errmsg = "No error";
+ Connection conn = null;
+ DatabaseMetaData dbmd = null;
+ Statement stmt = null;
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ ResultSetMetaData rsmd = null;
+ Hashtable<String,DataSource> dst = null;
+
+ // === Constructors/finalize =========================================
+ public JdbcDSInterface() {
+ this(true);
+ } // end of default constructor
+
+ public JdbcDSInterface(boolean b) {
+ DEBUG = b;
+ dst = new Hashtable<String, DataSource>();
+ } // end of constructor
+
+ private void SetErrmsg(Exception e) {
+ if (DEBUG)
+ System.out.println(e.getMessage());
+
+ Errmsg = e.toString();
+ } // end of SetErrmsg
+
+ private void SetErrmsg(String s) {
+ if (DEBUG)
+ System.out.println(s);
+
+ Errmsg = s;
+ } // end of SetErrmsg
+
+ public String GetErrmsg() {
+ String err = Errmsg;
+
+ Errmsg = "No error";
+ return err;
+ } // end of GetErrmsg
+
+ public int JdbcConnect(String[] parms, int fsize, boolean scrollable) {
+ int rc = 0;
+ String url = parms[1];
+ DataSource ds = null;
+ MysqlDataSource mds = null;
+ MariaDbDataSource ads = null;
+ OracleDataSource ods = null;
+ PoolingDataSource pds = null;
+
+ if (url == null) {
+ SetErrmsg("URL cannot be null");
+ return -1;
+ } // endif driver
+
+ try {
+ if ((ds = dst.get(url)) == null) {
+ if (url.toLowerCase().contains("mysql")) {
+ mds = new MysqlDataSource();
+ mds.setURL(url);
+ mds.setUser(parms[2]);
+ mds.setPassword(parms[3]);
+ ds = mds;
+ } else if (url.toLowerCase().contains("mariadb")) {
+ ads = new MariaDbDataSource();
+ ads.setUrl(url);
+ ads.setUser(parms[2]);
+ ads.setPassword(parms[3]);
+ ds = ads;
+ } else if (url.toLowerCase().contains("oracle")) {
+ ods = new OracleDataSource();
+ ods.setURL(url);
+ ods.setUser(parms[2]);
+ ods.setPassword(parms[3]);
+ ds = ods;
+ } else if (url.toLowerCase().contains("postgresql")) {
+ pds = new PoolingDataSource();
+ pds.setUrl(url);
+ pds.setUser(parms[2]);
+ pds.setPassword(parms[3]);
+ ds = pds;
+ } else {
+ SetErrmsg("Unsupported driver");
+ return -4;
+ } // endif driver
+
+ dst.put(url, ds);
+ } // endif ds
+
+ // Get a connection from the data source
+ conn = ds.getConnection();
+
+ // 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 (SQLException se) {
+ SetErrmsg(se);
+ rc = -2;
+ } catch( Exception e ) {
+ SetErrmsg(e);
+ rc = -3;
+ } // end try/catch
+
+ return rc;
+ } // end of JdbcConnect
+
+ public int CreatePrepStmt(String sql) {
+ int rc = 0;
+
+ try {
+ pstmt = conn.prepareStatement(sql);
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc = -1;
+ } catch (Exception e) {
+ SetErrmsg(e);
+ rc = -2;
+ } // end try/catch
+
+ return rc;
+ } // end of CreatePrepStmt
+
+ public void SetStringParm(int i, String s) {
+ try {
+ pstmt.setString(i, s);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetStringParm
+
+ public void SetIntParm(int i, int n) {
+ try {
+ pstmt.setInt(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetIntParm
+
+ public void SetShortParm(int i, short n) {
+ try {
+ pstmt.setShort(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetShortParm
+
+ public void SetBigintParm(int i, long n) {
+ try {
+ pstmt.setLong(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetBigintParm
+
+ public void SetFloatParm(int i, float f) {
+ try {
+ pstmt.setFloat(i, f);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetFloatParm
+
+ public void SetDoubleParm(int i, double d) {
+ try {
+ pstmt.setDouble(i, d);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetDoubleParm
+
+ public void SetTimestampParm(int i, Timestamp t) {
+ try {
+ pstmt.setTimestamp(i, t);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetTimestampParm
+
+ public int ExecutePrep() {
+ int n = -3;
+
+ if (pstmt != null) try {
+ n = pstmt.executeUpdate();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ b = true;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ rc += 1;
+ } // nothing more we can do
+
+ // Close the statement and the connection
+ if (rs != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing result set");
+
+ rs.close();
+ } catch(SQLException se) {
+ SetErrmsg(se);
+ rc = 2;
+ } // nothing more we can do
+
+ if (stmt != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing statement");
+
+ stmt.close();
+ } catch(SQLException se) {
+ SetErrmsg(se);
+ rc += 4;
+ } // nothing more we can do
+
+ ClosePrepStmt();
+
+ if (conn != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing connection");
+
+ conn.close();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc += 8;
+ } //end try/catch
+
+ if (DEBUG)
+ 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) {
+ SetErrmsg(e);
+ m = -1;
+ } // 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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ ncol = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ ncol = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(e);
+ n = -2;
+ } //end try/catch
+
+ return n;
+ } // end of ExecuteUpdate
+
+ public int ReadNext() {
+ if (rs != null) {
+ try {
+ return rs.next() ? 1 : 0;
+ } catch (SQLException se) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return 666; // Not a type
+ } // 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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of ColumnDesc
+
+ 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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of TimestampField
+
+ public String ObjectField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getObject(n).toString() : rs.getObject(name).toString();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of ObjectField
+
+ 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
+
+ /**
+ * Adds the specified path to the java library path
+ * from Fahd Shariff blog
+ *
+ * @param pathToAdd the path to add
+ static public int addLibraryPath(String pathToAdd) {
+ System.out.println("jpath = " + pathToAdd);
+
+ try {
+ Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
+ usrPathsField.setAccessible(true);
+
+ //get array of paths
+ String[] paths = (String[])usrPathsField.get(null);
+
+ //check if the path to add is already present
+ for (String path : paths) {
+ System.out.println("path = " + path);
+
+ if (path.equals(pathToAdd))
+ return -5;
+
+ } // endfor path
+
+ //add the new path
+ String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
+ newPaths[paths.length] = pathToAdd;
+ usrPathsField.set(null, newPaths);
+ System.setProperty("java.library.path",
+ System.getProperty("java.library.path") + File.pathSeparator + pathToAdd);
+ Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
+ fieldSysPath.setAccessible(true);
+ fieldSysPath.set(null, null);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ return -1;
+ } // end try/catch
+
+ return 0;
+ } // end of addLibraryPath
+ */
+
+} // end of class JdbcDSInterface
diff --git a/storage/connect/JdbcInterface.class b/storage/connect/JdbcInterface.class
new file mode 100644
index 00000000000..8c5ba6439f3
--- /dev/null
+++ b/storage/connect/JdbcInterface.class
Binary files differ
diff --git a/storage/connect/JdbcInterface.java b/storage/connect/JdbcInterface.java
new file mode 100644
index 00000000000..f9a6e734454
--- /dev/null
+++ b/storage/connect/JdbcInterface.java
@@ -0,0 +1,712 @@
+import java.math.*;
+import java.sql.*;
+//import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+//import java.io.File;
+//import java.lang.reflect.Field;
+
+public class JdbcInterface {
+ boolean DEBUG = false;
+ String Errmsg = "No error";
+ 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
+
+ private void SetErrmsg(Exception e) {
+ if (DEBUG)
+ System.out.println(e.getMessage());
+
+ Errmsg = e.toString();
+ } // end of SetErrmsg
+
+ public String GetErrmsg() {
+ String err = Errmsg;
+
+ Errmsg = "No error";
+ return err;
+ } // end of GetErrmsg
+
+ 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()) {
+ if (DEBUG)
+ System.out.println("Loading class" + parms[0]);
+
+ 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) {
+ SetErrmsg(e);
+ rc = -1;
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc = -2;
+ } catch( Exception e ) {
+ SetErrmsg(e);
+ rc = -3;
+ } // end try/catch
+
+ return rc;
+ } // end of JdbcConnect
+
+ public int CreatePrepStmt(String sql) {
+ int rc = 0;
+
+ try {
+ pstmt = conn.prepareStatement(sql);
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc = -1;
+ } catch (Exception e) {
+ SetErrmsg(e);
+ rc = -2;
+ } // end try/catch
+
+ return rc;
+ } // end of CreatePrepStmt
+
+ public void SetStringParm(int i, String s) {
+ try {
+ pstmt.setString(i, s);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetStringParm
+
+ public void SetIntParm(int i, int n) {
+ try {
+ pstmt.setInt(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetIntParm
+
+ public void SetShortParm(int i, short n) {
+ try {
+ pstmt.setShort(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetShortParm
+
+ public void SetBigintParm(int i, long n) {
+ try {
+ pstmt.setLong(i, n);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetBigintParm
+
+ public void SetFloatParm(int i, float f) {
+ try {
+ pstmt.setFloat(i, f);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetFloatParm
+
+ public void SetDoubleParm(int i, double d) {
+ try {
+ pstmt.setDouble(i, d);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetDoubleParm
+
+ public void SetTimestampParm(int i, Timestamp t) {
+ try {
+ pstmt.setTimestamp(i, t);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ } // end try/catch
+
+ } // end of SetTimestampParm
+
+ public int ExecutePrep() {
+ int n = -3;
+
+ if (pstmt != null) try {
+ n = pstmt.executeUpdate();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ b = true;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ rc += 1;
+ } // nothing more we can do
+
+ // Close the statement and the connection
+ if (rs != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing result set");
+
+ rs.close();
+ } catch(SQLException se) {
+ SetErrmsg(se);
+ rc = 2;
+ } // nothing more we can do
+
+ if (stmt != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing statement");
+
+ stmt.close();
+ } catch(SQLException se) {
+ SetErrmsg(se);
+ rc += 4;
+ } // nothing more we can do
+
+ ClosePrepStmt();
+
+ if (conn != null)
+ try {
+ if (DEBUG)
+ System.out.println("Closing connection");
+
+ conn.close();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ rc += 8;
+ } //end try/catch
+
+ if (DEBUG)
+ 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) {
+ SetErrmsg(e);
+ m = -1;
+ } // 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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ ncol = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ ncol = -1;
+ } catch (Exception e) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ n = -1;
+ } catch (Exception e) {
+ SetErrmsg(e);
+ n = -2;
+ } //end try/catch
+
+ return n;
+ } // end of ExecuteUpdate
+
+ public int ReadNext() {
+ if (rs != null) {
+ try {
+ return rs.next() ? 1 : 0;
+ } catch (SQLException se) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return 666; // Not a type
+ } // 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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of ColumnDesc
+
+ 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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(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) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of TimestampField
+
+ public String ObjectField(int n, String name) {
+ if (rs == null) {
+ System.out.println("No result set");
+ } else try {
+ return (n > 0) ? rs.getObject(n).toString() : rs.getObject(name).toString();
+ } catch (SQLException se) {
+ SetErrmsg(se);
+ } //end try/catch
+
+ return null;
+ } // end of ObjectField
+
+ 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
+
+ /**
+ * Adds the specified path to the java library path
+ * from Fahd Shariff blog
+ *
+ * @param pathToAdd the path to add
+ static public int addLibraryPath(String pathToAdd) {
+ System.out.println("jpath = " + pathToAdd);
+
+ try {
+ Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
+ usrPathsField.setAccessible(true);
+
+ //get array of paths
+ String[] paths = (String[])usrPathsField.get(null);
+
+ //check if the path to add is already present
+ for (String path : paths) {
+ System.out.println("path = " + path);
+
+ if (path.equals(pathToAdd))
+ return -5;
+
+ } // endfor path
+
+ //add the new path
+ String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
+ newPaths[paths.length] = pathToAdd;
+ usrPathsField.set(null, newPaths);
+ System.setProperty("java.library.path",
+ System.getProperty("java.library.path") + File.pathSeparator + pathToAdd);
+ Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
+ fieldSysPath.setAccessible(true);
+ fieldSysPath.set(null, null);
+ } catch (Exception e) {
+ SetErrmsg(e);
+ return -1;
+ } // end try/catch
+
+ return 0;
+ } // end of addLibraryPath
+ */
+
+} // end of class JdbcInterface
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index e8aa54fc87c..45ad4a484e1 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,13 +115,11 @@
#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
#define NOPARSE
+#define NJDBC
#if defined(UNIX)
#include "osutil.h"
#endif // UNIX
@@ -130,6 +128,10 @@
#if defined(ODBC_SUPPORT)
#include "odbccat.h"
#endif // ODBC_SUPPORT
+#if defined(JDBC_SUPPORT)
+#include "tabjdbc.h"
+#include "jdbconn.h"
+#endif // JDBC_SUPPORT
#include "xtable.h"
#include "tabmysql.h"
#include "filamdbf.h"
@@ -169,7 +171,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 +192,18 @@ extern "C" {
} // extern "C"
#endif // XMSG
+#if defined(JDBC_SUPPORT)
+ char *JvmPath;
+ char *ClassPath;
+ char *Wrapper;
+#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 +211,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 +648,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 +675,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 +694,17 @@ static int connect_done_func(void *)
#ifdef LIBXML2_SUPPORT
XmlCleanupParserLib();
-#endif // LIBXML2_SUPPORT
+#endif // LIBXML2_SUPPORT
-#if !defined(__WIN__)
-//PROFILE_End(); Causes signal 11
-#endif // !__WIN__
+#ifdef JDBC_SUPPORT
+ JDBConn::ResetJVM();
+#endif // JDBC_SUPPORT
+
+#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)
@@ -1117,7 +1142,7 @@ int GetIntegerTableOption(PGLOBAL g, PTOS options, char *opname, int idef)
else if (!stricmp(opname, "Compressed"))
opval= (options->compressed);
- if (opval == (ulonglong)NO_IVAL) {
+ if ((ulonglong) opval == (ulonglong)NO_IVAL) {
char *pv;
if ((pv= GetListOption(g, opname, options->oplist)))
@@ -1758,9 +1783,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;
@@ -1935,7 +1961,7 @@ int ha_connect::MakeRecord(char *buf)
if (trace > 1)
htrc("Maps: read=%08X write=%08X vcol=%08X defr=%08X defw=%08X\n",
*table->read_set->bitmap, *table->write_set->bitmap,
- *table->vcol_set->bitmap,
+ (table->vcol_set) ? *table->vcol_set->bitmap : 0,
*table->def_read_set.bitmap, *table->def_write_set.bitmap);
// Avoid asserts in field::store() for columns that are not updated
@@ -2077,8 +2103,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 +2654,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 +2662,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 +2985,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) {
@@ -4109,7 +4136,8 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn, bool quick)
/* 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:
@@ -4779,7 +4807,11 @@ int ha_connect::delete_or_rename_table(const char *name, const char *to)
DBUG_RETURN(rc);
// Get the share info from the .frm file
- if (!open_table_def(thd, share)) {
+ Dummy_error_handler error_handler;
+ thd->push_internal_handler(&error_handler);
+ bool got_error= open_table_def(thd, share);
+ thd->pop_internal_handler();
+ if (!got_error) {
// Now we can work
if ((pos= share->option_struct)) {
if (check_privileges(thd, pos, db))
@@ -4792,12 +4824,6 @@ int ha_connect::delete_or_rename_table(const char *name, const char *to)
} // endif open_table_def
-// This below was done to avoid DBUG_ASSERT in some case that
-// we don't know anymore what they were. It was suppressed because
-// it did cause assertion in other cases (see MDEV-7935)
-// } else // Avoid infamous DBUG_ASSERT
-// thd->get_stmt_da()->reset_diagnostics_area();
-
free_table_share(share);
} else // Temporary file
ok= true;
@@ -5129,13 +5155,19 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
char *nsp= NULL, *cls= NULL;
#endif // __WIN__
int port= 0, hdr= 0, mxr= 0, mxe= 0, rc= 0;
- int cop __attribute__((unused))= 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;
@@ -5146,15 +5178,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;
@@ -5173,7 +5200,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) {
@@ -5202,6 +5229,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"));
@@ -5262,44 +5294,75 @@ 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= strz(g, create_info->connect_string))) {
+ strcpy(g->Message, "Missing URL");
+ } else {
+ // Store JDBC additional parameters
+ int rc;
+ PJDBCDEF jdef= new(g) JDBCDEF();
+
+ jdef->SetName(create_info->alias);
+ sjp= (PJPARM)PlugSubAlloc(g, NULL, sizeof(JDBCPARM));
+ sjp->Driver= driver;
+ sjp->Fsize= 0;
+ sjp->Scrollable= false;
+
+ if ((rc = jdef->ParseURL(g, url, false)) == RC_OK) {
+ sjp->Url= url;
+ sjp->User= (char*)user;
+ sjp->Pwd= (char*)pwd;
+ ok= true;
+ } else if (rc == RC_NF) {
+ if (jdef->GetTabname())
+ tab= jdef->GetTabname();
+
+ ok= jdef->SetParms(sjp);
+ } // endif rc
+
+ } // endif's
+
+ supfnc |= (FNC_DRIVER | FNC_TABLE);
+ break;
+#endif // JDBC_SUPPORT
+ case TAB_DBF:
dbf= true;
// Passthru
case TAB_CSV:
@@ -5418,7 +5481,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)
@@ -5460,7 +5523,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,21 +5619,16 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
len= crp->Length;
dec= crp->Prec;
flg= crp->Flag;
- v= crp->Var;
+ v= (crp->Kdata->IsUnsigned()) ? 'U' : crp->Var;
tm= (crp->Kdata->IsNullable()) ? 0 : NOT_NULL_FLAG;
if (!len && typ == TYPE_STRING)
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 {
@@ -5563,12 +5651,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) {
@@ -5637,10 +5720,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
@@ -5689,7 +5772,40 @@ 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:
+ case TYPE_DECIM:
+ // Some data sources do not count dec in length (prec)
+ prec += (dec + 2); // To be safe
+ break;
+ default:
+ dec= 0;
+ } // endswitch typ
+
+ } else
+#endif // ODBC_SUPPORT
+ // Make the arguments as required by add_fields
if (typ == TYPE_DOUBLE)
prec= len;
@@ -5697,25 +5813,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;
@@ -6757,6 +6863,27 @@ 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);
+
+static MYSQL_SYSVAR_STR(java_wrapper, Wrapper,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "Java wrapper class",
+ // check_class_path, update_class_path,
+ NULL, NULL, "JdbcInterface");
+#endif // JDBC_SUPPORT
+
+
static struct st_mysql_sys_var* connect_system_variables[]= {
MYSQL_SYSVAR(xtrace),
MYSQL_SYSVAR(conv_size),
@@ -6774,7 +6901,12 @@ 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),
+ MYSQL_SYSVAR(java_wrapper),
+#endif // JDBC_SUPPORT
+ NULL
};
maria_declare_plugin(connect)
@@ -6791,6 +6923,6 @@ maria_declare_plugin(connect)
NULL, /* status variables */
connect_system_variables, /* system variables */
"1.04.0006", /* string version */
- MariaDB_PLUGIN_MATURITY_BETA /* maturity */
+ MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */
}
maria_declare_plugin_end;
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..a8c0b193dcd
--- /dev/null
+++ b/storage/connect/jdbconn.cpp
@@ -0,0 +1,2137 @@
+/************ 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 "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
+extern char *Wrapper; // The connect_java_wrapper global variable value
+
+/***********************************************************************/
+/* Static JDBConn objects. */
+/***********************************************************************/
+void *JDBConn::LibJvm = NULL;
+CRTJVM JDBConn::CreateJavaVM = NULL;
+GETJVM JDBConn::GetCreatedJavaVMs = NULL;
+#if defined(_DEBUG)
+GETDEF JDBConn::GetDefaultJavaVMInitArgs = NULL;
+#endif // _DEBUG
+
+/***********************************************************************/
+/* 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
+ case -3: // VARBINARY
+ type = TYPE_DECIM;
+ break;
+ case 4: // INTEGER
+ type = TYPE_INT;
+ break;
+ case 5: // SMALLINT
+ type = TYPE_SHORT;
+ break;
+ case -6: // TINYINT
+ case -7: // BIT
+ 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 -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)
+{
+ PQRYRES qrp;
+ JDBConn *jcp = new(g)JDBConn(g, NULL);
+
+ if (jcp->Open(sjp))
+ return NULL;
+
+ qrp = jcp->GetMetaData(g, src);
+ jcp->Close();
+ return qrp;
+} // end of JDBCSrcCols
+
+/**************************************************************************/
+/* JDBCTables: constructs the result blocks containing all tables in */
+/* an JDBC database that will be retrieved by GetData commands. */
+/**************************************************************************/
+PQRYRES JDBCTables(PGLOBAL g, 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 > 0) ? (n + 1) : 0;
+ n = jcp->GetMaxValue(3); // Max schema name length
+ length[1] = (n > 0) ? (n + 1) : 0;
+ n = jcp->GetMaxValue(4); // Max table name length
+ length[2] = (n > 0) ? (n + 1) : 128;
+ } else {
+ maxres = 0;
+ length[0] = 128;
+ length[1] = 128;
+ length[2] = 128;
+ length[4] = 255;
+ } // endif info
+
+ if (trace)
+ 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 java wrapper class
+ job = nullptr; // The java wrapper class object
+ xqid = xuid = xid = grs = readid = fetchid = typid = errid = nullptr;
+ prepid = xpid = pcid = nullptr;
+ chrfldid = intfldid = dblfldid = fltfldid = datfldid = bigfldid = nullptr;
+//m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT;
+//m_QueryTimeout = DEFAULT_QUERY_TIMEOUT;
+//m_UpdateOptions = 0;
+ Msg = NULL;
+ 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. */
+/***********************************************************************/
+bool JDBConn::Check(jint rc)
+{
+ jstring s;
+
+ if (env->ExceptionCheck()) {
+ 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();
+ } else if (rc < 0) {
+ s = (jstring)env->CallObjectMethod(job, errid);
+ Msg = (char*)env->GetStringUTFChars(s, (jboolean)false);
+ } else
+ Msg = NULL;
+
+ return (Msg != NULL);
+} // end of Check
+
+/***********************************************************************/
+/* Get MethodID if not exists yet. */
+/***********************************************************************/
+bool JDBConn::gmID(PGLOBAL g, jmethodID& mid, const char *name, const char *sig)
+{
+ if (mid == nullptr) {
+ mid = env->GetMethodID(jdi, name, sig);
+
+ if (Check()) {
+ strcpy(g->Message, Msg);
+ return true;
+ } else
+ return false;
+
+ } else
+ return false;
+
+} // end of gmID
+
+#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)
+{
+ jint m;
+ jmethodID maxid = nullptr;
+
+ if (gmID(m_G, maxid, "GetMaxValue", "(I)I"))
+ return -1;
+
+ // call method
+ if (Check(m = env->CallIntMethod(job, maxid, n)))
+ htrc("GetMaxValue: %s", Msg);
+
+ return (int)m;
+} // end of GetMaxValue
+
+/***********************************************************************/
+/* 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;
+#if defined(_DEBUG)
+ GetDefaultJavaVMInitArgs = NULL;
+#endif // _DEBUG
+ } // 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;
+#if defined(_DEBUG)
+ } else if (!(GetDefaultJavaVMInitArgs = (GETDEF)GetProcAddress((HINSTANCE)LibJvm,
+ "JNI_GetDefaultJavaVMInitArgs"))) {
+ sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(),
+ "JNI_GetDefaultJavaVMInitArgs");
+ FreeLibrary((HMODULE)LibJvm);
+ LibJvm = NULL;
+#endif // _DEBUG
+ } // 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;
+#if defined(_DEBUG)
+ } else if (!(GetDefaultJavaVMInitArgs = (GETDEF)dlsym(LibJvm,
+ "JNI_GetDefaultJavaVMInitArgs"))) {
+ error = dlerror();
+ sprintf(g->Message, MSG(GET_FUNC_ERR), "JNI_GetDefaultJavaVMInitArgs", SVP(error));
+ dlclose(LibJvm);
+ LibJvm = NULL;
+#endif // _DEBUG
+ } // endif LibJvm
+#endif // !__WIN__
+
+ } // endif LibJvm
+
+ return LibJvm == NULL;
+} // end of GetJVM
+
+/***********************************************************************/
+/* Open: connect to a data source. */
+/***********************************************************************/
+int JDBConn::Open(PJPARM sop)
+{
+ bool err = false;
+ 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 = ';';
+#define N 1
+//#define N 2
+//#define N 3
+#else
+ sep = ':';
+#define N 1
+#endif
+
+ //================== prepare loading of Java VM ============================
+ JavaVMInitArgs vm_args; // Initialization arguments
+ JavaVMOption* options = new JavaVMOption[N]; // JVM invocation options
+
+ // where to find java .class
+ if (ClassPath && *ClassPath) {
+ jpop->Append(sep);
+ jpop->Append(ClassPath);
+ } // endif ClassPath
+
+ if ((cp = getenv("CLASSPATH"))) {
+ jpop->Append(sep);
+ jpop->Append(cp);
+ } // endif cp
+
+ if (trace) {
+ htrc("ClassPath=%s\n", ClassPath);
+ htrc("CLASSPATH=%s\n", cp);
+ htrc("%s\n", jpop->GetStr());
+ } // endif trace
+
+ options[0].optionString = jpop->GetStr();
+#if N == 2
+ options[1].optionString = "-Xcheck:jni";
+#endif
+#if N == 3
+ options[1].optionString = "-Xms256M";
+ options[2].optionString = "-Xmx512M";
+#endif
+#if defined(_DEBUG)
+ vm_args.version = JNI_VERSION_1_2; // minimum Java version
+ rc = GetDefaultJavaVMInitArgs(&vm_args);
+#else
+ vm_args.version = JNI_VERSION_1_6; // minimum Java version
+#endif // _DEBUG
+ vm_args.nOptions = N; // 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 =======================================
+#if defined(_DEBUG)
+ jint ver = env->GetVersion();
+ printf("JVM Version %d.%d\n", ((ver>>16)&0x0f), (ver&0x0f));
+#endif //_DEBUG
+
+ // try to find the java wrapper class
+ jdi = env->FindClass(Wrapper);
+
+ if (jdi == nullptr) {
+ sprintf(g->Message, "ERROR: class %s not found!", Wrapper);
+ 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(rc))) {
+ 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) {
+ sprintf(g->Message, "ERROR: %s constructor not found!", Wrapper);
+ 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) {
+ sprintf(g->Message, "%s class object not constructed!", Wrapper);
+ return RC_FX;
+ } // endif job
+
+ errid = env->GetMethodID(jdi, "GetErrmsg", "()Ljava/lang/String;");
+
+ if (env->ExceptionCheck()) {
+ strcpy(g->Message, "ERROR: method GetErrmsg() not found!");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return RC_FX;
+ } // endif Check
+
+ if (!sop) // DRIVER catalog table
+ return RC_OK;
+
+ jmethodID cid = nullptr;
+
+ if (gmID(g, cid, "JdbcConnect", "([Ljava/lang/String;IZ)I"))
+ return RC_FX;
+
+ // Build the java string array
+ jobjectArray parms = env->NewObjectArray(4, // constructs java array of 4
+ env->FindClass("java/lang/String"), NULL); // Strings
+
+ 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;
+
+ // 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);
+ err = Check(rc);
+ env->DeleteLocalRef(parms); // Not used anymore
+
+ if (err) {
+ sprintf(g->Message, "Connecting: %s rc=%d", Msg, (int)rc);
+ return RC_FX;
+ } // endif Msg
+
+ if (gmID(g, typid, "ColumnType", "(ILjava/lang/String;)I"))
+ return RC_FX;
+ else
+ m_Opened = true;
+
+ return RC_OK;
+} // end of Open
+
+/***********************************************************************/
+/* Execute an SQL command. */
+/***********************************************************************/
+int JDBConn::ExecSQLcommand(char *sql)
+{
+ int rc;
+ jint n;
+ jstring qry;
+ PGLOBAL& g = m_G;
+
+ // Get the methods used to execute a query and get the result
+ if (gmID(g, xid, "Execute", "(Ljava/lang/String;)I") ||
+ gmID(g, grs, "GetResult", "()I"))
+ return RC_FX;
+
+ qry = env->NewStringUTF(sql);
+ n = env->CallIntMethod(job, xid, qry);
+ env->DeleteLocalRef(qry);
+
+ if (Check(n)) {
+ sprintf(g->Message, "Execute: %s", Msg);
+ return RC_FX;
+ } // endif n
+
+ m_Ncol = env->CallIntMethod(job, grs);
+
+ if (Check(m_Ncol)) {
+ sprintf(g->Message, "GetResult: %s", Msg);
+ rc = RC_FX;
+ } else if (m_Ncol) {
+ 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");
+ rc = RC_NF;
+ } // endif ncol
+
+ return rc;
+} // end of ExecSQLcommand
+
+/***********************************************************************/
+/* Fetch next row. */
+/***********************************************************************/
+int JDBConn::Fetch(int pos)
+{
+ jint rc = JNI_ERR;
+ PGLOBAL& g = m_G;
+
+ if (m_Full) // Result set has one row
+ return 1;
+
+ if (pos) {
+ if (!m_Scrollable) {
+ strcpy(g->Message, "Cannot fetch(pos) if FORWARD ONLY");
+ return rc;
+ } else if (gmID(m_G, fetchid, "Fetch", "(I)Z"))
+ return rc;
+
+ if (env->CallBooleanMethod(job, fetchid, pos))
+ rc = m_Rows;
+
+ } else {
+ if (gmID(g, readid, "ReadNext", "()I"))
+ return rc;
+
+ rc = env->CallBooleanMethod(job, readid);
+
+ if (!Check(rc)) {
+ if (rc == 0)
+ m_Full = (m_Fetch == 1);
+ else
+ m_Fetch++;
+
+ m_Rows += (int)rc;
+ } else
+ sprintf(g->Message, "Fetch: %s", Msg);
+
+ } // 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 (gmID(m_G, fetchid, "Fetch", "(I)Z"))
+ return -1;
+
+ 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 = nullptr;
+
+ if (gmID(m_G, did, "JdbcDisconnect", "()I"))
+ printf("%s\n", Msg);
+ else if (Check(env->CallIntMethod(job, did)))
+ printf("jdbcDisconnect: %s\n", Msg);
+
+ if ((rc = jvm->DetachCurrentThread()) != JNI_OK)
+ printf("DetachCurrentThread: rc=%d\n", (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;
+ jint ctyp;
+ jlong dtv;
+ jstring cn, jn = nullptr;
+ jobject dob;
+
+ 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
+
+ // Returns 666 is case of error
+ ctyp = env->CallIntMethod(job, typid, rank, jn);
+
+ if (Check((ctyp == 666) ? -1 : 1)) {
+ 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
+ if (!gmID(g, chrfldid, "StringField", "(ILjava/lang/String;)Ljava/lang/String;")) {
+ cn = (jstring)env->CallObjectMethod(job, chrfldid, (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
+ case -7: // BIT
+ if (!gmID(g, intfldid, "IntField", "(ILjava/lang/String;)I"))
+ val->SetValue((int)env->CallIntMethod(job, intfldid, rank, jn));
+ else
+ val->Reset();
+
+ break;
+ case 8: // DOUBLE
+ case 2: // NUMERIC
+ case 3: // DECIMAL
+ if (!gmID(g, dblfldid, "DoubleField", "(ILjava/lang/String;)D"))
+ val->SetValue((double)env->CallDoubleMethod(job, dblfldid, rank, jn));
+ else
+ val->Reset();
+
+ break;
+ case 7: // REAL
+ case 6: // FLOAT
+ if (!gmID(g, fltfldid, "FloatField", "(ILjava/lang/String;)F"))
+ val->SetValue((float)env->CallFloatMethod(job, fltfldid, rank, jn));
+ else
+ val->Reset();
+
+ break;
+ case 91: // DATE
+ case 92: // TIME
+ case 93: // TIMESTAMP
+ if (!gmID(g, datfldid, "TimestampField",
+ "(ILjava/lang/String;)Ljava/sql/Timestamp;")) {
+ dob = env->CallObjectMethod(job, datfldid, (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
+ if (!gmID(g, bigfldid, "BigintField", "(ILjava/lang/String;)J"))
+ val->SetValue((long long)env->CallLongMethod(job, bigfldid, (jint)rank, jn));
+ else
+ val->Reset();
+
+ break;
+ /* case java.sql.Types.SMALLINT:
+ System.out.print(jdi.IntField(i));
+ break;
+ case java.sql.Types.BOOLEAN:
+ System.out.print(jdi.BooleanField(i)); */
+ case 0: // NULL
+ val->SetNull(true);
+ // passthru
+ default:
+ val->Reset();
+ } // endswitch Type
+
+ if (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)
+{
+ bool b = true;
+ PGLOBAL& g = m_G;
+
+ if (!gmID(g, prepid, "CreatePrepStmt", "(Ljava/lang/String;)I")) {
+ // Create the prepared statement
+ jstring qry = env->NewStringUTF(sql);
+
+ if (Check(env->CallBooleanMethod(job, prepid, qry)))
+ sprintf(g->Message, "CreatePrepStmt: %s", Msg);
+ else
+ b = false;
+
+ env->DeleteLocalRef(qry);
+ } // endif prepid
+
+ return b;
+} // end of PrepareSQL
+
+/***********************************************************************/
+/* Execute an SQL query that returns a result set. */
+/***********************************************************************/
+int JDBConn::ExecuteQuery(char *sql)
+{
+ int rc = RC_FX;
+ jint ncol;
+ jstring qry;
+ PGLOBAL& g = m_G;
+
+ // Get the methods used to execute a query and get the result
+ if (!gmID(g, xqid, "ExecuteQuery", "(Ljava/lang/String;)I")) {
+ qry = env->NewStringUTF(sql);
+ ncol = env->CallIntMethod(job, xqid, qry);
+
+ if (!Check(ncol)) {
+ m_Ncol = (int)ncol;
+ m_Aff = 0; // Affected rows
+ rc = RC_OK;
+ } else
+ sprintf(g->Message, "ExecuteQuery: %s", Msg);
+
+ env->DeleteLocalRef(qry);
+ } // endif xqid
+
+ return rc;
+} // end of ExecuteQuery
+
+/***********************************************************************/
+/* Execute an SQL query and get the affected rows. */
+/***********************************************************************/
+int JDBConn::ExecuteUpdate(char *sql)
+{
+ int rc = RC_FX;
+ jint n;
+ jstring qry;
+ PGLOBAL& g = m_G;
+
+ // Get the methods used to execute a query and get the affected rows
+ if (!gmID(g, xuid, "ExecuteUpdate", "(Ljava/lang/String;)I")) {
+ qry = env->NewStringUTF(sql);
+ n = env->CallIntMethod(job, xuid, qry);
+
+ if (!Check(n)) {
+ m_Ncol = 0;
+ m_Aff = (int)n; // Affected rows
+ rc = RC_OK;
+ } else
+ sprintf(g->Message, "ExecuteUpdate: %s n=%d", Msg, n);
+
+ env->DeleteLocalRef(qry);
+ } // endif xuid
+
+ return rc;
+} // end of ExecuteUpdate
+
+/***********************************************************************/
+/* Get the number of lines of the result set. */
+/***********************************************************************/
+int JDBConn::GetResultSize(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;
+
+ // Get the methods used to execute a prepared statement
+ if (!gmID(g, xpid, "ExecutePrep", "()I")) {
+ jint n = env->CallIntMethod(job, xpid);
+
+ if (n == -3)
+ strcpy(g->Message, "SQL statement is not prepared");
+ else if (Check(n))
+ sprintf(g->Message, "ExecutePrep: %s", Msg);
+ else {
+ m_Aff = (int)n;
+ rc = RC_OK;
+ } // endswitch n
+
+ } // endif xpid
+
+ return rc;
+} // end of ExecuteSQL
+
+/***********************************************************************/
+/* Set a parameter for inserting. */
+/***********************************************************************/
+bool JDBConn::SetParam(JDBCCOL *colp)
+{
+ PGLOBAL& g = m_G;
+ bool rc = false;
+ PVAL val = colp->GetValue();
+ jint n, 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:
+ if (gmID(g, setid, "SetStringParm", "(ILjava/lang/String;)V"))
+ return true;
+
+ jst = env->NewStringUTF(val->GetCharValue());
+ env->CallVoidMethod(job, setid, i, jst);
+ break;
+ case TYPE_INT:
+ if (gmID(g, setid, "SetIntParm", "(II)V"))
+ return true;
+
+ n = (jint)val->GetIntValue();
+ env->CallVoidMethod(job, setid, i, n);
+ break;
+ case TYPE_TINY:
+ case TYPE_SHORT:
+ if (gmID(g, setid, "SetShortParm", "(IS)V"))
+ return true;
+
+ s = (jshort)val->GetShortValue();
+ env->CallVoidMethod(job, setid, i, s);
+ break;
+ case TYPE_BIGINT:
+ if (gmID(g, setid, "SetBigintParm", "(IJ)V"))
+ return true;
+
+ lg = (jlong)val->GetBigintValue();
+ env->CallVoidMethod(job, setid, i, lg);
+ break;
+ case TYPE_DOUBLE:
+ case TYPE_DECIM:
+ if (gmID(g, setid, "SetDoubleParm", "(ID)V"))
+ return true;
+
+ d = (jdouble)val->GetFloatValue();
+ env->CallVoidMethod(job, setid, i, d);
+ break;
+ case TYPE_DATE:
+ if ((dat = env->FindClass("java/sql/Timestamp")) == nullptr) {
+ 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 (gmID(g, setid, "SetTimestampParm", "(ILjava/sql/Timestamp;)V"))
+ return true;
+
+ env->CallVoidMethod(job, setid, i, datobj);
+ break;
+ default:
+ sprintf(g->Message, "Parm type %d not supported", val->GetType());
+ return true;
+ } // endswitch Type
+
+ if (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 = nullptr;
+
+ if (gmID(m_G, gdid, "GetDrivers", "([Ljava/lang/String;I)I"))
+ return true;
+
+ // Build the java string array
+ jobjectArray s = env->NewObjectArray(4 * qrp->Maxres,
+ env->FindClass("java/lang/String"), NULL);
+
+ size = env->CallIntMethod(job, gdid, s, qrp->Maxres);
+
+ for (i = 0, n = 0; i < size; i++) {
+ crp = qrp->Colresp;
+ js = (jstring)env->GetObjectArrayElement(s, n++);
+ sval = (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 = nullptr;
+ 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
+
+ if (gmID(g, colid, "ColumnDesc", "(I[I)Ljava/lang/String;"))
+ return NULL;
+
+ // Get max column name length
+ len = GetMaxValue(5);
+ length[0] = (len > 0) ? len + 1 : 128;
+
+ /************************************************************************/
+ /* Allocate the structures used to refer to the result set. */
+ /************************************************************************/
+ if (!(qrp = PlgAllocResult(g, qcol, m_Ncol, IDS_COLUMNS + 3,
+ buftyp, fldtyp, length, false, true)))
+ return NULL;
+
+ // Some columns must be renamed
+ for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
+ switch (++i) {
+ case 3: crp->Name = "Precision"; break;
+ case 4: crp->Name = "Scale"; break;
+ case 5: crp->Name = "Nullable"; break;
+ } // endswitch i
+
+ // Build the java string array
+ jintArray val = env->NewIntArray(4);
+
+ if (val == nullptr) {
+ strcpy(m_G->Message, "Cannot allocate jint array");
+ return NULL;
+ } // endif colid
+
+ /************************************************************************/
+ /* Now get the results into blocks. */
+ /************************************************************************/
+ for (i = 0; i < m_Ncol; i++) {
+ if (!(label = (jstring)env->CallObjectMethod(job, colid, i + 1, val))) {
+ if (Check())
+ sprintf(g->Message, "ColumnDesc: %s", Msg);
+ else
+ strcpy(g->Message, "No result metadata");
+
+ env->ReleaseIntArrayElements(val, n, 0);
+ return NULL;
+ } // endif label
+
+ 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);
+
+ /************************************************************************/
+ /* 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 = nullptr;
+
+ if (qrp->Maxres <= 0)
+ return 0; // 0-sized result"
+
+ SQLQualifiedName name(cap);
+
+ // Build the java string array
+ parms = env->NewObjectArray(4, env->FindClass("java/lang/String"), NULL);
+ env->SetObjectArrayElement(parms, 0, env->NewStringUTF(name.ptr(2)));
+ env->SetObjectArrayElement(parms, 1, env->NewStringUTF(name.ptr(1)));
+ env->SetObjectArrayElement(parms, 2, env->NewStringUTF(name.ptr(0)));
+
+ 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
+
+ if (gmID(g, catid, fnc, "([Ljava/lang/String;)I"))
+ return -1;
+
+ // call method
+ ncol = env->CallIntMethod(job, catid, parms);
+
+ if (Check(ncol)) {
+ sprintf(g->Message, "%s: %s", fnc, Msg);
+ env->DeleteLocalRef(parms);
+ return -1;
+ } // endif Check
+
+ // 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 (Check(rc = Fetch(0))) {
+ sprintf(g->Message, "Fetch: %s", Msg);
+ return -1;
+ } if (rc == 0) {
+ if (trace)
+ htrc("End of fetches i=%d\n", i);
+
+ break;
+ } // endif rc
+
+ for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
+ SetColumnValue(n + 1, nullptr, pval[n]);
+ crp->Kdata->SetValue(pval[n], i);
+ } // endfor n
+
+ } // endfor i
+
+ if (rc > 0)
+ qrp->Truncated = true;
+
+ return i;
+ } // end of GetCatInfo
+
+ /***********************************************************************/
+ /* Allocate a CONNECT result structure from the JDBC result. */
+ /***********************************************************************/
+ PQRYRES JDBConn::AllocateResult(PGLOBAL g)
+ {
+ bool uns;
+ PJDBCCOL colp;
+ PCOLRES *pcrp, crp;
+ PQRYRES qrp;
+
+ if (!m_Rows) {
+ strcpy(g->Message, "Void result");
+ return NULL;
+ } // endif m_Rows
+
+ /*********************************************************************/
+ /* Allocate the result storage for future retrieval. */
+ /*********************************************************************/
+ qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
+ pcrp = &qrp->Colresp;
+ qrp->Continued = FALSE;
+ qrp->Truncated = FALSE;
+ qrp->Info = FALSE;
+ qrp->Suball = TRUE;
+ qrp->BadLines = 0;
+ qrp->Maxsize = m_Rows;
+ qrp->Maxres = m_Rows;
+ qrp->Nbcol = 0;
+ qrp->Nblin = 0;
+ qrp->Cursor = 0;
+
+ for (colp = (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..db8a11716e5
--- /dev/null
+++ b/storage/connect/jdbconn.h
@@ -0,0 +1,193 @@
+/***********************************************************************/
+/* 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 *);
+#if defined(_DEBUG)
+typedef jint(JNICALL *GETDEF) (void *);
+#endif // _DEBUG
+
+// 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;
+#if defined(_DEBUG)
+ GetDefaultJavaVMInitArgs = NULL;
+#endif // _DEBUG
+ } // end of SetJVM
+
+ static void ResetJVM(void);
+ static bool GetJVM(PGLOBAL g);
+
+ // Implementation
+public:
+ //virtual ~JDBConn();
+
+ // JDBC operations
+protected:
+ bool gmID(PGLOBAL g, jmethodID& mid, const char *name, const char *sig);
+ bool Check(jint rc = 0);
+//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;
+#if defined(_DEBUG)
+ static GETDEF GetDefaultJavaVMInitArgs;
+#endif // _DEBUG
+ 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 java wrapper class
+ jobject job; // The java wrapper 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
+ jmethodID errid; // The GetErrmsg method ID
+ jmethodID chrfldid; // The StringField method ID
+ jmethodID intfldid; // The IntField method ID
+ jmethodID dblfldid; // The DoubleField method ID
+ jmethodID fltfldid; // The FloatField method ID
+ jmethodID datfldid; // The TimestampField method ID
+ jmethodID bigfldid; // The BigintField method ID
+ //DWORD m_LoginTimeout;
+//DWORD m_QueryTimeout;
+//DWORD m_UpdateOptions;
+ char *Msg;
+ 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 7b82ba2d627..e94d3817926 100644
--- a/storage/connect/jsonudf.cpp
+++ b/storage/connect/jsonudf.cpp
@@ -1,6 +1,6 @@
/****************** jsonudf C++ Program Source Code File (.CPP) ******************/
-/* PROGRAM NAME: jsonudf Version 1.3 */
-/* (C) Copyright to the author Olivier BERTRAND 2015 */
+/* PROGRAM NAME: jsonudf Version 1.4 */
+/* (C) Copyright to the author Olivier BERTRAND 2015-2016 */
/* This program are the JSON User Defined Functions . */
/*********************************************************************************/
@@ -1433,7 +1433,7 @@ static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n,
char *p = args->args[0];
// Is this a file name?
- if (!strchr("[{ \t\r\n", *p) && (len = GetFileLength(p)))
+ if (p && !strchr("[{ \t\r\n", *p) && (len = GetFileLength(p)))
ml += len * (M + 1);
else
ml += args->lengths[0] * M;
@@ -1805,7 +1805,20 @@ my_bool json_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *messa
} else
CalcLen(args, false, reslen, memlen);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ return false;
+ } else
+ return true;
+
} // end of json_array_add_values_init
char *json_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -1850,7 +1863,7 @@ char *json_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endif str
// Keep result of constant function
- g->Xchk = (initid->const_item) ? str : NULL;
+ g->Xchk = (g->N) ? str : NULL;
} else
str = (char*)g->Xchk;
@@ -1873,7 +1886,7 @@ void json_array_add_values_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool json_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen;
+ unsigned long reslen, memlen;
if (args->arg_count < 2) {
strcpy(message, "This function must have at least 2 arguments");
@@ -1884,7 +1897,20 @@ my_bool json_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, false, reslen, memlen, true);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ return false;
+ } else
+ return true;
+
} // end of json_array_add_init
char *json_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -1930,7 +1956,7 @@ char *json_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!str)
str = MakePSZ(g, args, 0);
- if (initid->const_item)
+ if (g->N)
// Keep result of constant function
g->Xchk = str;
@@ -1966,7 +1992,20 @@ my_bool json_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, false, reslen, memlen, true);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ return false;
+ } else
+ return true;
+
} // end of json_array_delete_init
char *json_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -2008,7 +2047,7 @@ char *json_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!str)
str = MakePSZ(g, args, 0);
- if (initid->const_item)
+ if (g->N)
// Keep result of constant function
g->Xchk = str;
@@ -2184,7 +2223,20 @@ my_bool json_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, true, reslen, memlen, true);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ return false;
+ } else
+ return true;
+
} // end of json_object_add_init
char *json_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -2227,7 +2279,7 @@ char *json_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!str)
str = MakePSZ(g, args, 0);
- if (initid->const_item)
+ if (g->N)
// Keep result of constant function
g->Xchk = str;
@@ -2266,7 +2318,20 @@ my_bool json_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, true, reslen, memlen, true);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ return false;
+ } else
+ return true;
+
} // end of json_object_delete_init
char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -2307,7 +2372,7 @@ char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!str)
str = MakePSZ(g, args, 0);
- if (initid->const_item)
+ if (g->N)
// Keep result of constant function
g->Xchk = str;
@@ -2605,7 +2670,20 @@ my_bool json_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, false, reslen, memlen, true);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ return false;
+ } else
+ return true;
+
} // end of json_item_merge_init
char *json_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -2651,7 +2729,7 @@ char *json_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!str)
str = MakePSZ(g, args, 0);
- if (initid->const_item)
+ if (g->N)
// Keep result of constant function
g->Xchk = str;
@@ -3538,37 +3616,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;
@@ -3580,18 +3630,22 @@ char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
PGLOBAL g = (PGLOBAL)initid->ptr;
PGLOBAL gb = GetMemPtr(g, args, 0);
- if (g->N) {
+ if (g->Alchecked) {
str = (char*)g->Activityp;
goto fin;
- } else if (initid->const_item)
- g->N = 1;
+ } else if (g->N)
+ g->Alchecked = 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) {
@@ -3656,14 +3710,14 @@ char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
if (!(str = MakeResult(g, args, jsp, INT_MAX32)))
str = MakePSZ(g, args, 0);
- if (initid->const_item)
+ if (g->N)
// 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 +3725,58 @@ 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;
+
+ if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ // This is a constant function
+ g->N = (initid->const_item) ? 1 : 0;
+
+ // This is to avoid double execution when using prepared statements
+ if (IsJson(args, 0) > 1)
+ initid->const_item = 0;
+
+ g->Alchecked = 0;
+ return false;
+ } else
+ return true;
+
+} // 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 +3796,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 +3816,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)
@@ -3728,8 +3834,8 @@ my_bool json_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
if (args->arg_count < 1 || args->arg_count > 4) {
strcpy(message, "This function only accepts 1 to 4 arguments");
return true;
- } else if (!args->args[0] || args->arg_type[0] != STRING_RESULT) {
- strcpy(message, "First argument must be a constant string (file name)");
+ } else if (args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a string (file name)");
return true;
} // endif's args[0]
@@ -3747,7 +3853,12 @@ my_bool json_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
initid->maybe_null = 1;
CalcLen(args, false, reslen, memlen);
- fl = GetFileLength(args->args[0]);
+
+ if (args->args[0])
+ fl = GetFileLength(args->args[0]);
+ else
+ fl = 100; // What can be done here?
+
reslen += fl;
if (initid->const_item)
@@ -4006,7 +4117,18 @@ void jbin_array_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jbin_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- return json_array_add_values_init(initid, args, message);
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json string or item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jbin_array_add_values_init
char *jbin_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -4076,7 +4198,18 @@ void jbin_array_add_values_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jbin_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- return json_array_add_init(initid, args, message);
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
} // end of jbin_array_add_init
char *jbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -4146,8 +4279,19 @@ void jbin_array_add_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jbin_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- return json_array_delete_init(initid, args, message);
-} // end of jbin_array_delete_init
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+ } // end of jbin_array_delete_init
char *jbin_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *error)
@@ -4369,8 +4513,19 @@ void jbin_object_key_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jbin_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- return json_object_add_init(initid, args, message);
-} // end of jbin_object_add_init
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, true, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+ } // end of jbin_object_add_init
char *jbin_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *error)
@@ -4435,8 +4590,22 @@ void jbin_object_add_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jbin_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- return json_object_delete_init(initid, args, message);
-} // end of jbin_object_delete_init
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have 2 or 3 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument must be a key string");
+ return true;
+ } else
+ CalcLen(args, true, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+ } // end of jbin_object_delete_init
char *jbin_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *error)
@@ -4645,8 +4814,22 @@ void jbin_get_item_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jbin_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- return json_item_merge_init(initid, args, message);
-} // end of jbin_item_merge_init
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (!IsJson(args, 1)) {
+ strcpy(message, "Second argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+ } // end of jbin_item_merge_init
char *jbin_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *is_null, char *error)
@@ -4706,14 +4889,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 +4910,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)) {
@@ -4792,7 +4974,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;
@@ -4800,6 +4982,44 @@ 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)
+{
+ 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 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)
@@ -4819,7 +5039,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)
@@ -4839,7 +5059,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)
@@ -4964,7 +5184,7 @@ my_bool json_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
if (args->arg_count != 1) {
strcpy(message, "This function must have 1 argument");
return true;
- } else if (IsJson(args, 0) != 3) {
+ } else if (args->args[0] && IsJson(args, 0) != 3) {
strcpy(message, "Argument must be a Jbin tree");
return true;
} else
@@ -4974,21 +5194,27 @@ my_bool json_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} // end of json_serialize_init
char *json_serialize(UDF_INIT *initid, UDF_ARGS *args, char *result,
- unsigned long *res_length, char *, char *)
+ unsigned long *res_length, char *, char *error)
{
char *str;
PGLOBAL g = (PGLOBAL)initid->ptr;
if (!g->Xchk) {
- PBSON bsp = (PBSON)args->args[0];
+ if (IsJson(args, 0) == 3) {
+ PBSON bsp = (PBSON)args->args[0];
- JsonSubSet(g);
+ JsonSubSet(g);
- if (!(str = Serialize(g, bsp->Jsp, NULL, 0)))
- str = strcpy(result, g->Message);
+ if (!(str = Serialize(g, bsp->Jsp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else {
+ *error = 1;
+ str = strcpy(result, "Argument is not a Jbin tree");
+ } // endif
- // Keep result of constant function
- g->Xchk = (initid->const_item) ? str : NULL;
} else
str = (char*)g->Xchk;
@@ -5000,3 +5226,37 @@ 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 {
+ initid->maybe_null = true;
+ return false;
+ } // endif count
+
+} // end of envar_init
+
+char *envar(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *)
+{
+ char *str, name[256];
+ int n = MY_MIN(args->lengths[0], sizeof(name) - 1);
+
+ memcpy(name, args->args[0], n);
+ name[n] = 0;
+
+ if (!(str = getenv(name))) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = strlen(str);
+
+ 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/mycat.cc b/storage/connect/mycat.cc
index 97ad980dd6a..da8be207237 100644
--- a/storage/connect/mycat.cc
+++ b/storage/connect/mycat.cc
@@ -80,6 +80,10 @@
#define NODBC
#include "tabodbc.h"
#endif // ODBC_SUPPORT
+#if defined(JDBC_SUPPORT)
+#define NJDBC
+#include "tabjdbc.h"
+#endif // ODBC_SUPPORT
#if defined(PIVOT_SUPPORT)
#include "tabpivot.h"
#endif // PIVOT_SUPPORT
@@ -140,7 +144,10 @@ TABTYPE GetTypeID(const char *type)
#ifdef ODBC_SUPPORT
: (!stricmp(type, "ODBC")) ? TAB_ODBC
#endif
- : (!stricmp(type, "MYSQL")) ? TAB_MYSQL
+#ifdef JDBC_SUPPORT
+ : (!stricmp(type, "JDBC")) ? TAB_JDBC
+#endif
+ : (!stricmp(type, "MYSQL")) ? TAB_MYSQL
: (!stricmp(type, "MYPRX")) ? TAB_MYSQL
: (!stricmp(type, "DIR")) ? TAB_DIR
#ifdef __WIN__
@@ -301,12 +308,12 @@ int GetIndexType(TABTYPE type)
break;
case TAB_MYSQL:
case TAB_ODBC:
- xtyp= 2;
+ case TAB_JDBC:
+ xtyp= 2;
break;
case TAB_VIR:
xtyp= 3;
break;
-// case TAB_ODBC:
default:
xtyp= 0;
break;
@@ -558,6 +565,9 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am)
#if defined(ODBC_SUPPORT)
case TAB_ODBC: tdp= new(g) ODBCDEF; break;
#endif // ODBC_SUPPORT
+#if defined(JDBC_SUPPORT)
+ case TAB_JDBC: tdp= new(g)JDBCDEF; break;
+#endif // JDBC_SUPPORT
#if defined(__WIN__)
case TAB_MAC: tdp= new(g) MACDEF; break;
case TAB_WMI: tdp= new(g) WMIDEF; break;
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index e9bd64cf8e6..644ca019e4a 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2007-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2007-2016 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -401,8 +401,10 @@ PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db,
MYSQLC::MYSQLC(void)
{
m_DB = NULL;
- m_Stmt = NULL;
- m_Res = NULL;
+#if defined (MYSQL_PREPARED_STATEMENTS)
+ m_Stmt = NULL;
+#endif // MYSQL_PREPARED_STATEMENTS
+ m_Res = NULL;
m_Rows = -1;
m_Row = NULL;
m_Fields = -1;
@@ -444,7 +446,10 @@ int MYSQLC::Open(PGLOBAL g, const char *host, const char *db,
return RC_FX;
} // endif m_DB
- // Removed to do like FEDERATED do
+ if (trace)
+ htrc("MYSQLC Open: m_DB=%.4X size=%d\n", m_DB, (int)sizeof(*m_DB));
+
+ // Removed to do like FEDERATED do
//mysql_options(m_DB, MYSQL_READ_DEFAULT_GROUP, "client-mariadb");
mysql_options(m_DB, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL);
mysql_options(m_DB, MYSQL_OPT_CONNECT_TIMEOUT, &cto);
@@ -701,6 +706,11 @@ int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w)
} else {
m_Fields = mysql_num_fields(m_Res);
m_Rows = (!m_Use) ? (int)mysql_num_rows(m_Res) : 0;
+
+ if (trace)
+ htrc("ExecSQL: m_Res=%.4X size=%d m_Fields=%d m_Rows=%d\n",
+ m_Res, sizeof(*m_Res), m_Fields, m_Rows);
+
} // endif m_Res
} else {
@@ -901,8 +911,12 @@ PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb)
if (fld->flags & NOT_NULL_FLAG)
crp->Nulls = NULL;
else {
- crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
- memset(crp->Nulls, ' ', m_Rows);
+ if (m_Rows) {
+ crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
+ memset(crp->Nulls, ' ', m_Rows);
+ } // endif m_Rows
+
+ crp->Kdata->SetNullable(true);
} // endelse fld->flags
} // endfor fld
@@ -1013,7 +1027,11 @@ int MYSQLC::ExecSQLcmd(PGLOBAL g, const char *query, int *w)
void MYSQLC::Close(void)
{
FreeResult();
- mysql_close(m_DB);
+
+ if (trace)
+ htrc("MYSQLC Close: m_DB=%.4X\n", m_DB);
+
+ mysql_close(m_DB);
m_DB = NULL;
} // end of Close
diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h
index 79f095f5c93..9ebd37527a6 100644
--- a/storage/connect/myconn.h
+++ b/storage/connect/myconn.h
@@ -90,8 +90,10 @@ class DllItem MYSQLC {
// Members
MYSQL *m_DB; // The return from MySQL connection
- MYSQL_STMT *m_Stmt; // Prepared statement handle
- MYSQL_RES *m_Res; // Points to MySQL Result
+#if defined (MYSQL_PREPARED_STATEMENTS)
+ MYSQL_STMT *m_Stmt; // Prepared statement handle
+#endif // MYSQL_PREPARED_STATEMENTS
+ MYSQL_RES *m_Res; // Points to MySQL Result
MYSQL_ROW m_Row; // Point to current row
int m_Rows; // The number of rows of the result
int N;
diff --git a/storage/connect/mysql-test/connect/disabled.def b/storage/connect/mysql-test/connect/disabled.def
new file mode 100644
index 00000000000..9b4570915c7
--- /dev/null
+++ b/storage/connect/mysql-test/connect/disabled.def
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# List the test cases that are to be disabled temporarily.
+#
+# Separate the test case name and the comment with ':'.
+#
+# <testcasename> : BUG#<xxxx> <date disabled> <disabler> <comment>
+#
+# Do not use any TAB characters for whitespace.
+#
+##############################################################################
+#json_udf_bin : broken upstream in --ps (fixed)
+jdbc : Variable settings depend on machine configuration
+jdbc_new : Variable settings depend on machine configuration
+jdbc_oracle : Variable settings depend on machine configuration
+jdbc_postgresql : Variable settings depend on machine configuration
diff --git a/storage/connect/mysql-test/connect/r/jdbc.result b/storage/connect/mysql-test/connect/r/jdbc.result
new file mode 100644
index 00000000000..5e844bc9900
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/jdbc.result
@@ -0,0 +1,269 @@
+CREATE DATABASE connect;
+USE connect;
+CREATE TABLE t2 (
+id bigint not null,
+msg varchar(500),
+tm time,
+dt date,
+dtm datetime,
+ts timestamp);
+INSERT INTO t2 VALUES(455000000000, 'A very big number', '18:10:25', '2016-03-16', '1999-12-11 23:01:52', '2015-07-24 09:32:45');
+SELECT * FROM t2;
+id msg tm dt dtm ts
+455000000000 A very big number 18:10:25 2016-03-16 1999-12-11 23:01:52 2015-07-24 09:32:45
+#
+# Testing JDBC connection to MySQL driver
+#
+USE test;
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME=t2 CONNECTION='jdbc:mysql://localhost:PORT/connect?user=root';
+SELECT * FROM t1;
+id msg tm dt dtm ts
+455000000000 A very big number 18:10:25 2016-03-16 1999-12-11 23:01:52 2015-07-24 09:32:45
+INSERT INTO t1 VALUES(786325481247, 'Hello!', '19:45:03', '1933-08-10', '1985-11-12 09:02:44', '2014-06-17 10:32:01');
+Warnings:
+Note 1105 t2: 1 affected rows
+SELECT * FROM t1;
+id msg tm dt dtm ts
+455000000000 A very big number 18:10:25 2016-03-16 1999-12-11 23:01:52 2015-07-24 09:32:45
+786325481247 Hello! 19:45:03 1933-08-09 1985-11-12 09:02:44 2014-06-17 10:32:01
+DELETE FROM t1 WHERE msg = 'Hello!';
+Warnings:
+Note 1105 t2: 1 affected rows
+SELECT * FROM t1;
+id msg tm dt dtm ts
+455000000000 A very big number 18:10:25 2016-03-16 1999-12-11 23:01:52 2015-07-24 09:32:45
+DROP TABLE t1;
+#
+# Testing JDBC view
+#
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC SRCDEF='select id, msg, tm, dt from t2' CONNECTION='jdbc:mysql://localhost:PORT/connect?user=root';
+SELECT * FROM t1;
+id msg tm dt
+455000000000 A very big number 18:10:25 2016-03-16
+SELECT msg, dt FROM t1;
+msg dt
+A very big number 2016-03-16
+DROP TABLE t1, connect.t2;
+#
+# Testing JDBC write operations
+#
+USE connect;
+CREATE TABLE boys (
+name CHAR(12) NOT NULL,
+city CHAR(11),
+birth DATE DATE_FORMAT='DD/MM/YYYY',
+hired DATE DATE_FORMAT='DD/MM/YYYY' flag=36)
+ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boys.txt' ENDING=1;
+SELECT * FROM boys;
+name city birth hired
+John Boston 1986-01-25 2010-06-02
+Henry Boston 1987-06-07 2008-04-01
+George San Jose 1981-08-10 2010-06-02
+Sam Chicago 1979-11-22 2007-10-10
+James Dallas 1992-05-13 2009-12-14
+Bill Boston 1986-09-11 2008-02-10
+USE test;
+CREATE TABLE t3 (
+name CHAR(12) NOT NULL,
+city CHAR(12),
+birth DATE,
+hired DATE);
+INSERT INTO t3 VALUES('Donald','Atlanta','1999-04-01','2016-03-31'),('Mick','New York','1980-01-20','2002-09-11');
+SELECT * FROM t3;
+name city birth hired
+Donald Atlanta 1999-04-01 2016-03-31
+Mick New York 1980-01-20 2002-09-11
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME=boys CONNECTION='jdbc:mysql://localhost:PORT/connect?user=root' OPTION_LIST='scrollable=1';
+SELECT * FROM t1;
+name city birth hired
+John Boston 1986-01-25 2010-06-02
+Henry Boston 1987-06-07 2008-04-01
+George San Jose 1981-08-10 2010-06-02
+Sam Chicago 1979-11-22 2007-10-10
+James Dallas 1992-05-13 2009-12-14
+Bill Boston 1986-09-11 2008-02-10
+UPDATE t1 SET city = 'Phoenix' WHERE name = 'Henry';
+Warnings:
+Note 1105 boys: 1 affected rows
+INSERT INTO t1 SELECT * FROM t3;
+Warnings:
+Note 1105 boys: 2 affected rows
+INSERT INTO t1 VALUES('Tom','Seatle','2002-03-15',NULL);
+Warnings:
+Note 1105 boys: 1 affected rows
+SELECT * FROM t1;
+name city birth hired
+John Boston 1986-01-25 2010-06-02
+Henry Phoenix 1987-06-07 2008-04-01
+George San Jose 1981-08-10 2010-06-02
+Sam Chicago 1979-11-22 2007-10-10
+James Dallas 1992-05-13 2009-12-14
+Bill Boston 1986-09-11 2008-02-10
+Donald Atlanta 1999-04-01 2016-03-31
+Mick New York 1980-01-20 2002-09-11
+Tom Seatle 2002-03-15 1970-01-01
+DROP TABLE t3;
+#
+# Testing JDBC join operations
+#
+CREATE TABLE t3 (
+name CHAR(9) NOT NULL,
+city CHAR(12) NOT NULL,
+age INT(2))
+engine=CONNECT table_type=FIX file_name='girls.txt';
+SELECT g.name, b.name, g.city FROM t3 g STRAIGHT_JOIN connect.boys b where g.city = b.city;
+name name city
+Mary John Boston
+Susan Sam Chicago
+Betty Sam Chicago
+Mary Bill Boston
+SELECT g.name, b.name, g.city FROM t3 g STRAIGHT_JOIN t1 b where g.city = b.city;
+name name city
+Mary John Boston
+Susan Sam Chicago
+Betty Sam Chicago
+Mary Bill Boston
+DROP TABLE t1, t3, connect.boys;
+#
+# Testing MariaDB JDBC driver
+#
+USE connect;
+CREATE TABLE emp (
+serialno CHAR(5) NOT NULL,
+name VARCHAR(12) NOT NULL FLAG=6,
+sex TINYINT(1) NOT NULL,
+title VARCHAR(15) NOT NULL FLAG=20,
+manager CHAR(5) NOT NULL,
+department CHAR(4) NOT NULL FLAG=41,
+secretary CHAR(5) NOT NULL FLAG=46,
+salary DOUBLE(8,2) NOT NULL FLAG=52)
+ENGINE=connect TABLE_TYPE=fix FILE_NAME='employee.dat' ENDING=1;
+SELECT * FROM emp;
+serialno name sex title manager department secretary salary
+74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00
+02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00
+78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00
+07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00
+45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00
+34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00
+77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00
+74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00
+56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00
+73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00
+22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00
+55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50
+27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00
+98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00
+11111 CHERRY 2 SECRETARY 31416 2452 4500.00
+33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00
+12345 KITTY 2 TYPIST 40567 0319 3000.45
+24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00
+87777 STRONG 1 DIRECTOR 0021 22222 23000.00
+76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00
+70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00
+40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00
+31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00
+36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00
+00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00
+73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00
+00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00
+USE test;
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME=emp CONNECTION='jdbc:mariadb://localhost:PORT/connect?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `serialno` char(5) NOT NULL,
+ `name` varchar(12) NOT NULL,
+ `sex` tinyint(3) NOT NULL,
+ `title` varchar(15) NOT NULL,
+ `manager` char(5) NOT NULL,
+ `department` char(4) NOT NULL,
+ `secretary` char(5) NOT NULL,
+ `salary` double(12,2) NOT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mariadb://localhost:PORT/connect?user=root' `TABLE_TYPE`='JDBC' `TABNAME`='emp'
+SELECT * FROM t1;
+serialno name sex title manager department secretary salary
+74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00
+02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00
+78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00
+07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00
+45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00
+34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00
+77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00
+74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00
+56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00
+73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00
+22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00
+55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50
+27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00
+98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00
+11111 CHERRY 2 SECRETARY 31416 2452 4500.00
+33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00
+12345 KITTY 2 TYPIST 40567 0319 3000.45
+24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00
+87777 STRONG 1 DIRECTOR 0021 22222 23000.00
+76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00
+70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00
+40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00
+31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00
+36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00
+00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00
+73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00
+00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00
+SELECT name, title, salary FROM t1 WHERE sex = 1;
+name title salary
+SMITH ENGINEER 9000.00
+MERCHANT SALESMAN 8700.00
+FUNNIGUY ADMINISTRATOR 8500.00
+BUGHAPPY PROGRAMMER 8500.00
+BIGHEAD SCIENTIST 8000.00
+WALTER ENGINEER 7400.00
+FODDERMAN SALESMAN 7000.00
+TONGHO ENGINEER 6800.00
+GOOSEPEN ADMINISTRATOR 4700.00
+STRONG DIRECTOR 23000.00
+BULLOZER SALESMAN 14800.00
+WERTHER DIRECTOR 14500.00
+QUINN DIRECTOR 14000.00
+ORELLY ENGINEER 13400.00
+BIGHORN SCIENTIST 11000.00
+BROWNY ENGINEER 10500.00
+WHEELFOR SALESMAN 10030.00
+MARTIN ENGINEER 10000.00
+DROP TABLE t1, connect.emp;
+CREATE TABLE t2 (command varchar(128) not null,number int(5) not null flag=1,message varchar(255) flag=2) ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='jdbc:mariadb://localhost:PORT/connect' OPTION_LIST='User=root,Execsrc=1';
+SELECT * FROM t2 WHERE command='drop table tx1';
+command number message
+drop table tx1 0 Execute: java.sql.SQLSyntaxErrorException: Unknown table 'connect.tx1'
+Query is : drop table tx1
+SELECT * FROM t2 WHERE command = 'create table tx1 (a int not null, b char(32), c double(8,2))';
+command number message
+create table tx1 (a int not null, b char(32), c double(8,2)) 0 Affected rows
+SELECT * FROM t2 WHERE command in ('insert into tx1 values(1,''The number one'',456.12)',"insert into tx1(a,b) values(2,'The number two'),(3,'The number three')");
+command number message
+insert into tx1 values(1,'The number one',456.12) 1 Affected rows
+insert into tx1(a,b) values(2,'The number two'),(3,'The number three') 2 Affected rows
+SELECT * FROM t2 WHERE command='update tx1 set c = 3.1416 where a = 2';
+command number message
+update tx1 set c = 3.1416 where a = 2 1 Affected rows
+SELECT * FROM t2 WHERE command='select * from tx1';
+command number message
+select * from tx1 3 Result set column number
+SELECT * FROM t2 WHERE command='delete from tx1 where a = 2';
+command number message
+delete from tx1 where a = 2 1 Affected rows
+SELECT * FROM connect.tx1;
+a b c
+1 The number one 456.12
+3 The number three NULL
+DROP TABLE t2;
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=tables CONNECTION='jdbc:mariadb://localhost:PORT/connect' option_list='User=root,Maxres=50';
+SELECT * FROM t1;
+Table_Cat Table_Schema Table_Name Table_Type Remark
+connect NULL tx1 BASE TABLE
+DROP TABLE t1;
+DROP TABLE connect.tx1;
+DROP DATABASE connect;
+SET GLOBAL connect_jvm_path=NULL;
+SET GLOBAL connect_class_path=NULL;
+SET GLOBAL time_zone = SYSTEM;
diff --git a/storage/connect/mysql-test/connect/r/jdbc_new.result b/storage/connect/mysql-test/connect/r/jdbc_new.result
new file mode 100644
index 00000000000..e5356edd5d8
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/jdbc_new.result
@@ -0,0 +1,216 @@
+CREATE TABLE t1 (a int, b char(10));
+INSERT INTO t1 VALUES (NULL,NULL),(0,'test00'),(1,'test01'),(2,'test02'),(3,'test03');
+SELECT * FROM t1;
+a b
+NULL NULL
+0 test00
+1 test01
+2 test02
+3 test03
+#
+# Testing errors
+#
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=unknown';
+SELECT * FROM t1;
+ERROR HY000: Got error 174 'Connecting: java.sql.SQLException: Access denied for user 'unknown'@'localhost' (using password: NO) rc=-2' from CONNECT
+DROP TABLE t1;
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/unknown?user=root';
+ERROR HY000: Connecting: java.sql.SQLSyntaxErrorException: Unknown database 'unknown' rc=-2
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME='unknown'
+ CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+ERROR HY000: Cannot get columns from unknown
+SHOW CREATE TABLE t1;
+ERROR 42S02: Table 'test.t1' doesn't exist
+CREATE TABLE t1 (x int, y char(10)) ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `x` int(11) DEFAULT NULL,
+ `y` char(10) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`=JDBC
+SELECT * FROM t1;
+ERROR HY000: Got error 174 'ExecuteQuery: java.sql.SQLSyntaxErrorException: Unknown column 'x' in 'field list'
+Query is : SELECT x, y FROM t1' from CONNECT
+DROP TABLE t1;
+CREATE TABLE t1 (a int, b char(10)) ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+ALTER TABLE t1 RENAME t1backup;
+SELECT * FROM t1;
+ERROR HY000: Got error 174 'ExecuteQuery: java.sql.SQLSyntaxErrorException: Table 'test.t1' doesn't exist
+Query is : SELECT a, b FROM t1' from CONNECT
+ALTER TABLE t1backup RENAME t1;
+DROP TABLE t1;
+#
+# Testing SELECT, etc.
+#
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(10) DEFAULT NULL,
+ `b` char(10) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`='JDBC'
+SELECT * FROM t1;
+a b
+0 NULL
+0 test00
+1 test01
+2 test02
+3 test03
+DROP TABLE t1;
+CREATE TABLE t1 (a int, b char(10)) ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME='t1'
+ CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` char(10) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`=JDBC `TABNAME`='t1'
+SELECT * FROM t1;
+a b
+0 NULL
+0 test00
+1 test01
+2 test02
+3 test03
+DROP TABLE t1;
+CREATE TABLE t1 (a INT NOT NULL, b CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) NOT NULL,
+ `b` char(10) NOT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`=JDBC
+SELECT * FROM t1;
+a b
+0
+0 test00
+1 test01
+2 test02
+3 test03
+DROP TABLE t1;
+CREATE TABLE t1 (a char(10), b int) ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(10) DEFAULT NULL,
+ `b` int(11) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`=JDBC
+SELECT * FROM t1;
+a b
+0 NULL
+0 0
+1 0
+2 0
+3 0
+DROP TABLE t1;
+DROP TABLE t1;
+#
+# Testing numeric data types
+#
+CREATE TABLE t1 (a tinyint, b smallint, c mediumint, d int, e bigint, f float, g double, h decimal(20,5));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` tinyint(4) DEFAULT NULL,
+ `b` smallint(6) DEFAULT NULL,
+ `c` mediumint(9) DEFAULT NULL,
+ `d` int(11) DEFAULT NULL,
+ `e` bigint(20) DEFAULT NULL,
+ `f` float DEFAULT NULL,
+ `g` double DEFAULT NULL,
+ `h` decimal(20,5) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+INSERT INTO t1 VALUES(100,3333,41235,1234567890,235000000000,3.14159265,3.14159265,3141.59265);
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` tinyint(3) DEFAULT NULL,
+ `b` smallint(5) DEFAULT NULL,
+ `c` int(7) DEFAULT NULL,
+ `d` int(10) DEFAULT NULL,
+ `e` bigint(19) DEFAULT NULL,
+ `f` double(14,0) DEFAULT NULL,
+ `g` double(24,0) DEFAULT NULL,
+ `h` decimal(27,5) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`='JDBC'
+SELECT * FROM t1;
+a b c d e f g h
+100 3333 41235 1234567890 235000000000 3 3 3141.59265
+DROP TABLE t1;
+DROP TABLE t1;
+#
+# Testing character data types
+#
+CREATE TABLE t1 (a char(12), b varchar(12));
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(12) DEFAULT NULL,
+ `b` varchar(12) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+INSERT INTO t1 VALUES('Welcome','Hello, World');
+SELECT * FROM t1;
+a b
+Welcome Hello, World
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(12) DEFAULT NULL,
+ `b` varchar(12) DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`='JDBC'
+SELECT * FROM t1;
+a b
+Welcome Hello, World
+DROP TABLE t1;
+DROP TABLE t1;
+#
+# Testing temporal data types
+#
+CREATE TABLE t1 (a date, b datetime, c time, d timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, e year);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` date DEFAULT NULL,
+ `b` datetime DEFAULT NULL,
+ `c` time DEFAULT NULL,
+ `d` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `e` year(4) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+INSERT INTO t1 VALUES('2003-05-27 10:45:23','2003-05-27 10:45:23','2003-05-27 10:45:23','2003-05-27 10:45:23','2003-05-27 10:45:23');
+Warnings:
+Note 1265 Data truncated for column 'a' at row 1
+Note 1265 Data truncated for column 'c' at row 1
+Warning 1265 Data truncated for column 'e' at row 1
+SELECT * FROM t1;
+a b c d e
+2003-05-27 2003-05-27 10:45:23 10:45:23 2003-05-27 10:45:23 2003
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root';
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` date DEFAULT NULL,
+ `b` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `c` time DEFAULT NULL,
+ `d` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `e` date DEFAULT NULL
+) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`='JDBC'
+SELECT * FROM t1;
+a b c d e
+2003-05-27 2003-05-27 10:45:23 10:45:23 2003-05-27 10:45:23 1970-01-01
+DROP TABLE t1;
+DROP TABLE t1;
+SET GLOBAL connect_jvm_path=NULL;
+SET GLOBAL connect_class_path=NULL;
+SET GLOBAL time_zone = SYSTEM;
diff --git a/storage/connect/mysql-test/connect/r/jdbc_oracle.result b/storage/connect/mysql-test/connect/r/jdbc_oracle.result
new file mode 100644
index 00000000000..2e36891a037
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/jdbc_oracle.result
@@ -0,0 +1,70 @@
+CREATE TABLE t2 (
+command varchar(128) not null,
+number int(5) not null flag=1,
+message varchar(255) flag=2)
+ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='jdbc:oracle:thin:@localhost:1521:xe'
+OPTION_LIST='User=system,Password=manager,Execsrc=1';
+SELECT * FROM t2 WHERE command = 'drop table employee';
+command number message
+drop table employee 0 Execute: java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
+
+SELECT * FROM t2 WHERE command = 'create table employee (id int not null, name varchar(32), title char(16), salary number(8,2))';
+command number message
+create table employee (id int not null, name varchar(32), title char(16), salary number(8,2)) 0 Affected rows
+SELECT * FROM t2 WHERE command = "insert into employee values(4567,'Johnson', 'Engineer', 12560.50)";
+command number message
+insert into employee values(4567,'Johnson', 'Engineer', 12560.50) 1 Affected rows
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=tables
+CONNECTION='jdbc:oracle:thin:@localhost:1521:xe'
+OPTION_LIST='User=system,Password=manager';
+SELECT * FROM t1 WHERE table_name='employee';
+Table_Cat Table_Schema Table_Name Table_Type Remark
+NULL SYSTEM EMPLOYEE TABLE NULL
+DROP TABLE t1;
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME='EMPLOYEE' CATFUNC=columns
+CONNECTION='jdbc:oracle:thin:@localhost:1521:xe'
+OPTION_LIST='User=system,Password=manager';
+SELECT * FROM t1;
+Table_Cat Table_Schema Table_Name Column_Name Data_Type Type_Name Column_Size Buffer_Length Decimal_Digits Radix Nullable Remarks
+NULL SYSTEM EMPLOYEE ID 3 NUMBER 38 0 0 10 0 NULL
+NULL SYSTEM EMPLOYEE NAME 12 VARCHAR2 32 0 0 10 1 NULL
+NULL SYSTEM EMPLOYEE TITLE 1 CHAR 16 0 0 10 1 NULL
+NULL SYSTEM EMPLOYEE SALARY 3 NUMBER 8 0 2 10 1 NULL
+DROP TABLE t1;
+CREATE SERVER 'oracle' FOREIGN DATA WRAPPER 'oracle.jdbc.driver.OracleDriver' OPTIONS (
+HOST 'jdbc:oracle:thin:@localhost:1521:xe',
+DATABASE 'SYSTEM',
+USER 'system',
+PASSWORD 'manager',
+PORT 0,
+SOCKET '',
+OWNER 'SYSTEM');
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='oracle' tabname='EMPLOYEE';
+SELECT * FROM t1;
+ID NAME TITLE SALARY
+4567 Johnson Engineer 12560.50
+INSERT INTO t1 VALUES(6214, 'Clinton', 'Retired', NULL);
+Warnings:
+Note 1105 EMPLOYEE: 1 affected rows
+UPDATE t1 set name='Trump' WHERE id = 4567;
+Warnings:
+Note 1105 EMPLOYEE: 1 affected rows
+SELECT * FROM t1;
+ID NAME TITLE SALARY
+4567 Trump Engineer 12560.50
+6214 Clinton Retired 0.00
+DELETE FROM t1 WHERE id = 6214;
+Warnings:
+Note 1105 EMPLOYEE: 1 affected rows
+SELECT * FROM t1;
+ID NAME TITLE SALARY
+4567 Trump Engineer 12560.50
+DROP TABLE t1;
+SELECT * FROM t2 WHERE command = 'drop table employee';
+command number message
+drop table employee 0 Affected rows
+DROP TABLE t2;
+DROP SERVER 'oracle';
+SET GLOBAL connect_jvm_path=NULL;
+SET GLOBAL connect_class_path=NULL;
+SET GLOBAL time_zone = SYSTEM;
diff --git a/storage/connect/mysql-test/connect/r/jdbc_postgresql.result b/storage/connect/mysql-test/connect/r/jdbc_postgresql.result
new file mode 100644
index 00000000000..6d77d79d5d3
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/jdbc_postgresql.result
@@ -0,0 +1,65 @@
+CREATE TABLE t2 (
+command varchar(128) not null,
+number int(5) not null flag=1,
+message varchar(255) flag=2)
+ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='jdbc:postgresql://localhost/mtr'
+OPTION_LIST='User=mtr,Password=mtr,Schema=public,Execsrc=1';
+SELECT * FROM t2 WHERE command='drop table employee';
+command number message
+drop table employee 0 Execute: org.postgresql.util.PSQLException: ERREUR: la table « employee » n'existe pas
+SELECT * FROM t2 WHERE command = 'create table employee (id int not null, name varchar(32), title char(16), salary decimal(8,2))';
+command number message
+create table employee (id int not null, name varchar(32), title char(16), salary decimal(8,2)) 0 Affected rows
+SELECT * FROM t2 WHERE command = "insert into employee values(4567,'Johnson', 'Engineer', 12560.50)";
+command number message
+insert into employee values(4567,'Johnson', 'Engineer', 12560.50) 1 Affected rows
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=tables
+CONNECTION='jdbc:postgresql://localhost/mtr'
+OPTION_LIST='User=mtr,Password=mtr,Schema=public,Tabtype=TABLE,Maxres=10';
+SELECT * FROM t1;
+Table_Cat Table_Schema Table_Name Table_Type Remark
+ public employee TABLE NULL
+ public t1 TABLE NULL
+ public t2 TABLE NULL
+DROP TABLE t1;
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=columns
+CONNECTION='jdbc:postgresql://localhost/mtr' tabname=employee
+OPTION_LIST='User=mtr,Password=mtr,Maxres=10';
+SELECT * FROM t1;
+Table_Cat Table_Schema Table_Name Column_Name Data_Type Type_Name Column_Size Buffer_Length Decimal_Digits Radix Nullable Remarks
+NULL public employee id 4 int4 10 0 0 10 0 NULL
+NULL public employee name 12 varchar 32 0 0 10 1 NULL
+NULL public employee title 1 bpchar 16 0 0 10 1 NULL
+NULL public employee salary 2 numeric 8 0 2 10 1 NULL
+DROP TABLE t1;
+CREATE SERVER 'postgresql' FOREIGN DATA WRAPPER 'postgresql' OPTIONS (
+HOST 'localhost',
+DATABASE 'mtr',
+USER 'mtr',
+PASSWORD 'mtr',
+PORT 0,
+SOCKET '',
+OWNER 'root');
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='postgresql/public.employee';
+SELECT * FROM t1;
+id name title salary
+4567 Johnson Engineer 12560.50
+INSERT INTO t1 VALUES(3126,'Smith', 'Clerk', 5230.00);
+Warnings:
+Note 1105 public.employee: 1 affected rows
+UPDATE t1 SET salary = salary + 100.00;
+Warnings:
+Note 1105 public.employee: 2 affected rows
+SELECT * FROM t1;
+id name title salary
+4567 Johnson Engineer 12660.50
+3126 Smith Clerk 5330.00
+DROP TABLE t1;
+DROP SERVER 'postgresql';
+SELECT * FROM t2 WHERE command='drop table employee';
+command number message
+drop table employee 0 Affected rows
+DROP TABLE t2;
+SET GLOBAL connect_jvm_path=NULL;
+SET GLOBAL connect_class_path=NULL;
+SET GLOBAL time_zone = SYSTEM;
diff --git a/storage/connect/mysql-test/connect/std_data/girls.txt b/storage/connect/mysql-test/connect/std_data/girls.txt
new file mode 100644
index 00000000000..12ce8babbaf
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/girls.txt
@@ -0,0 +1,5 @@
+Mary Boston 25
+Nancy Palo Alto 23
+Susan Chicago 18
+Betty Chicago 32
+Anne Denver 23
diff --git a/storage/connect/mysql-test/connect/t/jdbc.test b/storage/connect/mysql-test/connect/t/jdbc.test
new file mode 100644
index 00000000000..9389747ad9c
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/jdbc.test
@@ -0,0 +1,143 @@
+-- source jdbconn.inc
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+--copy_file $MTR_SUITE_DIR/std_data/girls.txt $MYSQLD_DATADIR/test/girls.txt
+
+let $PORT= `select @@port`;
+
+#
+# This test is run against a local MariaDB server
+#
+CREATE DATABASE connect;
+USE connect;
+CREATE TABLE t2 (
+ id bigint not null,
+ msg varchar(500),
+ tm time,
+ dt date,
+ dtm datetime,
+ ts timestamp);
+INSERT INTO t2 VALUES(455000000000, 'A very big number', '18:10:25', '2016-03-16', '1999-12-11 23:01:52', '2015-07-24 09:32:45');
+SELECT * FROM t2;
+
+--echo #
+--echo # Testing JDBC connection to MySQL driver
+--echo #
+USE test;
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME=t2 CONNECTION='jdbc:mysql://localhost:$PORT/connect?user=root'
+SELECT * FROM t1;
+INSERT INTO t1 VALUES(786325481247, 'Hello!', '19:45:03', '1933-08-10', '1985-11-12 09:02:44', '2014-06-17 10:32:01');
+SELECT * FROM t1;
+DELETE FROM t1 WHERE msg = 'Hello!';
+SELECT * FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing JDBC view
+--echo #
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC SRCDEF='select id, msg, tm, dt from t2' CONNECTION='jdbc:mysql://localhost:$PORT/connect?user=root'
+SELECT * FROM t1;
+SELECT msg, dt FROM t1;
+DROP TABLE t1, connect.t2;
+
+--echo #
+--echo # Testing JDBC write operations
+--echo #
+USE connect;
+--copy_file $MTR_SUITE_DIR/std_data/boys.txt $MYSQLD_DATADIR/connect/boys.txt
+CREATE TABLE boys (
+ name CHAR(12) NOT NULL,
+ city CHAR(11),
+ birth DATE DATE_FORMAT='DD/MM/YYYY',
+ hired DATE DATE_FORMAT='DD/MM/YYYY' flag=36)
+ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boys.txt' ENDING=1;
+SELECT * FROM boys;
+
+USE test;
+CREATE TABLE t3 (
+ name CHAR(12) NOT NULL,
+ city CHAR(12),
+ birth DATE,
+ hired DATE);
+INSERT INTO t3 VALUES('Donald','Atlanta','1999-04-01','2016-03-31'),('Mick','New York','1980-01-20','2002-09-11');
+SELECT * FROM t3;
+
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME=boys CONNECTION='jdbc:mysql://localhost:$PORT/connect?user=root' OPTION_LIST='scrollable=1'
+SELECT * FROM t1;
+UPDATE t1 SET city = 'Phoenix' WHERE name = 'Henry';
+INSERT INTO t1 SELECT * FROM t3;
+INSERT INTO t1 VALUES('Tom','Seatle','2002-03-15',NULL);
+SELECT * FROM t1;
+DROP TABLE t3;
+
+--echo #
+--echo # Testing JDBC join operations
+--echo #
+CREATE TABLE t3 (
+ name CHAR(9) NOT NULL,
+ city CHAR(12) NOT NULL,
+ age INT(2))
+engine=CONNECT table_type=FIX file_name='girls.txt';
+SELECT g.name, b.name, g.city FROM t3 g STRAIGHT_JOIN connect.boys b where g.city = b.city;
+SELECT g.name, b.name, g.city FROM t3 g STRAIGHT_JOIN t1 b where g.city = b.city;
+DROP TABLE t1, t3, connect.boys;
+
+--echo #
+--echo # Testing MariaDB JDBC driver
+--echo #
+USE connect;
+--copy_file $MTR_SUITE_DIR/std_data/employee.dat $MYSQLD_DATADIR/connect/employee.dat
+CREATE TABLE emp (
+ serialno CHAR(5) NOT NULL,
+ name VARCHAR(12) NOT NULL FLAG=6,
+ sex TINYINT(1) NOT NULL,
+ title VARCHAR(15) NOT NULL FLAG=20,
+ manager CHAR(5) NOT NULL,
+ department CHAR(4) NOT NULL FLAG=41,
+ secretary CHAR(5) NOT NULL FLAG=46,
+ salary DOUBLE(8,2) NOT NULL FLAG=52)
+ENGINE=connect TABLE_TYPE=fix FILE_NAME='employee.dat' ENDING=1;
+SELECT * FROM emp;
+
+USE test;
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME=emp CONNECTION='jdbc:mariadb://localhost:$PORT/connect?user=root'
+--replace_result $PORT PORT
+--eval SHOW CREATE TABLE t1
+SELECT * FROM t1;
+SELECT name, title, salary FROM t1 WHERE sex = 1;
+
+DROP TABLE t1, connect.emp;
+
+#
+# Testing remote command execution
+#
+--replace_result $PORT PORT
+--eval CREATE TABLE t2 (command varchar(128) not null,number int(5) not null flag=1,message varchar(255) flag=2) ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='jdbc:mariadb://localhost:$PORT/connect' OPTION_LIST='User=root,Execsrc=1'
+SELECT * FROM t2 WHERE command='drop table tx1';
+SELECT * FROM t2 WHERE command = 'create table tx1 (a int not null, b char(32), c double(8,2))';
+SELECT * FROM t2 WHERE command in ('insert into tx1 values(1,''The number one'',456.12)',"insert into tx1(a,b) values(2,'The number two'),(3,'The number three')");
+SELECT * FROM t2 WHERE command='update tx1 set c = 3.1416 where a = 2';
+SELECT * FROM t2 WHERE command='select * from tx1';
+SELECT * FROM t2 WHERE command='delete from tx1 where a = 2';
+SELECT * FROM connect.tx1;
+DROP TABLE t2;
+
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=tables CONNECTION='jdbc:mariadb://localhost:$PORT/connect' option_list='User=root,Maxres=50'
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP TABLE connect.tx1;
+
+#
+# Clean up
+#
+--remove_file $MYSQLD_DATADIR/connect/boys.txt
+--remove_file $MYSQLD_DATADIR/connect/employee.dat
+DROP DATABASE connect;
+--remove_file $MYSQLD_DATADIR/test/girls.txt
+
+-- source jdbconn_cleanup.inc
diff --git a/storage/connect/mysql-test/connect/t/jdbc_new.test b/storage/connect/mysql-test/connect/t/jdbc_new.test
new file mode 100644
index 00000000000..33ec1b343cc
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/jdbc_new.test
@@ -0,0 +1,179 @@
+#
+# This test is run against a remote MySQL server
+#
+connect (master,127.0.0.1,root,,test,$MASTER_MYPORT,);
+connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT,);
+connection master;
+
+-- source jdbconn.inc
+
+connection slave;
+CREATE TABLE t1 (a int, b char(10));
+INSERT INTO t1 VALUES (NULL,NULL),(0,'test00'),(1,'test01'),(2,'test02'),(3,'test03');
+SELECT * FROM t1;
+
+--echo #
+--echo # Testing errors
+--echo #
+connection master;
+
+# Bad user name
+# Suppress "mysql_real_connect failed:" (printed in _DEBUG build)
+--replace_result $SLAVE_MYPORT SLAVE_PORT "mysql_real_connect failed: " ""
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=unknown';
+--error ER_GET_ERRMSG
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# Bad database name
+--replace_result $SLAVE_MYPORT SLAVE_PORT "mysql_real_connect failed: " ""
+--error ER_UNKNOWN_ERROR
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/unknown?user=root';
+
+# Bad table name
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+--error ER_UNKNOWN_ERROR
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME='unknown'
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--error ER_NO_SUCH_TABLE
+SHOW CREATE TABLE t1;
+
+# Bad column name
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 (x int, y char(10)) ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+--error ER_GET_ERRMSG
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# The remote table disappeared
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 (a int, b char(10)) ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+
+connection slave;
+ALTER TABLE t1 RENAME t1backup;
+
+connection master;
+--error ER_GET_ERRMSG
+SELECT * FROM t1;
+
+connection slave;
+ALTER TABLE t1backup RENAME t1;
+
+connection master;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing SELECT, etc.
+--echo #
+
+# Automatic table structure
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# Explicit table structure
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 (a int, b char(10)) ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME='t1'
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# Explicit table structure: remote NULL, local NOT NULL
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 (a INT NOT NULL, b CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# Explicit table structure with wrong column types
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 (a char(10), b int) ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+connection slave;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing numeric data types
+--echo #
+
+# TODO: mediumint is converted to int, float is converted to double, decimal is converted to double
+CREATE TABLE t1 (a tinyint, b smallint, c mediumint, d int, e bigint, f float, g double, h decimal(20,5));
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES(100,3333,41235,1234567890,235000000000,3.14159265,3.14159265,3141.59265);
+
+connection master;
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+connection slave;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing character data types
+--echo #
+
+CREATE TABLE t1 (a char(12), b varchar(12));
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES('Welcome','Hello, World');
+SELECT * FROM t1;
+
+connection master;
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+connection slave;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing temporal data types
+--echo #
+
+CREATE TABLE t1 (a date, b datetime, c time, d timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, e year);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES('2003-05-27 10:45:23','2003-05-27 10:45:23','2003-05-27 10:45:23','2003-05-27 10:45:23','2003-05-27 10:45:23');
+SELECT * FROM t1;
+
+connection master;
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC
+ CONNECTION='jdbc:mysql://127.0.0.1:$SLAVE_MYPORT/test?user=root';
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+connection slave;
+DROP TABLE t1;
+
+connection master;
+-- source jdbconn_cleanup.inc
+
diff --git a/storage/connect/mysql-test/connect/t/jdbc_oracle.test b/storage/connect/mysql-test/connect/t/jdbc_oracle.test
new file mode 100644
index 00000000000..10cb7a7b77d
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/jdbc_oracle.test
@@ -0,0 +1,56 @@
+-- source jdbconn.inc
+
+#
+# This test is run against Oracle driver
+#
+CREATE TABLE t2 (
+ command varchar(128) not null,
+ number int(5) not null flag=1,
+ message varchar(255) flag=2)
+ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='jdbc:oracle:thin:@localhost:1521:xe'
+OPTION_LIST='User=system,Password=manager,Execsrc=1';
+SELECT * FROM t2 WHERE command = 'drop table employee';
+SELECT * FROM t2 WHERE command = 'create table employee (id int not null, name varchar(32), title char(16), salary number(8,2))';
+SELECT * FROM t2 WHERE command = "insert into employee values(4567,'Johnson', 'Engineer', 12560.50)";
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=tables
+CONNECTION='jdbc:oracle:thin:@localhost:1521:xe'
+OPTION_LIST='User=system,Password=manager';
+SELECT * FROM t1 WHERE table_name='employee';
+DROP TABLE t1;
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC TABNAME='EMPLOYEE' CATFUNC=columns
+CONNECTION='jdbc:oracle:thin:@localhost:1521:xe'
+OPTION_LIST='User=system,Password=manager';
+SELECT * FROM t1;
+DROP TABLE t1;
+
+#
+# Test connecting via a Federated server
+#
+CREATE SERVER 'oracle' FOREIGN DATA WRAPPER 'oracle.jdbc.driver.OracleDriver' OPTIONS (
+HOST 'jdbc:oracle:thin:@localhost:1521:xe',
+DATABASE 'SYSTEM',
+USER 'system',
+PASSWORD 'manager',
+PORT 0,
+SOCKET '',
+OWNER 'SYSTEM');
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='oracle' tabname='EMPLOYEE';
+SELECT * FROM t1;
+INSERT INTO t1 VALUES(6214, 'Clinton', 'Retired', NULL);
+UPDATE t1 set name='Trump' WHERE id = 4567;
+SELECT * FROM t1;
+DELETE FROM t1 WHERE id = 6214;
+SELECT * FROM t1;
+DROP TABLE t1;
+SELECT * FROM t2 WHERE command = 'drop table employee';
+DROP TABLE t2;
+DROP SERVER 'oracle';
+
+#
+# Clean up
+#
+
+-- source jdbconn_cleanup.inc
diff --git a/storage/connect/mysql-test/connect/t/jdbc_postgresql.test b/storage/connect/mysql-test/connect/t/jdbc_postgresql.test
new file mode 100644
index 00000000000..1041ef468d7
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/jdbc_postgresql.test
@@ -0,0 +1,53 @@
+-- source jdbconn.inc
+
+#
+# This test is run against Postgresql driver
+#
+CREATE TABLE t2 (
+ command varchar(128) not null,
+ number int(5) not null flag=1,
+ message varchar(255) flag=2)
+ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='jdbc:postgresql://localhost/mtr'
+OPTION_LIST='User=mtr,Password=mtr,Schema=public,Execsrc=1';
+SELECT * FROM t2 WHERE command='drop table employee';
+SELECT * FROM t2 WHERE command = 'create table employee (id int not null, name varchar(32), title char(16), salary decimal(8,2))';
+SELECT * FROM t2 WHERE command = "insert into employee values(4567,'Johnson', 'Engineer', 12560.50)";
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=tables
+CONNECTION='jdbc:postgresql://localhost/mtr'
+OPTION_LIST='User=mtr,Password=mtr,Schema=public,Tabtype=TABLE,Maxres=10';
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=columns
+CONNECTION='jdbc:postgresql://localhost/mtr' tabname=employee
+OPTION_LIST='User=mtr,Password=mtr,Maxres=10';
+SELECT * FROM t1;
+DROP TABLE t1;
+
+#
+# Test connecting via a Federated server
+#
+CREATE SERVER 'postgresql' FOREIGN DATA WRAPPER 'postgresql' OPTIONS (
+HOST 'localhost',
+DATABASE 'mtr',
+USER 'mtr',
+PASSWORD 'mtr',
+PORT 0,
+SOCKET '',
+OWNER 'root');
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CONNECTION='postgresql/public.employee';
+SELECT * FROM t1;
+INSERT INTO t1 VALUES(3126,'Smith', 'Clerk', 5230.00);
+UPDATE t1 SET salary = salary + 100.00;
+SELECT * FROM t1;
+DROP TABLE t1;
+DROP SERVER 'postgresql';
+SELECT * FROM t2 WHERE command='drop table employee';
+DROP TABLE t2;
+
+#
+# Clean up
+#
+-- source jdbconn_cleanup.inc
diff --git a/storage/connect/mysql-test/connect/t/jdbconn.inc b/storage/connect/mysql-test/connect/t/jdbconn.inc
new file mode 100644
index 00000000000..0bac0b35fc4
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/jdbconn.inc
@@ -0,0 +1,31 @@
+--source include/not_embedded.inc
+
+--disable_query_log
+--error 0,ER_UNKNOWN_ERROR
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=JDBC CATFUNC=drivers;
+if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
+ AND ENGINE='CONNECT'
+ AND (CREATE_OPTIONS LIKE "%`table_type`='JDBC'%" OR CREATE_OPTIONS LIKE '%`table_type`=JDBC%')`)
+{
+ Skip Need Java support;
+}
+DROP TABLE t1;
+
+# This is specific and explains why this test is disabled.
+# You should edit this file to reflect what is the required files location on your machine.
+# This is the path to the JVM library (dll or so)
+SET GLOBAL connect_jvm_path='C:\\Program Files\\Java\\jdk1.8.0_77\\jre\\bin\\client';
+
+# The complete class path send when creating the Java Virtual Machine is, in that order:
+# 1 - The current directory.
+# 2 - The paths of the connect_class_path global variable.
+# 3 - The paths of the CLASSPATH environment variable.
+# These are the paths to the needed classes or jar files. The Apache ones are only for the JdbcApacheInterface wrapper.
+SET GLOBAL connect_class_path='E:\\MariaDB-10.1\\Connect\\storage\\connect;E:\\MariaDB-10.1\\Connect\\sql\\data\\postgresql-9.4.1208.jar;E:\\Oracle\\ojdbc6.jar;E:\\Apache\\commons-dbcp2-2.1.1\\commons-dbcp2-2.1.1.jar;E:\\Apache\\commons-pool2-2.4.2\\commons-pool2-2.4.2.jar;E:\\Apache\\commons-logging-1.2\\commons-logging-1.2.jar';
+
+# On my machine, paths to the JDK classes and to the MySQL and MariaDB drivers are defined in the CLASSPATH environment variable
+#CREATE FUNCTION envar RETURNS STRING SONAME 'ha_connect.dll';
+#SELECT envar('CLASSPATH');
+
+--enable_query_log
diff --git a/storage/connect/mysql-test/connect/t/jdbconn_cleanup.inc b/storage/connect/mysql-test/connect/t/jdbconn_cleanup.inc
new file mode 100644
index 00000000000..48e321495ad
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/jdbconn_cleanup.inc
@@ -0,0 +1,6 @@
+--disable_warnings
+#DROP FUNCTION envar;
+SET GLOBAL connect_jvm_path=NULL;
+SET GLOBAL connect_class_path=NULL;
+SET GLOBAL time_zone = SYSTEM;
+--enable_warnings
diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp
index 55ccbdbada1..8b2626fe962 100644
--- a/storage/connect/odbconn.cpp
+++ b/storage/connect/odbconn.cpp
@@ -1458,7 +1458,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
n++;
// n can be 0 for query such as Select count(*) from table
- if (n && n != (UWORD)ncol)
+ if (n && n > (UWORD)ncol)
ThrowDBX(MSG(COL_NUM_MISM));
// Now bind the column buffers
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index b57d9e20ceb..910ce97f48a 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -77,7 +77,8 @@ enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */
TAB_JSON = 23, /* JSON tables */
TAB_JCT = 24, /* Junction tables NIY */
TAB_DMY = 25, /* DMY Dummy tables NIY */
- TAB_NIY = 26}; /* Table not implemented yet */
+ TAB_JDBC = 26, /* Table accessed via JDBC */
+ TAB_NIY = 27}; /* Table not implemented yet */
enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
TYPE_AM_ROWID = 1, /* ROWID type (special column) */
@@ -109,7 +110,9 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
TYPE_AM_DIR = 90, /* DIR access method type no */
TYPE_AM_ODBC = 100, /* ODBC access method type no */
TYPE_AM_XDBC = 101, /* XDBC access method type no */
- TYPE_AM_OEM = 110, /* OEM access method type no */
+ TYPE_AM_JDBC = 102, /* JDBC access method type no */
+ TYPE_AM_XJDC = 103, /* XJDC access method type no */
+ TYPE_AM_OEM = 110, /* OEM access method type no */
TYPE_AM_TBL = 115, /* TBL access method type no */
TYPE_AM_PIVOT = 120, /* PIVOT access method type no */
TYPE_AM_SRC = 121, /* PIVOT multiple column type no */
@@ -146,8 +149,9 @@ enum RECFM {RECFM_NAF = -2, /* Not a file */
RECFM_BIN = 2, /* Binary DOS files (also fixed) */
RECFM_VCT = 3, /* VCT formatted files */
RECFM_ODBC = 4, /* Table accessed via ODBC */
- RECFM_PLG = 5, /* Table accessed via PLGconn */
- RECFM_DBF = 6}; /* DBase formatted file */
+ RECFM_JDBC = 5, /* Table accessed via JDBC */
+ RECFM_PLG = 6, /* Table accessed via PLGconn */
+ RECFM_DBF = 7}; /* DBase formatted file */
enum MISC {DB_TABNO = 1, /* DB routines in Utility Table */
MAX_MULT_KEY = 10, /* Max multiple key number */
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/plugutil.c b/storage/connect/plugutil.c
index 38e28a171b2..2551b603349 100644
--- a/storage/connect/plugutil.c
+++ b/storage/connect/plugutil.c
@@ -516,7 +516,9 @@ void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
if (trace)
htrc("PlugSubAlloc: %s\n", g->Message);
- longjmp(g->jumper[g->jump_level], 1);
+ /* Nothing we can do if longjmp is not initialized. */
+ assert(g->jump_level >= 0);
+ longjmp(g->jumper[g->jump_level], 1);
} /* endif size OS32 code */
/*********************************************************************/
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/tabcol.cpp b/storage/connect/tabcol.cpp
index 662c0b514cf..fde1baa6317 100644
--- a/storage/connect/tabcol.cpp
+++ b/storage/connect/tabcol.cpp
@@ -50,7 +50,7 @@ XTAB::XTAB(PTABLE tp) : Name(tp->Name)
Qualifier = tp->Qualifier;
if (trace)
- htrc(" making copy TABLE %s %s\n", Name, Srcdef);
+ htrc(" making copy TABLE %s %s\n", Name, SVP(Srcdef));
} // end of XTAB constructor
diff --git a/storage/connect/tabjdbc.cpp b/storage/connect/tabjdbc.cpp
new file mode 100644
index 00000000000..f507e3df3ea
--- /dev/null
+++ b/storage/connect/tabjdbc.cpp
@@ -0,0 +1,1810 @@
+/************* TabJDBC C++ Program Source Code File (.CPP) *************/
+/* PROGRAM NAME: TABJDBC */
+/* ------------- */
+/* Version 1.1 */
+/* */
+/* 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. */
+/***********************************************************************/
+#define MYSQL_SERVER 1
+#include "my_global.h"
+#include "sql_class.h"
+#include "sql_servers.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 "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
+
+/***********************************************************************/
+/* Called on table construction. */
+/***********************************************************************/
+bool JDBCDEF::SetParms(PJPARM sjp)
+{
+ sjp->Url= Url;
+ sjp->User= Username;
+ sjp->Pwd= Password;
+ return true;
+} // end of SetParms
+
+/***********************************************************************/
+/* Parse connection string */
+/* */
+/* SYNOPSIS */
+/* ParseURL() */
+/* Url The connection string to parse */
+/* */
+/* DESCRIPTION */
+/* This is used to set the Url in case a wrapper server as been */
+/* specified. This is rather experimental yet. */
+/* */
+/* RETURN VALUE */
+/* RC_OK Url was a true URL */
+/* RC_NF Url was a server name/table */
+/* RC_FX Error */
+/* */
+/***********************************************************************/
+int JDBCDEF::ParseURL(PGLOBAL g, char *url, bool b)
+{
+ if (strncmp(url, "jdbc:", 5)) {
+ // No "jdbc:" in connection string. Must be a straight
+ // "server" or "server/table"
+ // ok, so we do a little parsing, but not completely!
+ if ((Tabname= strchr(url, '/'))) {
+ // If there is a single '/' in the connection string,
+ // this means the user is specifying a table name
+ *Tabname++= '\0';
+
+ // there better not be any more '/'s !
+ if (strchr(Tabname, '/'))
+ return RC_FX;
+
+ } else if (b) {
+ // Otherwise, straight server name,
+ Tabname = GetStringCatInfo(g, "Name", NULL);
+ Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+ } // endelse
+
+ if (trace)
+ htrc("server: %s Tabname: %s", url, Tabname);
+
+ // Now make the required URL
+ FOREIGN_SERVER *server, server_buffer;
+
+ // get_server_by_name() clones the server if exists
+ if (!(server= get_server_by_name(current_thd->mem_root, url, &server_buffer))) {
+ sprintf(g->Message, "Server %s does not exist!", url);
+ return RC_FX;
+ } // endif server
+
+ if (strncmp(server->host, "jdbc:", 5)) {
+ // Now make the required URL
+ Url = (PSZ)PlugSubAlloc(g, NULL, 0);
+ strcat(strcpy(Url, "jdbc:"), server->scheme);
+ strcat(strcat(Url, "://"), server->host);
+
+ if (server->port) {
+ char buf[16];
+
+ sprintf(buf, "%ld", server->port);
+ strcat(strcat(Url, ":"), buf);
+ } // endif port
+
+ if (server->db)
+ strcat(strcat(Url, "/"), server->db);
+
+ PlugSubAlloc(g, NULL, strlen(Url) + 1);
+ } else // host is a URL
+ Url = PlugDup(g, server->host);
+
+ if (server->username)
+ Username = PlugDup(g, server->username);
+
+ if (server->password)
+ Password = PlugDup(g, server->password);
+
+ return RC_NF;
+ } // endif
+
+ // Url was a JDBC URL, nothing to do
+ return RC_OK;
+} // end of ParseURL
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values from JDBC file. */
+/***********************************************************************/
+bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+{
+ int rc = RC_OK;
+
+ 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
+
+ if (Url)
+ rc = ParseURL(g, Url);
+
+ if (rc == RC_FX) // Error
+ return true;
+ else if (rc == RC_OK) { // Url was not a server name
+ Tabname = GetStringCatInfo(g, "Name",
+ (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
+ Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+ Username = GetStringCatInfo(g, "User", NULL);
+ Password = GetStringCatInfo(g, "Password", NULL);
+ } // endif rc
+
+ if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+ Read_Only = true;
+
+ Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
+ Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
+ Tabschema = GetStringCatInfo(g, "Dbname", NULL);
+ Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
+ Tabtype = GetStringCatInfo(g, "Tabtype", NULL);
+ 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);
+//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;
+ Prepared = 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..537276a6a7f
--- /dev/null
+++ b/storage/connect/tabjdbc.h
@@ -0,0 +1,346 @@
+/*************** 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"
+#include "jdbccat.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);
+ int ParseURL(PGLOBAL g, char *url, bool b = true);
+ bool SetParms(PJPARM sjp);
+
+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
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index c555f2a5abb..5fd0534210d 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -97,8 +97,8 @@ ODBCDEF::ODBCDEF(void)
{
Connect = Tabname = Tabschema = Username = Password = NULL;
Tabcat = Srcdef = Qchar = Qrystr = Sep = NULL;
- Catver = Options = Cto = Qto = Quoted = Maxerr = Maxres = 0;
- Scrollable = Memory = Xsrc = UseCnc = false;
+ Catver = Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0;
+ Scrollable = Xsrc = UseCnc = false;
} // end of ODBCDEF constructor
/***********************************************************************/
@@ -1009,7 +1009,7 @@ bool TDBODBC::SetRecpos(PGLOBAL g, int recpos)
} // end of SetRecpos
/***********************************************************************/
-/* Data Base indexed read routine for MYSQL access method. */
+/* Data Base indexed read routine for ODBC access method. */
/***********************************************************************/
bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
{
@@ -1028,7 +1028,7 @@ bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
return false;
} else {
- if (To_Def->GetHandler()->MakeKeyWhere(g, Query, op, c, kr))
+ if (hc->MakeKeyWhere(g, Query, op, c, kr))
return true;
if (To_CondFil) {
diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp
index 36849146746..e3baf7c3da5 100644
--- a/storage/connect/tabtbl.cpp
+++ b/storage/connect/tabtbl.cpp
@@ -569,6 +569,9 @@ pthread_handler_t ThreadOpen(void *p)
if (!my_thread_init()) {
set_current_thd(cmp->Thd);
+ if (trace)
+ htrc("ThreadOpen: Thd=%d\n", cmp->Thd);
+
// Try to open the connection
if (!cmp->Tap->GetTo_Tdb()->OpenDB(cmp->G)) {
cmp->Ready = true;
@@ -604,9 +607,14 @@ void TDBTBM::ResetDB(void)
if (colp->GetAmType() == TYPE_AM_TABID)
colp->COLBLK::Reset();
+ // Local tables
for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext())
((PTDBASE)tabp->GetTo_Tdb())->ResetDB();
+ // Remote tables
+ for (PTBMT tp = Tmp; tp; tp = tp->Next)
+ ((PTDBASE)tp->Tap->GetTo_Tdb())->ResetDB();
+
Tdbp = (Tablist) ? (PTDBASE)Tablist->GetTo_Tdb() : NULL;
Crp = 0;
} // end of ResetDB