diff options
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 Binary files differnew file mode 100644 index 00000000000..acd4258e3d3 --- /dev/null +++ b/storage/connect/JdbcApacheInterface.class 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 Binary files differnew file mode 100644 index 00000000000..d56c04bd81f --- /dev/null +++ b/storage/connect/JdbcDSInterface.class 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 Binary files differnew file mode 100644 index 00000000000..8c5ba6439f3 --- /dev/null +++ b/storage/connect/JdbcInterface.class diff --git a/storage/connect/JdbcInterface.java b/storage/connect/JdbcInterface.java new file mode 100644 index 00000000000..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 |