activerecord-jdbc-adapter 1.3.11 → 1.3.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -202,6 +202,11 @@ module ArJdbc
202
202
  true
203
203
  end
204
204
 
205
+ # @override
206
+ def supports_views?
207
+ true
208
+ end
209
+
205
210
  def sqlite_version
206
211
  @sqlite_version ||= Version.new(select_value('SELECT sqlite_version(*)'))
207
212
  end
@@ -213,8 +218,8 @@ module ArJdbc
213
218
 
214
219
  if value.kind_of?(String)
215
220
  column_type = column && column.type
216
- if column_type == :binary && column.class.respond_to?(:string_to_binary)
217
- "x'#{column.class.string_to_binary(value).unpack("H*")[0]}'"
221
+ if column_type == :binary
222
+ "x'#{value.unpack("H*")[0]}'"
218
223
  else
219
224
  super
220
225
  end
@@ -344,8 +349,15 @@ module ArJdbc
344
349
  # @override
345
350
  def columns(table_name, name = nil)
346
351
  klass = ::ActiveRecord::ConnectionAdapters::SQLite3Column
352
+ pass_cast_type = respond_to?(:lookup_cast_type)
347
353
  table_structure(table_name).map do |field|
348
- klass.new(field['name'], field['dflt_value'], field['type'], field['notnull'] == 0)
354
+ sql_type = field['type']
355
+ if pass_cast_type
356
+ cast_type = lookup_cast_type(sql_type)
357
+ klass.new(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'] == 0)
358
+ else
359
+ klass.new(field['name'], field['dflt_value'], sql_type, field['notnull'] == 0)
360
+ end
349
361
  end
350
362
  end
351
363
 
@@ -355,6 +367,38 @@ module ArJdbc
355
367
  column && column['name']
356
368
  end
357
369
 
370
+ # NOTE: do not override indexes without testing support for 3.7.2 & 3.8.7 !
371
+ # @override
372
+ def indexes(table_name, name = nil)
373
+ # on JDBC 3.7 we'll simply do super since it can not handle "PRAGMA index_info"
374
+ return @connection.indexes(table_name, name) if sqlite_version < '3.8' # super
375
+
376
+ name ||= 'SCHEMA'
377
+ exec_query_raw("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
378
+ index_name = row['name']
379
+ sql = "SELECT sql FROM sqlite_master"
380
+ sql << " WHERE name=#{quote(index_name)} AND type='index'"
381
+ sql << " UNION ALL "
382
+ sql << "SELECT sql FROM sqlite_temp_master"
383
+ sql << " WHERE name=#{quote(index_name)} AND type='index'"
384
+ where = nil
385
+ exec_query_raw(sql, name) do |index_sql|
386
+ match = /\sWHERE\s+(.+)$/i.match(index_sql)
387
+ where = match[1] if match
388
+ end
389
+ begin
390
+ columns = exec_query_raw("PRAGMA index_info('#{index_name}')", name).map { |col| col['name'] }
391
+ rescue => e
392
+ # NOTE: JDBC <= 3.8.7 bug work-around :
393
+ if e.message && e.message.index('[SQLITE_ERROR] SQL error or missing database')
394
+ columns = []
395
+ end
396
+ raise e
397
+ end
398
+ new_index_definition(table_name, index_name, row['unique'] != 0, columns, nil, nil, where)
399
+ end
400
+ end
401
+
358
402
  # @override
359
403
  def remove_index!(table_name, index_name)
360
404
  execute "DROP INDEX #{quote_column_name(index_name)}"
@@ -476,12 +520,17 @@ module ArJdbc
476
520
  end
477
521
 
478
522
  def translate_exception(exception, message)
479
- case exception.message
480
- when /column(s)? .* (is|are) not unique/
481
- ActiveRecord::RecordNotUnique.new(message, exception)
482
- else
483
- super
523
+ if msg = exception.message
524
+ # SQLite 3.8.2 returns a newly formatted error message:
525
+ # UNIQUE constraint failed: *table_name*.*column_name*
526
+ # Older versions of SQLite return:
527
+ # column *column_name* is not unique
528
+ if msg.index('UNIQUE constraint failed: ') ||
529
+ msg =~ /column(s)? .* (is|are) not unique/
530
+ return RecordNotUnique.new(message, exception)
531
+ end
484
532
  end
533
+ super
485
534
  end
486
535
 
487
536
  # @private available in native adapter way back to AR-2.3
@@ -560,4 +609,4 @@ module ActiveRecord::ConnectionAdapters
560
609
 
561
610
  SQLiteAdapter = SQLite3Adapter
562
611
  end
563
- end
612
+ end
@@ -1,5 +1,5 @@
1
1
  module ArJdbc
2
- VERSION = "1.3.11"
2
+ VERSION = "1.3.12"
3
3
  # @deprecated
4
4
  module Version
5
5
  # @private 1.2.x compatibility
@@ -53,7 +53,7 @@ def test_task_for(adapter, options = {})
53
53
  desc = options[:desc] || options[:comment] ||
54
54
  "Run tests against #{options[:database_name] || adapter}"
55
55
  adapter = adapter.to_s.downcase
56
- driver = options.key?(:driver) ? options[:driver] : adapter
56
+ driver = adapter if ( driver = options[:driver] ).nil?
57
57
  prereqs = options[:prereqs] || []
58
58
  unless prereqs.frozen?
59
59
  prereqs = [ prereqs ].flatten; prereqs << 'test_appraisal_hint'
@@ -87,7 +87,7 @@ test_task_for :MSSQL, :driver => :jtds, :database_name => 'MS-SQL (SQLServer)'
87
87
  test_task_for :MySQL, :prereqs => 'db:mysql'
88
88
  test_task_for :PostgreSQL, :driver => 'postgres', :prereqs => 'db:postgresql'
89
89
  task :test_postgres => :test_postgresql # alias
90
- test_task_for :SQLite3
90
+ test_task_for :SQLite3, :driver => ENV['JDBC_SQLITE_VERSION']
91
91
  task :test_sqlite => :test_sqlite3 # alias
92
92
  test_task_for :Firebird
93
93
 
@@ -89,7 +89,7 @@ import org.jruby.runtime.builtin.IRubyObject;
89
89
  import org.jruby.util.ByteList;
90
90
 
91
91
  /**
92
- * Part of our ActiveRecord::ConnectionAdapters::Connection impl.
92
+ * Most of our ActiveRecord::ConnectionAdapters::JdbcConnection implementation.
93
93
  */
94
94
  public class RubyJdbcConnection extends RubyObject {
95
95
 
@@ -444,7 +444,16 @@ public class RubyJdbcConnection extends RubyObject {
444
444
  * @return connection
445
445
  */
446
446
  @JRubyMethod(name = "init_connection")
447
- public synchronized IRubyObject init_connection(final ThreadContext context) throws SQLException {
447
+ public synchronized IRubyObject init_connection(final ThreadContext context) {
448
+ try {
449
+ return initConnection(context);
450
+ }
451
+ catch (SQLException e) {
452
+ return handleException(context, e); // throws
453
+ }
454
+ }
455
+
456
+ private IRubyObject initConnection(final ThreadContext context) throws SQLException {
448
457
  final IRubyObject jdbcConnection = setConnection( newConnection() );
449
458
  final IRubyObject adapter = callMethod("adapter"); // self.adapter
450
459
  if ( ! adapter.isNil() ) {
@@ -1140,10 +1149,10 @@ public class RubyJdbcConnection extends RubyObject {
1140
1149
  }
1141
1150
 
1142
1151
  // NOTE: metaData.getIndexInfo row mappings :
1143
- private static final int INDEX_INFO_TABLE_NAME = 3;
1144
- private static final int INDEX_INFO_NON_UNIQUE = 4;
1145
- private static final int INDEX_INFO_NAME = 6;
1146
- private static final int INDEX_INFO_COLUMN_NAME = 9;
1152
+ protected static final int INDEX_INFO_TABLE_NAME = 3;
1153
+ protected static final int INDEX_INFO_NON_UNIQUE = 4;
1154
+ protected static final int INDEX_INFO_NAME = 6;
1155
+ protected static final int INDEX_INFO_COLUMN_NAME = 9;
1147
1156
 
1148
1157
  /**
1149
1158
  * Default JDBC introspection for index metadata on the JdbcConnection.
@@ -1217,6 +1226,27 @@ public class RubyJdbcConnection extends RubyObject {
1217
1226
  });
1218
1227
  }
1219
1228
 
1229
+ @JRubyMethod(name = "supports_views?")
1230
+ public IRubyObject supports_views_p(final ThreadContext context) throws SQLException {
1231
+ return withConnection(context, new Callable<IRubyObject>() {
1232
+ public IRubyObject call(final Connection connection) throws SQLException {
1233
+ final DatabaseMetaData metaData = connection.getMetaData();
1234
+ final ResultSet tableTypes = metaData.getTableTypes();
1235
+ try {
1236
+ while ( tableTypes.next() ) {
1237
+ if ( "VIEW".equalsIgnoreCase( tableTypes.getString(1) ) ) {
1238
+ return context.getRuntime().newBoolean( true );
1239
+ }
1240
+ }
1241
+ }
1242
+ finally {
1243
+ close(tableTypes);
1244
+ }
1245
+ return context.getRuntime().newBoolean( false );
1246
+ }
1247
+ });
1248
+ }
1249
+
1220
1250
  // NOTE: this seems to be not used ... at all ?!
1221
1251
  /*
1222
1252
  * sql, values (array), types (column.type array), name = nil, pk = nil, id_value = nil, sequence_name = nil
@@ -1425,9 +1455,17 @@ public class RubyJdbcConnection extends RubyObject {
1425
1455
  return context.getRuntime().newBoolean( true );
1426
1456
  }
1427
1457
 
1458
+ private IRubyObject getConfig(final ThreadContext context) {
1459
+ return getInstanceVariable("@config"); // this.callMethod(context, "config");
1460
+ }
1461
+
1428
1462
  protected final IRubyObject getConfigValue(final ThreadContext context, final String key) {
1429
- final IRubyObject config = callMethod(context, "config");
1430
- return config.callMethod(context, "[]", context.getRuntime().newSymbol(key));
1463
+ final IRubyObject config = getConfig(context);
1464
+ final RubySymbol keySym = context.runtime.newSymbol(key);
1465
+ if ( config instanceof RubyHash ) {
1466
+ return ((RubyHash) config).op_aref(context, keySym);
1467
+ }
1468
+ return config.callMethod(context, "[]", keySym);
1431
1469
  }
1432
1470
 
1433
1471
  /**
@@ -1518,7 +1556,7 @@ public class RubyJdbcConnection extends RubyObject {
1518
1556
  final RubyArray resultRows = runtime.newArray();
1519
1557
 
1520
1558
  while ( resultSet.next() ) {
1521
- resultRows.add( resultHandler.mapRow(context, runtime, columns, resultSet, this) );
1559
+ resultRows.append( resultHandler.mapRow(context, runtime, columns, resultSet, this) );
1522
1560
  }
1523
1561
 
1524
1562
  return resultHandler.newResult(context, runtime, columns, resultRows);
@@ -2897,7 +2935,7 @@ public class RubyJdbcConnection extends RubyObject {
2897
2935
  }
2898
2936
 
2899
2937
  final RubyArray columns = runtime.newArray();
2900
- final IRubyObject config = getInstanceVariable("@config");
2938
+ final IRubyObject config = getConfig(context);
2901
2939
  while ( results.next() ) {
2902
2940
  final String colName = results.getString(COLUMN_NAME);
2903
2941
  IRubyObject column = jdbcColumn.callMethod(context, "new",
@@ -37,6 +37,7 @@ import java.sql.SQLException;
37
37
  import java.sql.Statement;
38
38
  import java.sql.DatabaseMetaData;
39
39
  import java.sql.Savepoint;
40
+ import java.sql.Types;
40
41
  import java.util.ArrayList;
41
42
  import java.util.List;
42
43
 
@@ -118,15 +119,81 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
118
119
  }
119
120
 
120
121
  @Override
121
- protected IRubyObject indexes(final ThreadContext context, String tableName, final String name, String schemaName) {
122
- if ( tableName != null ) {
123
- final int i = tableName.indexOf('.');
124
- if ( i > 0 && schemaName == null ) {
125
- schemaName = tableName.substring(0, i);
126
- tableName = tableName.substring(i + 1);
122
+ protected IRubyObject indexes(final ThreadContext context, String table, final String name, String schema) {
123
+ if ( table != null ) {
124
+ final int i = table.indexOf('.');
125
+ if ( i > 0 && schema == null ) {
126
+ schema = table.substring(0, i);
127
+ table = table.substring(i + 1);
127
128
  }
128
129
  }
129
- return super.indexes(context, tableName, name, schemaName);
130
+ final String tableName = table;
131
+ final String schemaName = schema;
132
+ // return super.indexes(context, tableName, name, schemaName);
133
+ return withConnection(context, new Callable<IRubyObject>() {
134
+ public RubyArray call(final Connection connection) throws SQLException {
135
+ final Ruby runtime = context.runtime;
136
+ final RubyClass indexDefinition = getIndexDefinition(runtime);
137
+
138
+ final TableName table = extractTableName(connection, schemaName, tableName);
139
+
140
+ final List<RubyString> primaryKeys = primaryKeys(context, connection, table);
141
+
142
+ final DatabaseMetaData metaData = connection.getMetaData();
143
+ ResultSet indexInfoSet = null;
144
+ try {
145
+ indexInfoSet = metaData.getIndexInfo(table.catalog, table.schema, table.name, false, true);
146
+ }
147
+ catch (SQLException e) {
148
+ final String msg = e.getMessage();
149
+ if ( msg != null && msg.startsWith("[SQLITE_ERROR] SQL error or missing database") ) {
150
+ return RubyArray.newEmptyArray(runtime); // on 3.8.7 getIndexInfo fails if table has no indexes
151
+ }
152
+ throw e;
153
+ }
154
+ final RubyArray indexes = RubyArray.newArray(runtime, 8);
155
+ try {
156
+ String currentIndex = null;
157
+
158
+ while ( indexInfoSet.next() ) {
159
+ String indexName = indexInfoSet.getString(INDEX_INFO_NAME);
160
+ if ( indexName == null ) continue;
161
+
162
+ final String columnName = indexInfoSet.getString(INDEX_INFO_COLUMN_NAME);
163
+ final RubyString rubyColumnName = RubyString.newUnicodeString(runtime, columnName);
164
+ if ( primaryKeys.contains(rubyColumnName) ) continue;
165
+
166
+ // We are working on a new index
167
+ if ( ! indexName.equals(currentIndex) ) {
168
+ currentIndex = indexName;
169
+
170
+ String indexTableName = indexInfoSet.getString(INDEX_INFO_TABLE_NAME);
171
+
172
+ final boolean nonUnique = indexInfoSet.getBoolean(INDEX_INFO_NON_UNIQUE);
173
+
174
+ IRubyObject[] args = new IRubyObject[] {
175
+ RubyString.newUnicodeString(runtime, indexTableName), // table_name
176
+ RubyString.newUnicodeString(runtime, indexName), // index_name
177
+ runtime.newBoolean( ! nonUnique ), // unique
178
+ runtime.newArray() // [] for column names, we'll add to that in just a bit
179
+ // orders, (since AR 3.2) where, type, using (AR 4.0)
180
+ };
181
+
182
+ indexes.append( indexDefinition.callMethod(context, "new", args) ); // IndexDefinition.new
183
+ }
184
+
185
+ // One or more columns can be associated with an index
186
+ IRubyObject lastIndexDef = indexes.isEmpty() ? null : indexes.entry(-1);
187
+ if ( lastIndexDef != null ) {
188
+ ( (RubyArray) lastIndexDef.callMethod(context, "columns") ).append(rubyColumnName);
189
+ }
190
+ }
191
+
192
+ return indexes;
193
+
194
+ } finally { close(indexInfoSet); }
195
+ }
196
+ });
130
197
  }
131
198
 
132
199
  @Override
@@ -151,14 +218,10 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
151
218
  name = nameParts[2];
152
219
  }
153
220
 
154
- name = caseConvertIdentifierForJdbc(connection, name);
155
-
156
221
  if ( schema != null ) {
157
- schema = caseConvertIdentifierForJdbc(connection, schema);
158
222
  // NOTE: hack to work-around SQLite JDBC ignoring schema :
159
223
  return new TableName(catalog, null, schema + '.' + name);
160
224
  }
161
-
162
225
  return new TableName(catalog, schema, name);
163
226
  }
164
227
 
@@ -173,6 +236,13 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
173
236
  if ( resultSet instanceof ResultSetMetaData ) {
174
237
  type = ((ResultSetMetaData) resultSet).getColumnType(column);
175
238
  }
239
+ // since JDBC 3.8 there seems to be more cleverness built-in that
240
+ // seems (<= 3.8.7) to get things wrong ... reports DATE SQL type
241
+ // for "datetime" columns :
242
+ if ( type == Types.DATE ) {
243
+ // return timestampToRuby(context, runtime, resultSet, column);
244
+ return stringToRuby(context, runtime, resultSet, column);
245
+ }
176
246
  return super.jdbcToRuby(context, runtime, column, type, resultSet);
177
247
  }
178
248
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-jdbc-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.11
4
+ version: 1.3.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sieger, Ola Bini, Karol Bucek and JRuby contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-20 00:00:00.000000000 Z
11
+ date: 2014-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord