activerecord-jdbc-adapter 5.0.pre1 → 51.0

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 (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.travis.yml +15 -416
  4. data/Gemfile +35 -37
  5. data/README.md +23 -118
  6. data/RUNNING_TESTS.md +31 -26
  7. data/Rakefile +2 -3
  8. data/activerecord-jdbc-adapter.gemspec +1 -2
  9. data/lib/arjdbc/abstract/connection_management.rb +21 -0
  10. data/lib/arjdbc/abstract/core.rb +62 -0
  11. data/lib/arjdbc/abstract/database_statements.rb +46 -0
  12. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  13. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  14. data/lib/arjdbc/derby/adapter.rb +6 -1
  15. data/lib/arjdbc/discover.rb +0 -7
  16. data/lib/arjdbc/firebird/adapter.rb +2 -2
  17. data/lib/arjdbc/jdbc/adapter.rb +10 -252
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/connection.rb +6 -0
  20. data/lib/arjdbc/jdbc.rb +2 -2
  21. data/lib/arjdbc/mysql/adapter.rb +87 -944
  22. data/lib/arjdbc/mysql/connection_methods.rb +4 -2
  23. data/lib/arjdbc/postgresql/adapter.rb +288 -1023
  24. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  25. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  26. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
  27. data/lib/arjdbc/postgresql/column.rb +10 -599
  28. data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
  29. data/lib/arjdbc/postgresql/name.rb +24 -0
  30. data/lib/arjdbc/postgresql/oid_types.rb +25 -110
  31. data/lib/arjdbc/sqlite3/adapter.rb +171 -170
  32. data/lib/arjdbc/tasks/database_tasks.rb +1 -3
  33. data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/pom.xml +3 -3
  36. data/rakelib/02-test.rake +0 -12
  37. data/rakelib/compile.rake +1 -1
  38. data/rakelib/db.rake +7 -5
  39. data/rakelib/rails.rake +63 -64
  40. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
  41. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
  42. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  43. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
  44. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
  45. data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
  46. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
  47. metadata +20 -34
  48. data/Appraisals +0 -41
  49. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  50. data/lib/arjdbc/common_jdbc_methods.rb +0 -89
  51. data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
  52. data/lib/arjdbc/mysql/column.rb +0 -162
  53. data/lib/arjdbc/mysql/explain_support.rb +0 -82
  54. data/lib/arjdbc/mysql/schema_creation.rb +0 -58
  55. data/lib/arjdbc/oracle/adapter.rb +0 -952
  56. data/lib/arjdbc/oracle/column.rb +0 -126
  57. data/lib/arjdbc/oracle/connection_methods.rb +0 -21
  58. data/lib/arjdbc/oracle.rb +0 -4
  59. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -21
  60. data/lib/arjdbc/postgresql/base/oid.rb +0 -412
  61. data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
  62. data/lib/arjdbc/postgresql/explain_support.rb +0 -53
  63. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
  64. data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
  65. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
  66. data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
  67. data/src/java/arjdbc/oracle/OracleModule.java +0 -75
  68. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -100,9 +100,9 @@ public class MySQLModule {
100
100
 
101
101
  final Ruby runtime = context.getRuntime();
102
102
  final RubyString quoted = runtime.newString(quotedBytes);
103
- if ( runtime.is1_9() ) { // only due mysql2 compatibility
104
- quoted.associateEncoding( UTF8Encoding.INSTANCE );
105
- }
103
+
104
+ quoted.associateEncoding(UTF8Encoding.INSTANCE);
105
+
106
106
  return quoted;
107
107
  }
108
108
 
@@ -26,7 +26,6 @@
26
26
  package arjdbc.mysql;
27
27
 
28
28
  import arjdbc.jdbc.RubyJdbcConnection;
29
- import arjdbc.jdbc.Callable;
30
29
 
31
30
  import java.lang.reflect.Field;
32
31
  import java.lang.reflect.InvocationTargetException;
@@ -43,17 +42,18 @@ import java.util.TimeZone;
43
42
  import java.util.regex.Matcher;
44
43
  import java.util.regex.Pattern;
45
44
 
45
+ import org.joda.time.DateTime;
46
46
  import org.jruby.Ruby;
47
- import org.jruby.RubyArray;
48
47
  import org.jruby.RubyClass;
49
- import org.jruby.RubyFloat;
50
48
  import org.jruby.RubyInteger;
51
- import org.jruby.RubyModule;
52
49
  import org.jruby.RubyString;
50
+ import org.jruby.RubyTime;
51
+ import org.jruby.anno.JRubyMethod;
53
52
  import org.jruby.exceptions.RaiseException;
54
53
  import org.jruby.runtime.ObjectAllocator;
55
54
  import org.jruby.runtime.ThreadContext;
56
55
  import org.jruby.runtime.builtin.IRubyObject;
56
+ import org.jruby.util.TypeConverter;
57
57
 
58
58
  /**
59
59
  *
@@ -79,25 +79,20 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
79
79
  return clazz;
80
80
  }
81
81
 
82
- @Override
83
- protected boolean doExecute(final Statement statement, final String query)
84
- throws SQLException {
85
- return statement.execute(query, Statement.RETURN_GENERATED_KEYS);
82
+ @JRubyMethod
83
+ public IRubyObject query(final ThreadContext context, final IRubyObject sql) throws SQLException {
84
+ final String query = sql.convertToString().getUnicodeValue(); // sql
85
+ return executeUpdate(context, query, false);
86
86
  }
87
87
 
88
88
  @Override
89
- protected IRubyObject mapGeneratedKeysOrUpdateCount(final ThreadContext context,
90
- final Connection connection, final Statement statement) throws SQLException {
91
- final Ruby runtime = context.getRuntime();
92
- final IRubyObject key = mapGeneratedKeys(runtime, connection, statement);
93
- return ( key == null || key.isNil() ) ? runtime.newFixnum( statement.getUpdateCount() ) : key;
89
+ protected boolean doExecute(final Statement statement, final String query) throws SQLException {
90
+ return statement.execute(query, Statement.RETURN_GENERATED_KEYS);
94
91
  }
95
92
 
96
93
  @Override
97
- protected IRubyObject jdbcToRuby(
98
- final ThreadContext context, final Ruby runtime,
99
- final int column, final int type, final ResultSet resultSet)
100
- throws SQLException {
94
+ protected IRubyObject jdbcToRuby(final ThreadContext context, final Ruby runtime,
95
+ final int column, final int type, final ResultSet resultSet) throws SQLException {
101
96
  if ( type == Types.BIT ) {
102
97
  final int value = resultSet.getInt(column);
103
98
  return resultSet.wasNull() ? runtime.getNil() : runtime.newFixnum(value);
@@ -106,53 +101,50 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
106
101
  }
107
102
 
108
103
  @Override // can not use statement.setTimestamp( int, Timestamp, Calendar )
109
- protected void setTimestampParameter(final ThreadContext context,
110
- final Connection connection, final PreparedStatement statement,
111
- final int index, IRubyObject value,
112
- final IRubyObject column, final int type) throws SQLException {
113
- if ( value.isNil() ) statement.setNull(index, Types.TIMESTAMP);
114
- else {
115
- value = getTimeInDefaultTimeZone(context, value);
116
- if ( value instanceof RubyString ) { // yyyy-[m]m-[d]d hh:mm:ss[.f...]
117
- final Timestamp timestamp = Timestamp.valueOf( value.toString() );
118
- statement.setTimestamp( index, timestamp ); // assume local time-zone
119
- }
120
- else { // Time or DateTime ( ActiveSupport::TimeWithZone.to_time )
121
- final double time = adjustTimeFromDefaultZone(value);
122
- final RubyFloat timeValue = context.getRuntime().newFloat( time );
123
- statement.setTimestamp( index, convertToTimestamp(timeValue) );
124
- }
125
- }
104
+ protected void setTimestampParameter(ThreadContext context, Connection connection, PreparedStatement statement,
105
+ int index, IRubyObject value, IRubyObject column, int type) throws SQLException {
106
+ value = callMethod(context, "time_in_default_timezone", value);
107
+ TypeConverter.checkType(context, value, context.runtime.getTime());
108
+ setTimestamp(statement, index, (RubyTime) value, type);
126
109
  }
127
110
 
128
- @Override // can not use statement.setTime( int, Time, Calendar )
129
- protected void setTimeParameter(final ThreadContext context,
130
- final Connection connection, final PreparedStatement statement,
131
- final int index, IRubyObject value,
132
- final IRubyObject column, final int type) throws SQLException {
133
- if ( value.isNil() ) statement.setNull(index, Types.TIME);
134
- else {
135
- value = getTimeInDefaultTimeZone(context, value);
136
- if ( value instanceof RubyString ) {
137
- final Time time = Time.valueOf( value.toString() );
138
- statement.setTime( index, time ); // assume local time-zone
139
- }
140
- else { // Time or DateTime ( ActiveSupport::TimeWithZone.to_time )
141
- final double timeValue = adjustTimeFromDefaultZone(value);
142
- final Time time = new Time(( (long) timeValue ) * 1000); // millis
143
- // java.sql.Time is expected to be only up to second precision
144
- statement.setTime( index, time );
145
- }
146
- }
111
+ @Override
112
+ protected void setTimeParameter(ThreadContext context, Connection connection, PreparedStatement statement,
113
+ int index, IRubyObject value, IRubyObject column, int type) throws SQLException {
114
+ setTimestampParameter(context, connection, statement, index, value, column, type);
147
115
  }
148
116
 
149
- private static double adjustTimeFromDefaultZone(final IRubyObject value) {
150
- // Time's to_f is : ( millis * 1000 + usec ) / 1_000_000.0
151
- final double time = value.convertToFloat().getDoubleValue(); // to_f
152
- // NOTE: MySQL assumes default TZ thus need to adjust to match :
153
- final int offset = TimeZone.getDefault().getOffset((long) time * 1000);
154
- // Time's to_f is : ( millis * 1000 + usec ) / 1_000_000.0
155
- return time - ( offset / 1000.0 );
117
+ // FIXME: we should detect adapter and not do this timezone offset calculation is it is jdbc version 6+.
118
+ private void setTimestamp(PreparedStatement statement, int index, RubyTime value, int type) throws SQLException {
119
+ DateTime dateTime = value.getDateTime();
120
+ int offset = TimeZone.getDefault().getOffset(dateTime.getMillis()); // JDBC <6.x ignores time zone info (we adjust manually).
121
+ Timestamp timestamp = new Timestamp(dateTime.getMillis() - offset);
122
+
123
+ // 1942-11-30T01:02:03.123_456
124
+ if (type != Types.DATE && value.getNSec() >= 0) timestamp.setNanos((int) (timestamp.getNanos() + value.getNSec()));
125
+
126
+ statement.setTimestamp(index, timestamp);
127
+ }
128
+
129
+ // FIXME: I think we can unify this back to main adapter code since previous conflict involved not using
130
+ // the raw string return type and not the extra formatting logic.
131
+ @Override
132
+ protected IRubyObject timeToRuby(ThreadContext context, Ruby runtime, ResultSet resultSet, int column) throws SQLException {
133
+ Time value = resultSet.getTime(column);
134
+
135
+ if (value == null) return resultSet.wasNull() ? runtime.getNil() : runtime.newString();
136
+
137
+ String strValue = value.toString();
138
+
139
+ // If time is column type but that time had a precision which included
140
+ // nanoseconds we used timestamp to save the data. Since this is conditional
141
+ // we grab data a second time as a timestamp to look for nsecs.
142
+ Timestamp nsecTimeHack = resultSet.getTimestamp(column);
143
+ if (nsecTimeHack.getNanos() != 0) {
144
+ strValue = String.format("%s.%09d", strValue, nsecTimeHack.getNanos());
145
+ }
146
+
147
+ return RubyString.newUnicodeString(runtime,strValue);
156
148
  }
157
149
 
158
150
  @Override
@@ -192,80 +184,7 @@ public class MySQLRubyJdbcConnection extends RubyJdbcConnection {
192
184
  }
193
185
 
194
186
  @Override
195
- protected IRubyObject indexes(final ThreadContext context, final String tableName, final String name, final String schemaName) {
196
- return withConnection(context, new Callable<IRubyObject>() {
197
- public IRubyObject call(final Connection connection) throws SQLException {
198
- final Ruby runtime = context.getRuntime();
199
- final RubyModule indexDefinition = getIndexDefinition(runtime);
200
- final String jdbcTableName = caseConvertIdentifierForJdbc(connection, tableName);
201
- final String jdbcSchemaName = caseConvertIdentifierForJdbc(connection, schemaName);
202
- final IRubyObject rubyTableName = RubyString.newUnicodeString(
203
- runtime, caseConvertIdentifierForJdbc(connection, tableName)
204
- );
205
-
206
- StringBuilder query = new StringBuilder("SHOW KEYS FROM ");
207
- if (jdbcSchemaName != null) {
208
- query.append(jdbcSchemaName).append(".");
209
- }
210
- query.append(jdbcTableName);
211
- query.append(" WHERE key_name != 'PRIMARY'");
212
-
213
- final RubyArray indexes = runtime.newArray(8);
214
- PreparedStatement statement = null;
215
- ResultSet keySet = null;
216
-
217
- try {
218
- statement = connection.prepareStatement(query.toString());
219
- keySet = statement.executeQuery();
220
-
221
- String currentKeyName = null;
222
-
223
- while ( keySet.next() ) {
224
- final String keyName = caseConvertIdentifierForRails(connection, keySet.getString("key_name"));
225
-
226
- if ( ! keyName.equals(currentKeyName) ) {
227
- currentKeyName = keyName;
228
-
229
- final boolean nonUnique = keySet.getBoolean("non_unique");
230
-
231
- IRubyObject[] args = new IRubyObject[] {
232
- rubyTableName, // table_name
233
- RubyString.newUnicodeString(runtime, keyName), // index_name
234
- runtime.newBoolean( ! nonUnique ), // unique
235
- runtime.newArray(), // [] for column names, we'll add to that in just a bit
236
- runtime.newArray() // lengths
237
- };
238
-
239
- indexes.append( indexDefinition.callMethod(context, "new", args) ); // IndexDefinition.new
240
- }
241
-
242
- IRubyObject lastIndexDef = indexes.isEmpty() ? null : indexes.entry(-1);
243
- if ( lastIndexDef != null ) {
244
- final String columnName = caseConvertIdentifierForRails(connection, keySet.getString("column_name"));
245
- final int length = keySet.getInt("sub_part");
246
- final boolean nullLength = keySet.wasNull();
247
-
248
- lastIndexDef.callMethod(context, "columns").callMethod(context,
249
- "<<", RubyString.newUnicodeString(runtime, columnName));
250
- lastIndexDef.callMethod(context, "lengths").callMethod(context,
251
- "<<", nullLength ? runtime.getNil() : runtime.newFixnum(length));
252
- }
253
- }
254
-
255
- return indexes;
256
- }
257
- finally {
258
- close(keySet);
259
- close(statement);
260
- }
261
- }
262
- });
263
- }
264
-
265
- @Override
266
- protected String caseConvertIdentifierForRails(final Connection connection, final String value)
267
- throws SQLException {
268
- if ( value == null ) return null;
187
+ protected String caseConvertIdentifierForRails(Connection connection, String value) throws SQLException {
269
188
  return value; // MySQL does not storesUpperCaseIdentifiers() :
270
189
  }
271
190