activerecord-jdbc-adapter-ficoh 1.3.21-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +462 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +36 -0
  6. data/CONTRIBUTING.md +49 -0
  7. data/Gemfile +68 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +277 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +298 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +63 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +64 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +22 -0
  43. data/lib/arjdbc/db2.rb +4 -0
  44. data/lib/arjdbc/db2/adapter.rb +802 -0
  45. data/lib/arjdbc/db2/as400.rb +137 -0
  46. data/lib/arjdbc/db2/column.rb +177 -0
  47. data/lib/arjdbc/db2/connection_methods.rb +45 -0
  48. data/lib/arjdbc/derby.rb +3 -0
  49. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  50. data/lib/arjdbc/derby/adapter.rb +567 -0
  51. data/lib/arjdbc/derby/connection_methods.rb +16 -0
  52. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  53. data/lib/arjdbc/discover.rb +104 -0
  54. data/lib/arjdbc/firebird.rb +4 -0
  55. data/lib/arjdbc/firebird/adapter.rb +468 -0
  56. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  57. data/lib/arjdbc/h2.rb +3 -0
  58. data/lib/arjdbc/h2/adapter.rb +335 -0
  59. data/lib/arjdbc/h2/connection_methods.rb +22 -0
  60. data/lib/arjdbc/hsqldb.rb +3 -0
  61. data/lib/arjdbc/hsqldb/adapter.rb +304 -0
  62. data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
  63. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  64. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  65. data/lib/arjdbc/informix.rb +5 -0
  66. data/lib/arjdbc/informix/adapter.rb +160 -0
  67. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  68. data/lib/arjdbc/jdbc.rb +62 -0
  69. data/lib/arjdbc/jdbc/adapter.rb +997 -0
  70. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  71. data/lib/arjdbc/jdbc/arel_support.rb +149 -0
  72. data/lib/arjdbc/jdbc/base_ext.rb +34 -0
  73. data/lib/arjdbc/jdbc/callbacks.rb +52 -0
  74. data/lib/arjdbc/jdbc/column.rb +83 -0
  75. data/lib/arjdbc/jdbc/connection.rb +26 -0
  76. data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
  77. data/lib/arjdbc/jdbc/driver.rb +44 -0
  78. data/lib/arjdbc/jdbc/error.rb +75 -0
  79. data/lib/arjdbc/jdbc/extension.rb +69 -0
  80. data/lib/arjdbc/jdbc/java.rb +13 -0
  81. data/lib/arjdbc/jdbc/type_cast.rb +154 -0
  82. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  83. data/lib/arjdbc/mssql.rb +7 -0
  84. data/lib/arjdbc/mssql/adapter.rb +822 -0
  85. data/lib/arjdbc/mssql/column.rb +207 -0
  86. data/lib/arjdbc/mssql/connection_methods.rb +72 -0
  87. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  88. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  89. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  90. data/lib/arjdbc/mssql/types.rb +343 -0
  91. data/lib/arjdbc/mssql/utils.rb +82 -0
  92. data/lib/arjdbc/mysql.rb +3 -0
  93. data/lib/arjdbc/mysql/adapter.rb +998 -0
  94. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  95. data/lib/arjdbc/mysql/column.rb +167 -0
  96. data/lib/arjdbc/mysql/connection_methods.rb +137 -0
  97. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  98. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  99. data/lib/arjdbc/oracle.rb +4 -0
  100. data/lib/arjdbc/oracle/adapter.rb +968 -0
  101. data/lib/arjdbc/oracle/column.rb +136 -0
  102. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  103. data/lib/arjdbc/postgresql.rb +3 -0
  104. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  105. data/lib/arjdbc/postgresql/adapter.rb +1498 -0
  106. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  107. data/lib/arjdbc/postgresql/base/oid.rb +412 -0
  108. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
  109. data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
  110. data/lib/arjdbc/postgresql/column.rb +640 -0
  111. data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
  112. data/lib/arjdbc/postgresql/explain_support.rb +53 -0
  113. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  114. data/lib/arjdbc/postgresql/oid_types.rb +265 -0
  115. data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
  116. data/lib/arjdbc/railtie.rb +11 -0
  117. data/lib/arjdbc/sqlite3.rb +3 -0
  118. data/lib/arjdbc/sqlite3/adapter.rb +654 -0
  119. data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
  120. data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
  121. data/lib/arjdbc/sybase.rb +2 -0
  122. data/lib/arjdbc/sybase/adapter.rb +47 -0
  123. data/lib/arjdbc/tasks.rb +13 -0
  124. data/lib/arjdbc/tasks/database_tasks.rb +66 -0
  125. data/lib/arjdbc/tasks/databases.rake +91 -0
  126. data/lib/arjdbc/tasks/databases3.rake +239 -0
  127. data/lib/arjdbc/tasks/databases4.rake +39 -0
  128. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  129. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  130. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  131. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  132. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  133. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  134. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  135. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  136. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  137. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  138. data/lib/arjdbc/util/table_copier.rb +108 -0
  139. data/lib/arjdbc/version.rb +8 -0
  140. data/lib/generators/jdbc/USAGE +9 -0
  141. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  142. data/pom.xml +285 -0
  143. data/rails_generators/jdbc_generator.rb +15 -0
  144. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  145. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  146. data/rakelib/01-tomcat.rake +51 -0
  147. data/rakelib/02-test.rake +151 -0
  148. data/rakelib/bundler_ext.rb +11 -0
  149. data/rakelib/db.rake +58 -0
  150. data/rakelib/rails.rake +77 -0
  151. data/src/java/arjdbc/ArJdbcModule.java +288 -0
  152. data/src/java/arjdbc/db2/DB2Module.java +77 -0
  153. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
  154. data/src/java/arjdbc/derby/DerbyModule.java +180 -0
  155. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
  156. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  157. data/src/java/arjdbc/h2/H2Module.java +50 -0
  158. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
  159. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
  160. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
  161. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  162. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  163. data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
  164. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  165. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  166. data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
  167. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
  168. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
  169. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  170. data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
  171. data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
  172. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
  173. data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
  174. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
  175. data/src/java/arjdbc/oracle/OracleModule.java +81 -0
  176. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
  177. data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
  178. data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
  179. data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
  180. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
  181. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
  182. data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
  184. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  185. data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
  186. data/src/java/arjdbc/util/NumberUtils.java +50 -0
  187. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  188. data/src/java/arjdbc/util/QuotingUtils.java +139 -0
  189. data/src/java/arjdbc/util/StringCache.java +60 -0
  190. data/src/java/arjdbc/util/StringHelper.java +155 -0
  191. metadata +288 -0
@@ -0,0 +1,456 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2013 Karol Bucek <self@kares.org>
3
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
+ * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining
8
+ * a copy of this software and associated documentation files (the
9
+ * "Software"), to deal in the Software without restriction, including
10
+ * without limitation the rights to use, copy, modify, merge, publish,
11
+ * distribute, sublicense, and/or sell copies of the Software, and to
12
+ * permit persons to whom the Software is furnished to do so, subject to
13
+ * the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ ***** END LICENSE BLOCK *****/
26
+ package arjdbc.mysql;
27
+
28
+ import arjdbc.jdbc.RubyJdbcConnection;
29
+ import arjdbc.jdbc.Callable;
30
+ import arjdbc.jdbc.DriverWrapper;
31
+ import arjdbc.util.DateTimeUtils;
32
+
33
+ import java.lang.reflect.Field;
34
+ import java.lang.reflect.InvocationTargetException;
35
+ import java.lang.reflect.Proxy;
36
+ import java.sql.Connection;
37
+ import java.sql.PreparedStatement;
38
+ import java.sql.SQLException;
39
+ import java.sql.ResultSet;
40
+ import java.sql.Statement;
41
+ import java.sql.Time;
42
+ import java.sql.Timestamp;
43
+ import java.sql.Types;
44
+ import java.util.regex.Matcher;
45
+ import java.util.regex.Pattern;
46
+
47
+ import org.jruby.Ruby;
48
+ import org.jruby.RubyArray;
49
+ import org.jruby.RubyClass;
50
+ import org.jruby.RubyFixnum;
51
+ import org.jruby.RubyFloat;
52
+ import org.jruby.RubyInteger;
53
+ import org.jruby.RubyModule;
54
+ import org.jruby.RubyString;
55
+ import org.jruby.exceptions.RaiseException;
56
+ import org.jruby.runtime.ObjectAllocator;
57
+ import org.jruby.runtime.ThreadContext;
58
+ import org.jruby.runtime.builtin.IRubyObject;
59
+ import org.jruby.util.SafePropertyAccessor;
60
+
61
+ /**
62
+ *
63
+ * @author nicksieger
64
+ */
65
+ @org.jruby.anno.JRubyClass(name = "ActiveRecord::ConnectionAdapters::MySQLJdbcConnection")
66
+ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
67
+ private static final long serialVersionUID = -8842614212147138733L;
68
+
69
+ public MySQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
70
+ super(runtime, metaClass);
71
+ }
72
+
73
+ public static RubyClass createMySQLJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
74
+ RubyClass clazz = getConnectionAdapters(runtime).
75
+ defineClassUnder("MySQLJdbcConnection", jdbcConnection, ALLOCATOR);
76
+ clazz.defineAnnotatedMethods(MySQLRubyJdbcConnection.class);
77
+ return clazz;
78
+ }
79
+
80
+ public static RubyClass load(final Ruby runtime) {
81
+ RubyClass jdbcConnection = getJdbcConnection(runtime);
82
+ return createMySQLJdbcConnectionClass(runtime, jdbcConnection);
83
+ }
84
+
85
+ protected static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
86
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
87
+ return new MySQLRubyJdbcConnection(runtime, klass);
88
+ }
89
+ };
90
+
91
+ @Override
92
+ protected DriverWrapper newDriverWrapper(final ThreadContext context, final String driver) {
93
+ DriverWrapper driverWrapper = super.newDriverWrapper(context, driver);
94
+
95
+ final java.sql.Driver jdbcDriver = driverWrapper.getDriverInstance();
96
+ if ( jdbcDriver.getClass().getName().startsWith("com.mysql.jdbc.") ) {
97
+ final int major = jdbcDriver.getMajorVersion();
98
+ final int minor = jdbcDriver.getMinorVersion();
99
+ if ( major < 5 ) {
100
+ final RubyClass errorClass = getConnectionNotEstablished(context.runtime);
101
+ throw new RaiseException(context.runtime, errorClass,
102
+ "MySQL adapter requires driver >= 5.0 got: " + major + "." + minor + "", false);
103
+ }
104
+ if ( major == 5 && minor < 1 ) { // need 5.1 for JDBC 4.0
105
+ // lightweight validation query: "/* ping */ SELECT 1"
106
+ setConfigValueIfNotSet(context, "connection_alive_sql", context.runtime.newString("/* ping */ SELECT 1"));
107
+ }
108
+ }
109
+
110
+ return driverWrapper;
111
+ }
112
+
113
+ @Override
114
+ protected boolean doExecute(final Statement statement, final String query)
115
+ throws SQLException {
116
+ return statement.execute(query, Statement.RETURN_GENERATED_KEYS);
117
+ }
118
+
119
+ @Override
120
+ protected IRubyObject mapGeneratedKeysOrUpdateCount(final ThreadContext context,
121
+ final Connection connection, final Statement statement) throws SQLException {
122
+ final Ruby runtime = context.runtime;
123
+ final IRubyObject key = mapGeneratedKeys(runtime, connection, statement);
124
+ return ( key == null || key.isNil() ) ?
125
+ RubyFixnum.newFixnum( runtime, statement.getUpdateCount() ) : key;
126
+ }
127
+
128
+ @Override
129
+ protected IRubyObject jdbcToRuby(
130
+ final ThreadContext context, final Ruby runtime,
131
+ final int column, final int type, final ResultSet resultSet)
132
+ throws SQLException {
133
+ if ( type == Types.BIT ) {
134
+ final int value = resultSet.getInt(column);
135
+ return resultSet.wasNull() ? runtime.getNil() : runtime.newFixnum(value);
136
+ }
137
+ return super.jdbcToRuby(context, runtime, column, type, resultSet);
138
+ }
139
+
140
+ @Override
141
+ protected boolean useByteStrings() {
142
+ final Boolean useByteStrings = byteStrings; // true by default :
143
+ return useByteStrings == null ? true : useByteStrings.booleanValue();
144
+ }
145
+
146
+ /*
147
+ @Override // optimized CLOBs
148
+ protected IRubyObject readerToRuby(final ThreadContext context,
149
+ final Ruby runtime, final ResultSet resultSet, final int column)
150
+ throws SQLException {
151
+ return bytesToUTF8String(context, runtime, resultSet, column);
152
+ } */
153
+
154
+ @Override // can not use statement.setTimestamp( int, Timestamp, Calendar )
155
+ protected void setTimestampParameter(final ThreadContext context,
156
+ final Connection connection, final PreparedStatement statement,
157
+ final int index, IRubyObject value,
158
+ final IRubyObject column, final int type) throws SQLException {
159
+ if ( value.isNil() ) statement.setNull(index, Types.TIMESTAMP);
160
+ else {
161
+ value = DateTimeUtils.getTimeInDefaultTimeZone(context, value);
162
+ if ( value instanceof RubyString ) { // yyyy-[m]m-[d]d hh:mm:ss[.f...]
163
+ final Timestamp timestamp = Timestamp.valueOf( value.toString() );
164
+ statement.setTimestamp( index, timestamp ); // assume local time-zone
165
+ }
166
+ else { // Time or DateTime ( ActiveSupport::TimeWithZone.to_time )
167
+ final double time = DateTimeUtils.adjustTimeFromDefaultZone(value);
168
+ final RubyFloat timeValue = context.runtime.newFloat( time );
169
+ statement.setTimestamp( index, DateTimeUtils.convertToTimestamp(timeValue) );
170
+ }
171
+ }
172
+ }
173
+
174
+ @Override // can not use statement.setTime( int, Time, Calendar )
175
+ protected void setTimeParameter(final ThreadContext context,
176
+ final Connection connection, final PreparedStatement statement,
177
+ final int index, IRubyObject value,
178
+ final IRubyObject column, final int type) throws SQLException {
179
+ if ( value.isNil() ) statement.setNull(index, Types.TIME);
180
+ else {
181
+ value = DateTimeUtils.getTimeInDefaultTimeZone(context, value);
182
+ if ( value instanceof RubyString ) {
183
+ final Time time = Time.valueOf( value.toString() );
184
+ statement.setTime( index, time ); // assume local time-zone
185
+ }
186
+ else { // Time or DateTime ( ActiveSupport::TimeWithZone.to_time )
187
+ final double timeValue = DateTimeUtils.adjustTimeFromDefaultZone(value);
188
+ final Time time = new Time(( (long) timeValue ) * 1000); // millis
189
+ // java.sql.Time is expected to be only up to second precision
190
+ statement.setTime( index, time );
191
+ }
192
+ }
193
+ }
194
+
195
+ @Override
196
+ protected final boolean isConnectionValid(final ThreadContext context, final Connection connection) {
197
+ if ( connection == null ) return false;
198
+ Statement statement = null;
199
+ try {
200
+ final RubyString aliveSQL = getAliveSQL(context);
201
+ final RubyInteger aliveTimeout = getAliveTimeout(context);
202
+ if ( aliveSQL != null ) {
203
+ // expect a SELECT/CALL SQL statement
204
+ statement = createStatement(context, connection);
205
+ if (aliveTimeout != null) {
206
+ statement.setQueryTimeout((int) aliveTimeout.getLongValue()); // 0 - no timeout
207
+ }
208
+ statement.execute( aliveSQL.toString() );
209
+ return true; // connection alive
210
+ }
211
+ else { // alive_sql nil (or not a statement we can execute)
212
+ return connection.isValid(aliveTimeout == null ? 0 : (int) aliveTimeout.getLongValue()); // since JDBC 4.0
213
+ // ... isValid(0) (default) means no timeout applied
214
+ }
215
+ }
216
+ catch (Exception e) {
217
+ debugMessage(context, "connection considered broken due: " + e.toString());
218
+ return false;
219
+ }
220
+ catch (AbstractMethodError e) { // non-JDBC 4.0 driver
221
+ warn( context,
222
+ "WARN: driver does not support checking if connection isValid()" +
223
+ " please make sure you're using a JDBC 4.0 compilant driver or" +
224
+ " set `connection_alive_sql: ...` in your database configuration" );
225
+ debugStackTrace(context, e);
226
+ throw e;
227
+ }
228
+ finally { close(statement); }
229
+ }
230
+
231
+ @Override
232
+ protected RubyArray indexes(final ThreadContext context,
233
+ final String tableName, final String name, final String schemaName) {
234
+ return withConnection(context, new Callable<RubyArray>() {
235
+ public RubyArray call(final Connection connection) throws SQLException {
236
+ final Ruby runtime = context.runtime;
237
+ final RubyModule IndexDefinition = getIndexDefinition(runtime);
238
+ final String jdbcTableName = caseConvertIdentifierForJdbc(connection, tableName);
239
+ final String jdbcSchemaName = caseConvertIdentifierForJdbc(connection, schemaName);
240
+ final RubyString rubyTableName = cachedString(
241
+ context, caseConvertIdentifierForJdbc(connection, tableName)
242
+ );
243
+
244
+ StringBuilder query = new StringBuilder(60).append("SHOW KEYS FROM ");
245
+ if ( jdbcSchemaName != null ) query.append(jdbcSchemaName).append('.');
246
+ query.append(jdbcTableName);
247
+ query.append(" WHERE key_name != 'PRIMARY'");
248
+
249
+ final RubyArray indexes = RubyArray.newArray(runtime, 8);
250
+ PreparedStatement statement = null;
251
+ ResultSet keySet = null;
252
+
253
+ try {
254
+ statement = connection.prepareStatement(query.toString());
255
+ keySet = statement.executeQuery();
256
+
257
+ String currentKeyName = null;
258
+ RubyArray currentColumns = null;
259
+ RubyArray currentLengths = null;
260
+
261
+ while ( keySet.next() ) {
262
+ final String keyName = caseConvertIdentifierForRails(connection, keySet.getString("key_name"));
263
+
264
+ if ( ! keyName.equals(currentKeyName) ) {
265
+ currentKeyName = keyName;
266
+
267
+ final boolean nonUnique = keySet.getBoolean("non_unique");
268
+
269
+ IRubyObject[] args = new IRubyObject[] {
270
+ rubyTableName, // table_name
271
+ RubyString.newUnicodeString(runtime, keyName), // index_name
272
+ nonUnique ? runtime.getFalse() : runtime.getTrue(), // unique
273
+ currentColumns = RubyArray.newArray(runtime, 4), // columns
274
+ currentLengths = RubyArray.newArray(runtime, 4) // lengths
275
+ };
276
+
277
+ indexes.append( IndexDefinition.callMethod(context, "new", args) ); // IndexDefinition.new
278
+ }
279
+
280
+ if ( currentColumns != null ) {
281
+ final String columnName = caseConvertIdentifierForRails(connection, keySet.getString("column_name"));
282
+ final int length = keySet.getInt("sub_part");
283
+ final boolean nullLength = length == 0 && keySet.wasNull();
284
+
285
+ currentColumns.callMethod(context, "<<", cachedString(context, columnName));
286
+ currentLengths.callMethod(context, "<<", nullLength ? context.nil : RubyFixnum.newFixnum(runtime, length));
287
+ }
288
+ }
289
+
290
+ return indexes;
291
+ }
292
+ finally {
293
+ close(keySet);
294
+ close(statement);
295
+ }
296
+ }
297
+ });
298
+ }
299
+
300
+ // MySQL does never storesUpperCaseIdentifiers() :
301
+ // storesLowerCaseIdentifiers() depends on "lower_case_table_names" server variable
302
+
303
+ @Override
304
+ protected final String caseConvertIdentifierForRails(
305
+ final Connection connection, final String value) throws SQLException {
306
+ if ( value == null ) return null;
307
+ return value;
308
+ }
309
+
310
+ @Override
311
+ protected final String caseConvertIdentifierForJdbc(
312
+ final Connection connection, final String value) throws SQLException {
313
+ if ( value == null ) return null;
314
+ if ( connection.getMetaData().storesLowerCaseIdentifiers() ) {
315
+ return value.toLowerCase();
316
+ }
317
+ return value;
318
+ }
319
+
320
+ @Override
321
+ protected Connection newConnection() throws RaiseException, SQLException {
322
+ final Connection connection = super.newConnection();
323
+ if ( doStopCleanupThread() ) shutdownCleanupThread();
324
+ if ( doKillCancelTimer(connection) ) killCancelTimer(connection);
325
+ return connection;
326
+ }
327
+
328
+ private static Boolean stopCleanupThread;
329
+ static {
330
+ final String stopThread = SafePropertyAccessor.getProperty("arjdbc.mysql.stop_cleanup_thread");
331
+ if ( stopThread != null ) stopCleanupThread = Boolean.parseBoolean(stopThread);
332
+ }
333
+
334
+ private static boolean doStopCleanupThread() throws SQLException {
335
+ // TODO when refactoring default behavior to "stop" consider not doing so for JNDI
336
+ return stopCleanupThread != null && stopCleanupThread.booleanValue();
337
+ }
338
+
339
+ private static boolean cleanupThreadShutdown;
340
+
341
+ @SuppressWarnings("unchecked")
342
+ private static void shutdownCleanupThread() {
343
+ if ( cleanupThreadShutdown ) return;
344
+ try {
345
+ Class<?> threadClass = Class.forName("com.mysql.jdbc.AbandonedConnectionCleanupThread");
346
+ threadClass.getMethod("shutdown").invoke(null);
347
+ }
348
+ catch (ClassNotFoundException e) {
349
+ debugMessage("missing MySQL JDBC cleanup thread: " + e);
350
+ }
351
+ catch (NoSuchMethodException e) {
352
+ debugMessage( e.toString() );
353
+ }
354
+ catch (IllegalAccessException e) {
355
+ debugMessage( e.toString() );
356
+ }
357
+ catch (InvocationTargetException e) {
358
+ debugMessage( e.getTargetException().toString() );
359
+ }
360
+ catch (SecurityException e) {
361
+ debugMessage( e.toString() );
362
+ }
363
+ finally { cleanupThreadShutdown = true; }
364
+ }
365
+
366
+ private static Boolean killCancelTimer;
367
+ static {
368
+ final String killTimer = SafePropertyAccessor.getProperty("arjdbc.mysql.kill_cancel_timer");
369
+ if ( killTimer != null ) killCancelTimer = Boolean.parseBoolean(killTimer);
370
+ }
371
+
372
+ private static boolean doKillCancelTimer(final Connection connection) throws SQLException {
373
+ if ( killCancelTimer == null ) {
374
+ synchronized (MySQLRubyJdbcConnection.class) {
375
+ final String version = connection.getMetaData().getDriverVersion();
376
+ if ( killCancelTimer == null ) {
377
+ String regex = "mysql\\-connector\\-java-(\\d)\\.(\\d)\\.(\\d+)";
378
+ Matcher match = Pattern.compile(regex).matcher(version);
379
+ if ( match.find() ) {
380
+ final int major = Integer.parseInt( match.group(1) );
381
+ final int minor = Integer.parseInt( match.group(2) );
382
+ if ( major < 5 || ( major == 5 && minor <= 1 ) ) {
383
+ final int patch = Integer.parseInt( match.group(3) );
384
+ killCancelTimer = patch < 11;
385
+ }
386
+ }
387
+ else {
388
+ killCancelTimer = Boolean.FALSE;
389
+ }
390
+ }
391
+ }
392
+ }
393
+ return killCancelTimer;
394
+ }
395
+
396
+ /**
397
+ * HACK HACK HACK See http://bugs.mysql.com/bug.php?id=36565
398
+ * MySQL's statement cancel timer can cause memory leaks, so cancel it
399
+ * if we loaded MySQL classes from the same class-loader as JRuby
400
+ *
401
+ * NOTE: MySQL Connector/J 5.1.11 (2010-01-21) fixed the issue !
402
+ */
403
+ private void killCancelTimer(final Connection connection) {
404
+ final Ruby runtime = getRuntime();
405
+ if (connection.getClass().getClassLoader() == runtime.getJRubyClassLoader()) {
406
+ final Field field = cancelTimerField(runtime);
407
+ if ( field != null ) {
408
+ java.util.Timer timer = null;
409
+ try {
410
+ Connection unwrap = connection.unwrap(Connection.class);
411
+ // when failover is used (LoadBalancedMySQLConnection)
412
+ // we'll end up with a proxy returned not the real thing :
413
+ if ( Proxy.isProxyClass(unwrap.getClass()) ) return;
414
+ // connection likely: com.mysql.jdbc.JDBC4Connection
415
+ // or (for 3.0) super class: com.mysql.jdbc.ConnectionImpl
416
+ timer = (java.util.Timer) field.get( unwrap );
417
+ }
418
+ catch (SQLException e) {
419
+ debugMessage( e.toString() );
420
+ }
421
+ catch (IllegalAccessException e) {
422
+ debugMessage( e.toString() );
423
+ }
424
+ if ( timer != null ) timer.cancel();
425
+ }
426
+ }
427
+ }
428
+
429
+ private static Field cancelTimer = null;
430
+ private static boolean cancelTimerChecked = false;
431
+
432
+ private Field cancelTimerField(final Ruby runtime) {
433
+ if ( cancelTimerChecked ) return cancelTimer;
434
+ final String name = "com.mysql.jdbc.ConnectionImpl";
435
+ try {
436
+ Class<?> klass = runtime.getJavaSupport().loadJavaClass(name);
437
+ Field field = klass.getDeclaredField("cancelTimer");
438
+ field.setAccessible(true);
439
+ synchronized (MySQLRubyJdbcConnection.class) {
440
+ if ( cancelTimer == null ) cancelTimer = field;
441
+ }
442
+ }
443
+ catch (ClassNotFoundException e) {
444
+ debugMessage("missing MySQL JDBC connection impl: " + e);
445
+ }
446
+ catch (NoSuchFieldException e) {
447
+ debugMessage("MySQL's cancel timer seems to have changed: " + e);
448
+ }
449
+ catch (SecurityException e) {
450
+ debugMessage( e.toString() );
451
+ }
452
+ finally { cancelTimerChecked = true; }
453
+ return cancelTimer;
454
+ }
455
+
456
+ }