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.
- checksums.yaml +5 -5
- data/.gitignore +1 -2
- data/.travis.yml +15 -416
- data/Gemfile +35 -37
- data/README.md +23 -118
- data/RUNNING_TESTS.md +31 -26
- data/Rakefile +2 -3
- data/activerecord-jdbc-adapter.gemspec +1 -2
- data/lib/arjdbc/abstract/connection_management.rb +21 -0
- data/lib/arjdbc/abstract/core.rb +62 -0
- data/lib/arjdbc/abstract/database_statements.rb +46 -0
- data/lib/arjdbc/abstract/statement_cache.rb +58 -0
- data/lib/arjdbc/abstract/transaction_support.rb +86 -0
- data/lib/arjdbc/derby/adapter.rb +6 -1
- data/lib/arjdbc/discover.rb +0 -7
- data/lib/arjdbc/firebird/adapter.rb +2 -2
- data/lib/arjdbc/jdbc/adapter.rb +10 -252
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/connection.rb +6 -0
- data/lib/arjdbc/jdbc.rb +2 -2
- data/lib/arjdbc/mysql/adapter.rb +87 -944
- data/lib/arjdbc/mysql/connection_methods.rb +4 -2
- data/lib/arjdbc/postgresql/adapter.rb +288 -1023
- data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
- data/lib/arjdbc/postgresql/column.rb +10 -599
- data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
- data/lib/arjdbc/postgresql/name.rb +24 -0
- data/lib/arjdbc/postgresql/oid_types.rb +25 -110
- data/lib/arjdbc/sqlite3/adapter.rb +171 -170
- data/lib/arjdbc/tasks/database_tasks.rb +1 -3
- data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +3 -3
- data/rakelib/02-test.rake +0 -12
- data/rakelib/compile.rake +1 -1
- data/rakelib/db.rake +7 -5
- data/rakelib/rails.rake +63 -64
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
- data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
- metadata +20 -34
- data/Appraisals +0 -41
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
- data/lib/arjdbc/common_jdbc_methods.rb +0 -89
- data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
- data/lib/arjdbc/mysql/column.rb +0 -162
- data/lib/arjdbc/mysql/explain_support.rb +0 -82
- data/lib/arjdbc/mysql/schema_creation.rb +0 -58
- data/lib/arjdbc/oracle/adapter.rb +0 -952
- data/lib/arjdbc/oracle/column.rb +0 -126
- data/lib/arjdbc/oracle/connection_methods.rb +0 -21
- data/lib/arjdbc/oracle.rb +0 -4
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -21
- data/lib/arjdbc/postgresql/base/oid.rb +0 -412
- data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
- data/lib/arjdbc/postgresql/explain_support.rb +0 -53
- data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
- data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
- data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
- data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
- data/src/java/arjdbc/oracle/OracleModule.java +0 -75
- 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
|
-
|
104
|
-
|
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
|
-
@
|
83
|
-
|
84
|
-
|
85
|
-
return
|
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
|
90
|
-
|
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
|
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(
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
129
|
-
protected void setTimeParameter(
|
130
|
-
|
131
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
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
|
|