activerecord-jdbc-adapter 1.3.11 → 1.3.12

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.
@@ -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