activerecord-jdbc-adapter 1.3.0.rc1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CONTRIBUTING.md +3 -5
  2. data/Gemfile +3 -5
  3. data/Gemfile.lock +1 -1
  4. data/History.md +30 -3
  5. data/README.md +14 -9
  6. data/gemfiles/rails23.gemfile +3 -0
  7. data/gemfiles/rails23.gemfile.lock +8 -0
  8. data/gemfiles/rails30.gemfile +3 -0
  9. data/gemfiles/rails30.gemfile.lock +11 -0
  10. data/gemfiles/rails31.gemfile +3 -0
  11. data/gemfiles/rails31.gemfile.lock +8 -0
  12. data/gemfiles/rails32.gemfile +3 -0
  13. data/gemfiles/rails32.gemfile.lock +11 -0
  14. data/gemfiles/rails40.gemfile +3 -0
  15. data/gemfiles/rails40.gemfile.lock +6 -0
  16. data/lib/arjdbc/db2/adapter.rb +39 -23
  17. data/lib/arjdbc/db2/column.rb +3 -3
  18. data/lib/arjdbc/derby/adapter.rb +45 -0
  19. data/lib/arjdbc/firebird/adapter.rb +38 -36
  20. data/lib/arjdbc/h2/adapter.rb +1 -1
  21. data/lib/arjdbc/hsqldb/adapter.rb +1 -0
  22. data/lib/arjdbc/hsqldb/explain_support.rb +6 -6
  23. data/lib/arjdbc/jdbc/adapter.rb +80 -39
  24. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  25. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -21
  26. data/lib/arjdbc/mssql/adapter.rb +41 -18
  27. data/lib/arjdbc/mssql/column.rb +3 -8
  28. data/lib/arjdbc/mssql/explain_support.rb +1 -1
  29. data/lib/arjdbc/mysql/adapter.rb +19 -9
  30. data/lib/arjdbc/mysql/column.rb +1 -1
  31. data/lib/arjdbc/mysql/connection_methods.rb +1 -0
  32. data/lib/arjdbc/mysql/explain_support.rb +2 -1
  33. data/lib/arjdbc/oracle/adapter.rb +42 -26
  34. data/lib/arjdbc/oracle/column.rb +1 -1
  35. data/lib/arjdbc/postgresql/adapter.rb +13 -4
  36. data/lib/arjdbc/sqlite3/adapter.rb +2 -0
  37. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +5 -5
  38. data/lib/arjdbc/util/serialized_attributes.rb +87 -0
  39. data/lib/arjdbc/version.rb +1 -1
  40. data/rakelib/02-test.rake +1 -1
  41. data/rakelib/db.rake +1 -1
  42. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +2 -2
  43. data/src/java/arjdbc/derby/DerbyModule.java +26 -173
  44. data/src/java/arjdbc/h2/H2Module.java +1 -0
  45. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +1 -2
  46. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +10 -9
  47. data/src/java/arjdbc/jdbc/AdapterJavaService.java +3 -3
  48. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +4 -3
  49. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +189 -70
  50. data/src/java/arjdbc/jdbc/SQLBlock.java +4 -4
  51. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +6 -7
  52. data/src/java/arjdbc/mysql/MySQLModule.java +1 -0
  53. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +14 -9
  54. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +19 -3
  55. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +305 -11
  56. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +3 -3
  57. metadata +6 -5
@@ -1,5 +1,5 @@
1
- /*
2
- **** BEGIN LICENSE BLOCK *****
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2013 Karol Bucek <self@kares.org>
3
3
  * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
4
  * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
5
  * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
@@ -41,5 +41,5 @@ public class AdapterJavaService implements BasicLibraryService {
41
41
  ArJdbcModule.load(runtime);
42
42
  return true;
43
43
  }
44
-
44
+
45
45
  }
@@ -1,4 +1,5 @@
1
1
  /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2013 Karol Bucek <self@kares.org>
2
3
  * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
3
4
  * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
4
5
  *
@@ -33,12 +34,12 @@ import java.sql.SQLException;
33
34
  * @author nicksieger
34
35
  */
35
36
  public interface JdbcConnectionFactory {
36
-
37
+
37
38
  /**
38
39
  * Retrieve a (new) connection from the factory.
39
40
  * @return a connection
40
- * @throws SQLException
41
+ * @throws SQLException
41
42
  */
42
43
  Connection newConnection() throws SQLException;
43
-
44
+
44
45
  }
@@ -1,5 +1,5 @@
1
- /*
2
- **** BEGIN LICENSE BLOCK *****
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2013 Karol Bucek <self@kares.org>
3
3
  * Copyright (c) 2006-2011 Nick Sieger <nick@nicksieger.com>
4
4
  * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
5
  * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
@@ -453,9 +453,8 @@ public class RubyJdbcConnection extends RubyObject {
453
453
  }
454
454
  }
455
455
  else {
456
- callMethod(context, "warn",
457
- context.getRuntime().newString("WARN: adapter not set for: " + inspect() +
458
- " make sure you pass it on initialize(config, adapter)"));
456
+ warn(context, "WARN: adapter not set for: " + inspect() +
457
+ " make sure you pass it on initialize(config, adapter)");
459
458
  }
460
459
  return jdbcConnection;
461
460
  }
@@ -542,7 +541,7 @@ public class RubyJdbcConnection extends RubyObject {
542
541
  try {
543
542
  statement = createStatement(context, connection);
544
543
  if ( doExecute(statement, query) ) {
545
- return mapResults(context, connection.getMetaData(), statement, false);
544
+ return mapResults(context, connection, statement, false);
546
545
  } else {
547
546
  return mapGeneratedKeysOrUpdateCount(context, connection, statement);
548
547
  }
@@ -791,7 +790,6 @@ public class RubyJdbcConnection extends RubyObject {
791
790
  return withConnection(context, new Callable<IRubyObject>() {
792
791
  public IRubyObject call(final Connection connection) throws SQLException {
793
792
  final Ruby runtime = context.getRuntime();
794
- final DatabaseMetaData metaData = connection.getMetaData();
795
793
 
796
794
  Statement statement = null; ResultSet resultSet = null;
797
795
  try {
@@ -811,10 +809,10 @@ public class RubyJdbcConnection extends RubyObject {
811
809
  if ( block != null && block.isGiven() ) {
812
810
  // yield(id1, name1) ... row 1 result data
813
811
  // yield(id2, name2) ... row 2 result data
814
- return yieldResultRows(context, runtime, metaData, resultSet, block);
812
+ return yieldResultRows(context, runtime, connection, resultSet, block);
815
813
  }
816
814
 
817
- return mapToRawResult(context, runtime, metaData, resultSet, false);
815
+ return mapToRawResult(context, runtime, connection, resultSet, false);
818
816
  }
819
817
  catch (final SQLException e) {
820
818
  debugErrorSQL(context, query);
@@ -938,9 +936,8 @@ public class RubyJdbcConnection extends RubyObject {
938
936
  private IRubyObject mapQueryResult(final ThreadContext context,
939
937
  final Connection connection, final ResultSet resultSet) throws SQLException {
940
938
  final Ruby runtime = context.getRuntime();
941
- final DatabaseMetaData metaData = connection.getMetaData();
942
- final ColumnData[] columns = setupColumns(runtime, metaData, resultSet.getMetaData(), false);
943
- return mapToResult(context, runtime, metaData, resultSet, columns);
939
+ final ColumnData[] columns = extractColumns(runtime, connection, resultSet, false);
940
+ return mapToResult(context, runtime, connection, resultSet, columns);
944
941
  }
945
942
 
946
943
  /**
@@ -976,10 +973,11 @@ public class RubyJdbcConnection extends RubyObject {
976
973
  @JRubyMethod(name = "supported_data_types")
977
974
  public IRubyObject supported_data_types(final ThreadContext context) throws SQLException {
978
975
  final Ruby runtime = context.getRuntime();
979
- final DatabaseMetaData metaData = getConnection(true).getMetaData();
980
- final IRubyObject types; final ResultSet typeDesc = metaData.getTypeInfo();
976
+ final Connection connection = getConnection(true);
977
+ final ResultSet typeDesc = connection.getMetaData().getTypeInfo();
978
+ final IRubyObject types;
981
979
  try {
982
- types = mapToRawResult(context, runtime, metaData, typeDesc, true);
980
+ types = mapToRawResult(context, runtime, connection, typeDesc, true);
983
981
  }
984
982
  finally { close(typeDesc); }
985
983
 
@@ -999,8 +997,8 @@ public class RubyJdbcConnection extends RubyObject {
999
997
  return withConnection(context, new Callable<List<RubyString>>() {
1000
998
  public List<RubyString> call(final Connection connection) throws SQLException {
1001
999
  final Ruby runtime = context.getRuntime();
1000
+ final String _tableName = caseConvertIdentifierForJdbc(connection, tableName);
1002
1001
  final DatabaseMetaData metaData = connection.getMetaData();
1003
- final String _tableName = caseConvertIdentifierForJdbc(metaData, tableName);
1004
1002
  ResultSet resultSet = null;
1005
1003
  final List<RubyString> keyNames = new ArrayList<RubyString>();
1006
1004
  try {
@@ -1009,7 +1007,7 @@ public class RubyJdbcConnection extends RubyObject {
1009
1007
 
1010
1008
  while (resultSet.next()) {
1011
1009
  String columnName = resultSet.getString(PRIMARY_KEYS_COLUMN_NAME);
1012
- columnName = caseConvertIdentifierForRails(metaData, columnName);
1010
+ columnName = caseConvertIdentifierForRails(connection, columnName);
1013
1011
  keyNames.add( RubyString.newUnicodeString(runtime, columnName) );
1014
1012
  }
1015
1013
  }
@@ -1156,9 +1154,9 @@ public class RubyJdbcConnection extends RubyObject {
1156
1154
  final Ruby runtime = context.getRuntime();
1157
1155
  final RubyClass indexDefinition = getIndexDefinition(runtime);
1158
1156
 
1157
+ String _tableName = caseConvertIdentifierForJdbc(connection, tableName);
1158
+ String _schemaName = caseConvertIdentifierForJdbc(connection, schemaName);
1159
1159
  final DatabaseMetaData metaData = connection.getMetaData();
1160
- String _tableName = caseConvertIdentifierForJdbc(metaData, tableName);
1161
- String _schemaName = caseConvertIdentifierForJdbc(metaData, schemaName);
1162
1160
 
1163
1161
  final List<RubyString> primaryKeys = primaryKeys(context, _tableName);
1164
1162
  ResultSet indexInfoSet = null;
@@ -1296,12 +1294,12 @@ public class RubyJdbcConnection extends RubyObject {
1296
1294
  column.callMethod(context, "type").toString() == (Object) "binary";
1297
1295
 
1298
1296
  final RubyClass recordClass = record.getMetaClass(); // record.class
1299
- final IRubyObject connection = record.callMethod(context, "connection");
1297
+ final IRubyObject adapter = recordClass.callMethod(context, "connection");
1300
1298
 
1301
1299
  IRubyObject columnName = column.callMethod(context, "name");
1302
- columnName = connection.callMethod(context, "quote_column_name", columnName);
1300
+ columnName = adapter.callMethod(context, "quote_column_name", columnName);
1303
1301
  IRubyObject tableName = recordClass.callMethod(context, "table_name");
1304
- tableName = connection.callMethod(context, "quote_table_name", tableName);
1302
+ tableName = adapter.callMethod(context, "quote_table_name", tableName);
1305
1303
  final IRubyObject idKey = recordClass.callMethod(context, "primary_key"); // 'id'
1306
1304
  // callMethod(context, "quote", primaryKey);
1307
1305
  final IRubyObject idColumn = // record.class.columns_hash['id']
@@ -1342,6 +1340,12 @@ public class RubyJdbcConnection extends RubyObject {
1342
1340
  });
1343
1341
  }
1344
1342
 
1343
+ protected String caseConvertIdentifierForRails(final Connection connection, final String value)
1344
+ throws SQLException {
1345
+ if ( value == null ) return null;
1346
+ return caseConvertIdentifierForRails(connection.getMetaData(), value);
1347
+ }
1348
+
1345
1349
  /**
1346
1350
  * Convert an identifier coming back from the database to a case which Rails is expecting.
1347
1351
  *
@@ -1356,16 +1360,21 @@ public class RubyJdbcConnection extends RubyObject {
1356
1360
  protected static String caseConvertIdentifierForRails(final DatabaseMetaData metaData, final String value)
1357
1361
  throws SQLException {
1358
1362
  if ( value == null ) return null;
1359
-
1360
1363
  return metaData.storesUpperCaseIdentifiers() ? value.toLowerCase() : value;
1361
1364
  }
1362
1365
 
1366
+ protected String caseConvertIdentifierForJdbc(final Connection connection, final String value)
1367
+ throws SQLException {
1368
+ if ( value == null ) return null;
1369
+ return caseConvertIdentifierForJdbc(connection.getMetaData(), value);
1370
+ }
1371
+
1363
1372
  /**
1364
1373
  * Convert an identifier destined for a method which cares about the databases internal
1365
1374
  * storage case. Methods like DatabaseMetaData.getPrimaryKeys() needs the table name to match
1366
- * the internal storage name. Arbtrary queries and the like DO NOT need to do this.
1375
+ * the internal storage name. Arbitrary queries and the like DO NOT need to do this.
1367
1376
  */
1368
- protected String caseConvertIdentifierForJdbc(final DatabaseMetaData metaData, final String value)
1377
+ protected static String caseConvertIdentifierForJdbc(final DatabaseMetaData metaData, final String value)
1369
1378
  throws SQLException {
1370
1379
  if ( value == null ) return null;
1371
1380
 
@@ -1375,7 +1384,6 @@ public class RubyJdbcConnection extends RubyObject {
1375
1384
  else if ( metaData.storesLowerCaseIdentifiers() ) {
1376
1385
  return value.toLowerCase();
1377
1386
  }
1378
-
1379
1387
  return value;
1380
1388
  }
1381
1389
 
@@ -1465,7 +1473,7 @@ public class RubyJdbcConnection extends RubyObject {
1465
1473
  * @throws SQLException
1466
1474
  */
1467
1475
  protected IRubyObject mapToResult(final ThreadContext context, final Ruby runtime,
1468
- final DatabaseMetaData metaData, final ResultSet resultSet,
1476
+ final Connection connection, final ResultSet resultSet,
1469
1477
  final ColumnData[] columns) throws SQLException {
1470
1478
 
1471
1479
  final ResultHandler resultHandler = ResultHandler.getInstance(runtime);
@@ -1635,20 +1643,29 @@ public class RubyJdbcConnection extends RubyObject {
1635
1643
  return runtime.getKernel().callMethod("BigDecimal", runtime.newString(value));
1636
1644
  }
1637
1645
 
1638
- protected static boolean rawDateTime = Boolean.getBoolean("arjdbc.datetime.raw");
1646
+ protected static Boolean rawDateTime;
1647
+ static {
1648
+ final String dateTimeRaw = System.getProperty("arjdbc.datetime.raw");
1649
+ if ( dateTimeRaw != null ) {
1650
+ rawDateTime = Boolean.parseBoolean(dateTimeRaw);
1651
+ }
1652
+ // NOTE: we do this since it will have a different value depending on
1653
+ // AR version - since 4.0 false by default otherwise will be true ...
1654
+ }
1639
1655
 
1640
- @JRubyMethod(name = "raw_date_time?")
1656
+ @JRubyMethod(name = "raw_date_time?", meta = true)
1641
1657
  public static IRubyObject useRawDateTime(final ThreadContext context, final IRubyObject self) {
1642
- return context.getRuntime().newBoolean(rawDateTime);
1658
+ if ( rawDateTime == null ) return context.getRuntime().getNil();
1659
+ return context.getRuntime().newBoolean( rawDateTime.booleanValue() );
1643
1660
  }
1644
1661
 
1645
- @JRubyMethod(name = "raw_date_time=")
1662
+ @JRubyMethod(name = "raw_date_time=", meta = true)
1646
1663
  public static IRubyObject setRawDateTime(final IRubyObject self, final IRubyObject value) {
1647
1664
  if ( value instanceof RubyBoolean ) {
1648
1665
  rawDateTime = ((RubyBoolean) value).isTrue();
1649
1666
  }
1650
1667
  else {
1651
- rawDateTime = value.isNil();
1668
+ rawDateTime = value.isNil() ? null : Boolean.TRUE;
1652
1669
  }
1653
1670
  return value;
1654
1671
  }
@@ -1664,7 +1681,7 @@ public class RubyJdbcConnection extends RubyObject {
1664
1681
  }
1665
1682
 
1666
1683
  final RubyString strValue = RubyString.newUnicodeString(runtime, value.toString());
1667
- if ( rawDateTime ) return strValue;
1684
+ if ( rawDateTime != null && rawDateTime.booleanValue() ) return strValue;
1668
1685
 
1669
1686
  final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
1670
1687
  if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
@@ -1682,7 +1699,7 @@ public class RubyJdbcConnection extends RubyObject {
1682
1699
  }
1683
1700
 
1684
1701
  final RubyString strValue = RubyString.newUnicodeString(runtime, value.toString());
1685
- if ( rawDateTime ) return strValue;
1702
+ if ( rawDateTime != null && rawDateTime.booleanValue() ) return strValue;
1686
1703
 
1687
1704
  final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
1688
1705
  if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
@@ -1700,7 +1717,7 @@ public class RubyJdbcConnection extends RubyObject {
1700
1717
  }
1701
1718
 
1702
1719
  final RubyString strValue = timestampToRubyString(runtime, value.toString());
1703
- if ( rawDateTime ) return strValue;
1720
+ if ( rawDateTime != null && rawDateTime.booleanValue() ) return strValue;
1704
1721
 
1705
1722
  final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
1706
1723
  if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
@@ -1728,9 +1745,39 @@ public class RubyJdbcConnection extends RubyObject {
1728
1745
  return timestampToRubyString(runtime, value.toString());
1729
1746
  }
1730
1747
 
1748
+ protected static Boolean rawBoolean;
1749
+ static {
1750
+ final String booleanRaw = System.getProperty("arjdbc.boolean.raw");
1751
+ if ( booleanRaw != null ) {
1752
+ rawBoolean = Boolean.parseBoolean(booleanRaw);
1753
+ }
1754
+ }
1755
+
1756
+ @JRubyMethod(name = "raw_boolean?", meta = true)
1757
+ public static IRubyObject useRawBoolean(final ThreadContext context, final IRubyObject self) {
1758
+ if ( rawBoolean == null ) return context.getRuntime().getNil();
1759
+ return context.getRuntime().newBoolean( rawBoolean.booleanValue() );
1760
+ }
1761
+
1762
+ @JRubyMethod(name = "raw_boolean=", meta = true)
1763
+ public static IRubyObject setRawBoolean(final IRubyObject self, final IRubyObject value) {
1764
+ if ( value instanceof RubyBoolean ) {
1765
+ rawBoolean = ((RubyBoolean) value).isTrue();
1766
+ }
1767
+ else {
1768
+ rawBoolean = value.isNil() ? null : Boolean.TRUE;
1769
+ }
1770
+ return value;
1771
+ }
1772
+
1731
1773
  protected IRubyObject booleanToRuby(final ThreadContext context,
1732
1774
  final Ruby runtime, final ResultSet resultSet, final int column)
1733
1775
  throws SQLException {
1776
+ if ( rawBoolean != null && rawBoolean.booleanValue() ) {
1777
+ final String value = resultSet.getString(column);
1778
+ if ( resultSet.wasNull() ) return runtime.getNil();
1779
+ return RubyString.newUnicodeString(runtime, value);
1780
+ }
1734
1781
  final boolean value = resultSet.getBoolean(column);
1735
1782
  if ( resultSet.wasNull() ) return runtime.getNil();
1736
1783
  return booleanToRuby(runtime, resultSet, value);
@@ -1863,15 +1910,7 @@ public class RubyJdbcConnection extends RubyObject {
1863
1910
  column = (IRubyObject) _param[0]; param = _param[1];
1864
1911
  }
1865
1912
 
1866
- final IRubyObject type;
1867
- if ( column != null && ! column.isNil() ) {
1868
- type = column.callMethod(context, "type");
1869
- }
1870
- else {
1871
- type = null;
1872
- }
1873
-
1874
- setStatementParameter(context, runtime, connection, statement, i + 1, param, type);
1913
+ setStatementParameter(context, runtime, connection, statement, i + 1, param, column);
1875
1914
  }
1876
1915
  }
1877
1916
 
@@ -1987,8 +2026,16 @@ public class RubyJdbcConnection extends RubyObject {
1987
2026
 
1988
2027
  final String internedType;
1989
2028
  if ( column != null && ! column.isNil() ) {
1990
- final RubySymbol columnType = resolveColumnType(context, runtime, column);
1991
- internedType = columnType.asJavaString();
2029
+ // NOTE: there's no ActiveRecord "convention" really for this ...
2030
+ // this is based on Postgre's initial support for arrays :
2031
+ // `column.type` contains the base type while there's `column.array?`
2032
+ if ( column.respondsTo("array?") && column.callMethod(context, "array?").isTrue() ) {
2033
+ internedType = "array";
2034
+ }
2035
+ else {
2036
+ final RubySymbol columnType = resolveColumnType(context, runtime, column);
2037
+ internedType = columnType.asJavaString();
2038
+ }
1992
2039
  }
1993
2040
  else {
1994
2041
  if ( value instanceof RubyInteger ) {
@@ -2452,7 +2499,7 @@ public class RubyJdbcConnection extends RubyObject {
2452
2499
  }
2453
2500
  }
2454
2501
 
2455
- /* protected */ void setArrayParameter(final ThreadContext context,
2502
+ protected void setArrayParameter(final ThreadContext context,
2456
2503
  final Connection connection, final PreparedStatement statement,
2457
2504
  final int index, final Object value,
2458
2505
  final IRubyObject column, final int type) throws SQLException {
@@ -2462,25 +2509,34 @@ public class RubyJdbcConnection extends RubyObject {
2462
2509
  else {
2463
2510
  if ( value == null ) statement.setNull(index, Types.ARRAY);
2464
2511
  else {
2465
- // TODO get array element type name ?!
2466
- Array array = connection.createArrayOf(null, (Object[]) value);
2512
+ String typeName = resolveArrayBaseTypeName(context, value, column, type);
2513
+ Array array = connection.createArrayOf(typeName, (Object[]) value);
2467
2514
  statement.setArray(index, array);
2468
2515
  }
2469
2516
  }
2470
2517
  }
2471
2518
 
2472
- /* protected */ void setArrayParameter(final ThreadContext context,
2519
+ protected void setArrayParameter(final ThreadContext context,
2473
2520
  final Connection connection, final PreparedStatement statement,
2474
2521
  final int index, final IRubyObject value,
2475
2522
  final IRubyObject column, final int type) throws SQLException {
2476
2523
  if ( value.isNil() ) statement.setNull(index, Types.ARRAY);
2477
2524
  else {
2478
- // TODO get array element type name ?!
2479
- Array array = connection.createArrayOf(null, ((RubyArray) value).toArray());
2525
+ String typeName = resolveArrayBaseTypeName(context, value, column, type);
2526
+ Array array = connection.createArrayOf(typeName, ((RubyArray) value).toArray());
2480
2527
  statement.setArray(index, array);
2481
2528
  }
2482
2529
  }
2483
2530
 
2531
+ protected String resolveArrayBaseTypeName(final ThreadContext context,
2532
+ final Object value, final IRubyObject column, final int type) {
2533
+ // return column.callMethod(context, "sql_type").toString();
2534
+ String sqlType = column.callMethod(context, "sql_type").toString();
2535
+ final int index = sqlType.indexOf('('); // e.g. "character varying(255)"
2536
+ if ( index > 0 ) sqlType = sqlType.substring(0, index);
2537
+ return sqlType;
2538
+ }
2539
+
2484
2540
  protected void setXmlParameter(final ThreadContext context,
2485
2541
  final Connection connection, final PreparedStatement statement,
2486
2542
  final int index, final Object value,
@@ -2637,6 +2693,14 @@ public class RubyJdbcConnection extends RubyObject {
2637
2693
  debugMessage(context, "connection considered broken due: " + e.toString());
2638
2694
  return false;
2639
2695
  }
2696
+ catch (AbstractMethodError e) { // non-JDBC 4.0 driver
2697
+ warn( context,
2698
+ "WARN: driver does not support checking if connection isValid()" +
2699
+ " please make sure you're using a JDBC 4.0 compilant driver or" +
2700
+ " set `connection_alive_sql: ...` in your database configuration" );
2701
+ debugStackTrace(context, e);
2702
+ throw e;
2703
+ }
2640
2704
  finally { close(statement); }
2641
2705
  }
2642
2706
 
@@ -2671,11 +2735,10 @@ public class RubyJdbcConnection extends RubyObject {
2671
2735
  final String tablePattern, final String[] types,
2672
2736
  final boolean checkExistsOnly) throws SQLException {
2673
2737
 
2738
+ final String _tablePattern = caseConvertIdentifierForJdbc(connection, tablePattern);
2739
+ final String _schemaPattern = caseConvertIdentifierForJdbc(connection, schemaPattern);
2674
2740
  final DatabaseMetaData metaData = connection.getMetaData();
2675
2741
 
2676
- final String _tablePattern = caseConvertIdentifierForJdbc(metaData, tablePattern);
2677
- final String _schemaPattern = caseConvertIdentifierForJdbc(metaData, schemaPattern);
2678
-
2679
2742
  ResultSet tablesSet = null;
2680
2743
  try {
2681
2744
  tablesSet = metaData.getTables(catalog, _schemaPattern, _tablePattern, types);
@@ -2705,13 +2768,16 @@ public class RubyJdbcConnection extends RubyObject {
2705
2768
  * @return List<RubyString>
2706
2769
  * @throws SQLException
2707
2770
  */
2771
+ // NOTE: change to accept a connection instead of meta-data
2708
2772
  protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
2709
2773
  final String catalog, final String schemaPattern, final String tablePattern,
2710
2774
  final ResultSet tablesSet) throws SQLException {
2711
2775
  final RubyArray tables = runtime.newArray();
2712
2776
  while ( tablesSet.next() ) {
2713
2777
  String name = tablesSet.getString(TABLES_TABLE_NAME);
2778
+
2714
2779
  name = caseConvertIdentifierForRails(metaData, name);
2780
+
2715
2781
  tables.add(RubyString.newUnicodeString(runtime, name));
2716
2782
  }
2717
2783
  return tables;
@@ -2920,14 +2986,14 @@ public class RubyJdbcConnection extends RubyObject {
2920
2986
  }
2921
2987
 
2922
2988
  protected IRubyObject mapResults(final ThreadContext context,
2923
- final DatabaseMetaData metaData, final Statement statement,
2989
+ final Connection connection, final Statement statement,
2924
2990
  final boolean downCase) throws SQLException {
2925
2991
 
2926
2992
  final Ruby runtime = context.getRuntime();
2927
2993
  IRubyObject result;
2928
2994
  ResultSet resultSet = statement.getResultSet();
2929
2995
  try {
2930
- result = mapToRawResult(context, runtime, metaData, resultSet, downCase);
2996
+ result = mapToRawResult(context, runtime, connection, resultSet, downCase);
2931
2997
  }
2932
2998
  finally { close(resultSet); }
2933
2999
 
@@ -2939,7 +3005,7 @@ public class RubyJdbcConnection extends RubyObject {
2939
3005
  do {
2940
3006
  resultSet = statement.getResultSet();
2941
3007
  try {
2942
- result = mapToRawResult(context, runtime, metaData, resultSet, downCase);
3008
+ result = mapToRawResult(context, runtime, connection, resultSet, downCase);
2943
3009
  }
2944
3010
  finally { close(resultSet); }
2945
3011
 
@@ -2966,6 +3032,20 @@ public class RubyJdbcConnection extends RubyObject {
2966
3032
  * @param downCase should column names only be in lower case?
2967
3033
  */
2968
3034
  @SuppressWarnings("unchecked")
3035
+ private IRubyObject mapToRawResult(final ThreadContext context, final Ruby runtime,
3036
+ final Connection connection, final ResultSet resultSet,
3037
+ final boolean downCase) throws SQLException {
3038
+
3039
+ final ColumnData[] columns = extractColumns(runtime, connection, resultSet, downCase);
3040
+
3041
+ final RubyArray results = runtime.newArray();
3042
+ // [ { 'col1': 1, 'col2': 2 }, { 'col1': 3, 'col2': 4 } ]
3043
+ populateFromResultSet(context, runtime, (List<IRubyObject>) results, resultSet, columns);
3044
+ return results;
3045
+ }
3046
+
3047
+ @Deprecated
3048
+ @SuppressWarnings("unchecked")
2969
3049
  private IRubyObject mapToRawResult(final ThreadContext context, final Ruby runtime,
2970
3050
  final DatabaseMetaData metaData, final ResultSet resultSet,
2971
3051
  final boolean downCase) throws SQLException {
@@ -2979,10 +3059,10 @@ public class RubyJdbcConnection extends RubyObject {
2979
3059
  }
2980
3060
 
2981
3061
  private IRubyObject yieldResultRows(final ThreadContext context, final Ruby runtime,
2982
- final DatabaseMetaData metaData, final ResultSet resultSet,
3062
+ final Connection connection, final ResultSet resultSet,
2983
3063
  final Block block) throws SQLException {
2984
3064
 
2985
- final ColumnData[] columns = extractColumns(runtime, metaData, resultSet, false);
3065
+ final ColumnData[] columns = extractColumns(runtime, connection, resultSet, false);
2986
3066
 
2987
3067
  final IRubyObject[] blockArgs = new IRubyObject[columns.length];
2988
3068
  while ( resultSet.next() ) {
@@ -3005,6 +3085,13 @@ public class RubyJdbcConnection extends RubyObject {
3005
3085
  * @return columns data
3006
3086
  * @throws SQLException
3007
3087
  */
3088
+ protected ColumnData[] extractColumns(final Ruby runtime,
3089
+ final Connection connection, final ResultSet resultSet,
3090
+ final boolean downCase) throws SQLException {
3091
+ return setupColumns(runtime, connection, resultSet.getMetaData(), downCase);
3092
+ }
3093
+
3094
+ @Deprecated
3008
3095
  protected ColumnData[] extractColumns(final Ruby runtime,
3009
3096
  final DatabaseMetaData metaData, final ResultSet resultSet,
3010
3097
  final boolean downCase) throws SQLException {
@@ -3358,12 +3445,10 @@ public class RubyJdbcConnection extends RubyObject {
3358
3445
  name = nameParts[2];
3359
3446
  }
3360
3447
 
3361
- final DatabaseMetaData metaData = connection.getMetaData();
3362
-
3363
3448
  if (schema != null) {
3364
- schema = caseConvertIdentifierForJdbc(metaData, schema);
3449
+ schema = caseConvertIdentifierForJdbc(connection, schema);
3365
3450
  }
3366
- name = caseConvertIdentifierForJdbc(metaData, name);
3451
+ name = caseConvertIdentifierForJdbc(connection, name);
3367
3452
 
3368
3453
  if (schema != null && ! databaseSupportsSchemas()) {
3369
3454
  catalog = schema;
@@ -3395,11 +3480,16 @@ public class RubyJdbcConnection extends RubyObject {
3395
3480
  this.index = idx;
3396
3481
  }
3397
3482
 
3483
+ @Override
3484
+ public String toString() {
3485
+ return "'" + name + "'i" + index + "t" + type + "";
3486
+ }
3487
+
3398
3488
  }
3399
3489
 
3400
- private static ColumnData[] setupColumns(
3490
+ private ColumnData[] setupColumns(
3401
3491
  final Ruby runtime,
3402
- final DatabaseMetaData metaData,
3492
+ final Connection connection,
3403
3493
  final ResultSetMetaData resultMetaData,
3404
3494
  final boolean downCase) throws SQLException {
3405
3495
 
@@ -3407,14 +3497,39 @@ public class RubyJdbcConnection extends RubyObject {
3407
3497
  final ColumnData[] columns = new ColumnData[columnCount];
3408
3498
 
3409
3499
  for ( int i = 1; i <= columnCount; i++ ) { // metadata is one-based
3410
- final String name;
3411
- if (downCase) {
3412
- name = resultMetaData.getColumnLabel(i).toLowerCase();
3500
+ String name = resultMetaData.getColumnLabel(i);
3501
+ if ( downCase ) {
3502
+ name = name.toLowerCase();
3413
3503
  } else {
3414
- name = caseConvertIdentifierForRails(metaData, resultMetaData.getColumnLabel(i));
3504
+ name = caseConvertIdentifierForRails(connection, name);
3415
3505
  }
3506
+ final RubyString columnName = RubyString.newUnicodeString(runtime, name);
3416
3507
  final int columnType = resultMetaData.getColumnType(i);
3508
+ columns[i - 1] = new ColumnData(columnName, columnType, i);
3509
+ }
3510
+
3511
+ return columns;
3512
+ }
3513
+
3514
+ @Deprecated
3515
+ private ColumnData[] setupColumns(
3516
+ final Ruby runtime,
3517
+ final DatabaseMetaData metaData,
3518
+ final ResultSetMetaData resultMetaData,
3519
+ final boolean downCase) throws SQLException {
3520
+
3521
+ final int columnCount = resultMetaData.getColumnCount();
3522
+ final ColumnData[] columns = new ColumnData[columnCount];
3523
+
3524
+ for ( int i = 1; i <= columnCount; i++ ) { // metadata is one-based
3525
+ String name = resultMetaData.getColumnLabel(i);
3526
+ if ( downCase ) {
3527
+ name = name.toLowerCase();
3528
+ } else {
3529
+ name = caseConvertIdentifierForRails(metaData, name);
3530
+ }
3417
3531
  final RubyString columnName = RubyString.newUnicodeString(runtime, name);
3532
+ final int columnType = resultMetaData.getColumnType(i);
3418
3533
  columns[i - 1] = new ColumnData(columnName, columnType, i);
3419
3534
  }
3420
3535
 
@@ -3491,6 +3606,10 @@ public class RubyJdbcConnection extends RubyObject {
3491
3606
  }
3492
3607
  }
3493
3608
 
3609
+ protected void warn(final ThreadContext context, final String message) {
3610
+ callMethod(context, "warn", context.getRuntime().newString(message));
3611
+ }
3612
+
3494
3613
  private static RubyArray createCallerBacktrace(final ThreadContext context) {
3495
3614
  final Ruby runtime = context.getRuntime();
3496
3615
  runtime.incrementCallerCount();