activerecord-jdbc-adapter 5.0.pre1 → 51.0

Sign up to get free protection for your applications and to get access to all the features.
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