activerecord-jdbc-adapter 1.3.0.beta2 → 1.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. data/.gitignore +14 -8
  2. data/.travis.yml +40 -31
  3. data/.yardopts +4 -0
  4. data/Appraisals +2 -5
  5. data/CONTRIBUTING.md +46 -0
  6. data/Gemfile +21 -4
  7. data/Gemfile.lock +42 -17
  8. data/{History.txt → History.md} +142 -75
  9. data/README.md +102 -104
  10. data/RUNNING_TESTS.md +76 -0
  11. data/Rakefile.jdbc +20 -0
  12. data/activerecord-jdbc-adapter.gemspec +35 -18
  13. data/gemfiles/rails23.gemfile +4 -3
  14. data/gemfiles/rails23.gemfile.lock +9 -6
  15. data/gemfiles/rails30.gemfile +4 -3
  16. data/gemfiles/rails30.gemfile.lock +9 -6
  17. data/gemfiles/rails31.gemfile +4 -3
  18. data/gemfiles/rails31.gemfile.lock +9 -6
  19. data/gemfiles/rails32.gemfile +4 -3
  20. data/gemfiles/rails32.gemfile.lock +17 -14
  21. data/gemfiles/rails40.gemfile +5 -5
  22. data/gemfiles/rails40.gemfile.lock +17 -69
  23. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  25. data/lib/arel/visitors/compat.rb +22 -3
  26. data/lib/arel/visitors/db2.rb +8 -4
  27. data/lib/arel/visitors/derby.rb +14 -13
  28. data/lib/arel/visitors/firebird.rb +5 -4
  29. data/lib/arel/visitors/hsqldb.rb +11 -9
  30. data/lib/arel/visitors/sql_server.rb +89 -61
  31. data/lib/arjdbc.rb +1 -1
  32. data/lib/arjdbc/db2/adapter.rb +181 -212
  33. data/lib/arjdbc/db2/as400.rb +31 -18
  34. data/lib/arjdbc/db2/column.rb +167 -0
  35. data/lib/arjdbc/db2/connection_methods.rb +2 -0
  36. data/lib/arjdbc/derby/adapter.rb +206 -107
  37. data/lib/arjdbc/derby/connection_methods.rb +4 -9
  38. data/lib/arjdbc/firebird.rb +1 -0
  39. data/lib/arjdbc/firebird/adapter.rb +202 -64
  40. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  41. data/lib/arjdbc/h2/adapter.rb +56 -36
  42. data/lib/arjdbc/hsqldb/adapter.rb +99 -68
  43. data/lib/arjdbc/jdbc/adapter.rb +474 -265
  44. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  45. data/lib/arjdbc/jdbc/adapter_require.rb +8 -7
  46. data/lib/arjdbc/jdbc/arel_support.rb +132 -0
  47. data/lib/arjdbc/jdbc/base_ext.rb +8 -7
  48. data/lib/arjdbc/jdbc/callbacks.rb +16 -10
  49. data/lib/arjdbc/jdbc/column.rb +25 -3
  50. data/lib/arjdbc/jdbc/connection.rb +28 -55
  51. data/lib/arjdbc/jdbc/extension.rb +14 -14
  52. data/lib/arjdbc/jdbc/java.rb +6 -3
  53. data/lib/arjdbc/jdbc/jdbc.rake +1 -1
  54. data/lib/arjdbc/jdbc/quoted_primary_key.rb +2 -2
  55. data/lib/arjdbc/jdbc/rake_tasks.rb +1 -1
  56. data/lib/arjdbc/jdbc/type_converter.rb +5 -2
  57. data/lib/arjdbc/mssql/adapter.rb +160 -280
  58. data/lib/arjdbc/mssql/column.rb +182 -0
  59. data/lib/arjdbc/mssql/connection_methods.rb +37 -4
  60. data/lib/arjdbc/mssql/explain_support.rb +13 -21
  61. data/lib/arjdbc/mssql/limit_helpers.rb +79 -42
  62. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  63. data/lib/arjdbc/mssql/utils.rb +11 -11
  64. data/lib/arjdbc/mysql/adapter.rb +165 -247
  65. data/lib/arjdbc/mysql/column.rb +123 -0
  66. data/lib/arjdbc/mysql/connection_methods.rb +3 -6
  67. data/lib/arjdbc/oracle/adapter.rb +282 -288
  68. data/lib/arjdbc/oracle/column.rb +122 -0
  69. data/lib/arjdbc/oracle/connection_methods.rb +3 -0
  70. data/lib/arjdbc/postgresql/adapter.rb +336 -574
  71. data/lib/arjdbc/postgresql/column.rb +458 -0
  72. data/lib/arjdbc/postgresql/connection_methods.rb +1 -2
  73. data/lib/arjdbc/postgresql/schema_creation.rb +38 -0
  74. data/lib/arjdbc/sqlite3/adapter.rb +189 -145
  75. data/lib/arjdbc/sqlite3/explain_support.rb +1 -1
  76. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +8 -8
  77. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  78. data/lib/arjdbc/util/table_copier.rb +110 -0
  79. data/lib/arjdbc/version.rb +6 -7
  80. data/pom.xml +56 -2
  81. data/rakelib/02-test.rake +72 -83
  82. data/rakelib/db.rake +29 -17
  83. data/src/java/arjdbc/ArJdbcModule.java +21 -18
  84. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +84 -12
  85. data/src/java/arjdbc/derby/DerbyModule.java +140 -143
  86. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +58 -7
  87. data/src/java/arjdbc/h2/H2Module.java +43 -0
  88. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +7 -6
  89. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1223 -648
  90. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +24 -23
  91. data/src/java/arjdbc/mysql/MySQLModule.java +33 -32
  92. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +147 -30
  93. data/src/java/arjdbc/oracle/OracleModule.java +13 -13
  94. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +114 -6
  95. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +166 -36
  96. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +101 -19
  97. data/src/java/arjdbc/util/QuotingUtils.java +19 -19
  98. metadata +240 -394
  99. data/bench/bench_attributes.rb +0 -13
  100. data/bench/bench_attributes_new.rb +0 -14
  101. data/bench/bench_create.rb +0 -12
  102. data/bench/bench_find_all.rb +0 -12
  103. data/bench/bench_find_all_mt.rb +0 -25
  104. data/bench/bench_model.rb +0 -85
  105. data/bench/bench_new.rb +0 -12
  106. data/bench/bench_new_valid.rb +0 -12
  107. data/bench/bench_valid.rb +0 -13
  108. data/lib/arel/engines/sql/compilers/db2_compiler.rb +0 -9
  109. data/lib/arel/engines/sql/compilers/derby_compiler.rb +0 -6
  110. data/lib/arel/engines/sql/compilers/h2_compiler.rb +0 -6
  111. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +0 -15
  112. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +0 -6
  113. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +0 -46
  114. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +0 -98
  115. data/lib/arjdbc/mssql/lock_helpers.rb +0 -76
  116. data/lib/arjdbc/mssql/tsql_methods.rb +0 -58
  117. data/lib/arjdbc/postgresql/column_cast.rb +0 -134
  118. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +0 -25
  119. data/test/activerecord/jall.sh +0 -7
  120. data/test/activerecord/jtest.sh +0 -3
  121. data/test/assets/flowers.jpg +0 -0
  122. data/test/binary.rb +0 -67
  123. data/test/db/db2.rb +0 -43
  124. data/test/db/db2/binary_test.rb +0 -6
  125. data/test/db/db2/has_many_through_test.rb +0 -6
  126. data/test/db/db2/rake_test.rb +0 -82
  127. data/test/db/db2/rake_test_data.sql +0 -35
  128. data/test/db/db2/reset_column_information_test.rb +0 -5
  129. data/test/db/db2/serialize_test.rb +0 -6
  130. data/test/db/db2/simple_test.rb +0 -81
  131. data/test/db/db2/test_helper.rb +0 -6
  132. data/test/db/db2/unit_test.rb +0 -73
  133. data/test/db/derby.rb +0 -12
  134. data/test/db/derby/binary_test.rb +0 -6
  135. data/test/db/derby/migration_test.rb +0 -74
  136. data/test/db/derby/rake_test.rb +0 -96
  137. data/test/db/derby/reset_column_information_test.rb +0 -6
  138. data/test/db/derby/row_locking_test.rb +0 -20
  139. data/test/db/derby/schema_dump_test.rb +0 -5
  140. data/test/db/derby/serialize_test.rb +0 -6
  141. data/test/db/derby/simple_test.rb +0 -173
  142. data/test/db/derby/test_helper.rb +0 -6
  143. data/test/db/derby/unit_test.rb +0 -32
  144. data/test/db/derby/xml_column_test.rb +0 -17
  145. data/test/db/h2.rb +0 -11
  146. data/test/db/h2/binary_test.rb +0 -6
  147. data/test/db/h2/change_column_test.rb +0 -68
  148. data/test/db/h2/identity_column_test.rb +0 -35
  149. data/test/db/h2/offset_test.rb +0 -49
  150. data/test/db/h2/rake_test.rb +0 -98
  151. data/test/db/h2/schema_dump_test.rb +0 -29
  152. data/test/db/h2/serialize_test.rb +0 -6
  153. data/test/db/h2/simple_test.rb +0 -56
  154. data/test/db/hsqldb.rb +0 -11
  155. data/test/db/hsqldb/binary_test.rb +0 -6
  156. data/test/db/hsqldb/rake_test.rb +0 -101
  157. data/test/db/hsqldb/schema_dump_test.rb +0 -19
  158. data/test/db/hsqldb/serialize_test.rb +0 -6
  159. data/test/db/hsqldb/simple_test.rb +0 -17
  160. data/test/db/informix.rb +0 -13
  161. data/test/db/jdbc.rb +0 -16
  162. data/test/db/jdbc_derby.rb +0 -14
  163. data/test/db/jdbc_h2.rb +0 -17
  164. data/test/db/jdbc_mysql.rb +0 -13
  165. data/test/db/jdbc_postgres.rb +0 -23
  166. data/test/db/jndi_config.rb +0 -32
  167. data/test/db/jndi_pooled_config.rb +0 -32
  168. data/test/db/mssql.rb +0 -11
  169. data/test/db/mssql/binary_test.rb +0 -6
  170. data/test/db/mssql/exec_proc_test.rb +0 -46
  171. data/test/db/mssql/identity_insert_test.rb +0 -18
  172. data/test/db/mssql/ignore_system_views_test.rb +0 -40
  173. data/test/db/mssql/limit_offset_test.rb +0 -190
  174. data/test/db/mssql/multibyte_test.rb +0 -16
  175. data/test/db/mssql/multiple_connections_test.rb +0 -71
  176. data/test/db/mssql/rake_test.rb +0 -143
  177. data/test/db/mssql/reset_column_information_test.rb +0 -6
  178. data/test/db/mssql/row_locking_test.rb +0 -7
  179. data/test/db/mssql/serialize_test.rb +0 -6
  180. data/test/db/mssql/simple_test.rb +0 -140
  181. data/test/db/mssql/transaction_test.rb +0 -6
  182. data/test/db/mssql/types_test.rb +0 -205
  183. data/test/db/mssql/unit_test.rb +0 -249
  184. data/test/db/mysql.rb +0 -4
  185. data/test/db/mysql/_rails_test_mysql.32.out +0 -6585
  186. data/test/db/mysql/binary_test.rb +0 -6
  187. data/test/db/mysql/connection_test.rb +0 -51
  188. data/test/db/mysql/index_length_test.rb +0 -58
  189. data/test/db/mysql/multibyte_test.rb +0 -10
  190. data/test/db/mysql/nonstandard_primary_key_test.rb +0 -39
  191. data/test/db/mysql/rake_test.rb +0 -97
  192. data/test/db/mysql/reset_column_information_test.rb +0 -6
  193. data/test/db/mysql/schema_dump_test.rb +0 -228
  194. data/test/db/mysql/serialize_test.rb +0 -6
  195. data/test/db/mysql/simple_test.rb +0 -187
  196. data/test/db/mysql/statement_escaping_test.rb +0 -46
  197. data/test/db/mysql/transaction_test.rb +0 -6
  198. data/test/db/mysql/types_test.rb +0 -30
  199. data/test/db/mysql/unit_test.rb +0 -93
  200. data/test/db/mysql_config.rb +0 -7
  201. data/test/db/oracle.rb +0 -27
  202. data/test/db/oracle/binary_test.rb +0 -6
  203. data/test/db/oracle/limit_test.rb +0 -24
  204. data/test/db/oracle/multibyte_test.rb +0 -22
  205. data/test/db/oracle/rake_test.rb +0 -100
  206. data/test/db/oracle/reset_column_information_test.rb +0 -6
  207. data/test/db/oracle/serialize_test.rb +0 -6
  208. data/test/db/oracle/simple_test.rb +0 -140
  209. data/test/db/oracle/specific_test.rb +0 -180
  210. data/test/db/oracle/transaction_test.rb +0 -31
  211. data/test/db/oracle/unit_test.rb +0 -31
  212. data/test/db/postgres.rb +0 -11
  213. data/test/db/postgres/_rails_test_postgres.32.out +0 -6405
  214. data/test/db/postgres/a_custom_primary_key_test.rb +0 -50
  215. data/test/db/postgres/active_schema_unit_test.rb +0 -68
  216. data/test/db/postgres/array_type_test.rb +0 -101
  217. data/test/db/postgres/binary_test.rb +0 -6
  218. data/test/db/postgres/connection_test.rb +0 -63
  219. data/test/db/postgres/data_types_test.rb +0 -703
  220. data/test/db/postgres/hstore_test.rb +0 -200
  221. data/test/db/postgres/information_schema_leak_test.rb +0 -30
  222. data/test/db/postgres/json_test.rb +0 -86
  223. data/test/db/postgres/ltree_test.rb +0 -51
  224. data/test/db/postgres/mixed_case_test.rb +0 -29
  225. data/test/db/postgres/native_types_test.rb +0 -124
  226. data/test/db/postgres/rake_test.rb +0 -117
  227. data/test/db/postgres/reserved_test.rb +0 -22
  228. data/test/db/postgres/reset_column_information_test.rb +0 -6
  229. data/test/db/postgres/row_locking_test.rb +0 -21
  230. data/test/db/postgres/schema_dump_test.rb +0 -95
  231. data/test/db/postgres/schema_test.rb +0 -115
  232. data/test/db/postgres/simple_test.rb +0 -260
  233. data/test/db/postgres/table_alias_length_test.rb +0 -16
  234. data/test/db/postgres/transaction_test.rb +0 -6
  235. data/test/db/postgres/unit_test.rb +0 -31
  236. data/test/db/postgres_config.rb +0 -10
  237. data/test/db/sqlite3.rb +0 -6
  238. data/test/db/sqlite3/_rails_test_sqlite3.32.out +0 -6274
  239. data/test/db/sqlite3/has_many_though_test.rb +0 -6
  240. data/test/db/sqlite3/rake_test.rb +0 -71
  241. data/test/db/sqlite3/reset_column_information_test.rb +0 -6
  242. data/test/db/sqlite3/schema_dump_test.rb +0 -6
  243. data/test/db/sqlite3/serialize_test.rb +0 -6
  244. data/test/db/sqlite3/simple_test.rb +0 -268
  245. data/test/db/sqlite3/transaction_test.rb +0 -32
  246. data/test/db/sqlite3/type_conversion_test.rb +0 -104
  247. data/test/has_many_through.rb +0 -61
  248. data/test/informix_simple_test.rb +0 -48
  249. data/test/jdbc/db2.rb +0 -36
  250. data/test/jdbc/oracle.rb +0 -34
  251. data/test/jdbc_column_test.rb +0 -23
  252. data/test/jdbc_common.rb +0 -16
  253. data/test/jdbc_connection_test.rb +0 -196
  254. data/test/jndi_callbacks_test.rb +0 -33
  255. data/test/jndi_test.rb +0 -55
  256. data/test/manualTestDatabase.rb +0 -191
  257. data/test/models/add_not_null_column_to_table.rb +0 -9
  258. data/test/models/auto_id.rb +0 -15
  259. data/test/models/binary.rb +0 -18
  260. data/test/models/custom_pk_name.rb +0 -15
  261. data/test/models/data_types.rb +0 -40
  262. data/test/models/entry.rb +0 -41
  263. data/test/models/mixed_case.rb +0 -22
  264. data/test/models/reserved_word.rb +0 -15
  265. data/test/models/rights_and_roles.rb +0 -57
  266. data/test/models/string_id.rb +0 -17
  267. data/test/models/thing.rb +0 -17
  268. data/test/models/topic.rb +0 -32
  269. data/test/models/validates_uniqueness_of_string.rb +0 -19
  270. data/test/rails/mysql.rb +0 -13
  271. data/test/rails/sqlite3/version.rb +0 -6
  272. data/test/rails_stub.rb +0 -31
  273. data/test/rake_test_support.rb +0 -298
  274. data/test/row_locking.rb +0 -102
  275. data/test/schema_dump.rb +0 -182
  276. data/test/serialize.rb +0 -275
  277. data/test/shared_helper.rb +0 -35
  278. data/test/simple.rb +0 -1317
  279. data/test/sybase_jtds_simple_test.rb +0 -28
  280. data/test/sybase_reset_column_information_test.rb +0 -6
  281. data/test/test_helper.rb +0 -304
  282. data/test/transaction.rb +0 -109
@@ -23,21 +23,30 @@
23
23
  */
24
24
  package arjdbc.derby;
25
25
 
26
+ import arjdbc.jdbc.Callable;
27
+ import arjdbc.jdbc.RubyJdbcConnection;
28
+
26
29
  import java.sql.Connection;
30
+ import java.sql.PreparedStatement;
31
+ import java.sql.ResultSet;
27
32
  import java.sql.SQLException;
28
33
 
29
34
  import org.jruby.Ruby;
30
35
  import org.jruby.RubyClass;
36
+ import org.jruby.RubyString;
37
+ import org.jruby.anno.JRubyMethod;
31
38
  import org.jruby.runtime.ObjectAllocator;
39
+ import org.jruby.runtime.ThreadContext;
32
40
  import org.jruby.runtime.builtin.IRubyObject;
41
+ import org.jruby.util.ByteList;
33
42
 
34
43
  /**
35
44
  * ArJdbc::DerbyJdbcConnection
36
- *
45
+ *
37
46
  * @author kares
38
47
  */
39
- public class DerbyRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
40
-
48
+ public class DerbyRubyJdbcConnection extends RubyJdbcConnection {
49
+
41
50
  protected DerbyRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
42
51
  super(runtime, metaClass);
43
52
  }
@@ -54,17 +63,59 @@ public class DerbyRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
54
63
  return new DerbyRubyJdbcConnection(runtime, klass);
55
64
  }
56
65
  };
57
-
66
+
67
+ @JRubyMethod(name = "select?", required = 1, meta = true, frame = false)
68
+ public static IRubyObject select_p(final ThreadContext context,
69
+ final IRubyObject self, final IRubyObject sql) {
70
+ if ( isValues(sql.convertToString()) ) {
71
+ return context.getRuntime().newBoolean( true );
72
+ }
73
+ return arjdbc.jdbc.RubyJdbcConnection.select_p(context, self, sql);
74
+ }
75
+
76
+ // Derby supports 'stand-alone' VALUES expressions
77
+ private static final byte[] VALUES = new byte[]{ 'v','a','l','u', 'e', 's' };
78
+
79
+ private static boolean isValues(final RubyString sql) {
80
+ final ByteList sqlBytes = sql.getByteList();
81
+ return startsWithIgnoreCase(sqlBytes, VALUES);
82
+ }
83
+
84
+ @JRubyMethod(name = {"identity_val_local", "last_insert_id"})
85
+ public IRubyObject identity_val_local(final ThreadContext context)
86
+ throws SQLException {
87
+ return withConnection(context, new Callable<IRubyObject>() {
88
+ public IRubyObject call(final Connection connection) throws SQLException {
89
+ PreparedStatement statement = null; ResultSet genKeys = null;
90
+ try {
91
+ statement = connection.prepareStatement("values IDENTITY_VAL_LOCAL()");
92
+ genKeys = statement.executeQuery();
93
+ return doMapGeneratedKeys(context.getRuntime(), genKeys, true);
94
+ }
95
+ catch (final SQLException e) {
96
+ debugMessage(context, "failed to get generated keys: " + e.getMessage());
97
+ throw e;
98
+ }
99
+ finally { close(genKeys); close(statement); }
100
+ }
101
+ });
102
+ }
103
+
58
104
  @Override
59
- protected IRubyObject matchTables(final Ruby runtime,
105
+ protected boolean supportsGeneratedKeys(final Connection connection) throws SQLException {
106
+ return true; // driver reports false from supportsGetGeneratedKeys()
107
+ }
108
+
109
+ @Override
110
+ protected IRubyObject matchTables(final Ruby runtime,
60
111
  final Connection connection,
61
112
  final String catalog, String schemaPattern,
62
113
  final String tablePattern, final String[] types,
63
114
  final boolean checkExistsOnly) throws SQLException {
64
- if (schemaPattern != null && schemaPattern.equals("")) {
115
+ if (schemaPattern != null && schemaPattern.equals("")) {
65
116
  schemaPattern = null; // Derby doesn't like empty-string schema name
66
117
  }
67
118
  return super.matchTables(runtime, connection, catalog, schemaPattern, tablePattern, types, checkExistsOnly);
68
119
  }
69
-
120
+
70
121
  }
@@ -0,0 +1,43 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
3
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files (the
7
+ * "Software"), to deal in the Software without restriction, including
8
+ * without limitation the rights to use, copy, modify, merge, publish,
9
+ * distribute, sublicense, and/or sell copies of the Software, and to
10
+ * permit persons to whom the Software is furnished to do so, subject to
11
+ * the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ ***** END LICENSE BLOCK *****/
24
+
25
+ package arjdbc.h2;
26
+
27
+ import org.jruby.RubyModule;
28
+
29
+ /**
30
+ * ArJdbc::H2
31
+ *
32
+ * @author kares
33
+ */
34
+ public class H2Module {
35
+
36
+ public static RubyModule load(final RubyModule arJdbc) {
37
+ RubyModule h2 = arJdbc.defineModuleUnder("H2");
38
+ // NOTE: currently no Java implemented Ruby methods
39
+ // h2.defineAnnotatedMethods( H2Module.class );
40
+ return h2;
41
+ }
42
+
43
+ }
@@ -35,6 +35,7 @@ import arjdbc.jdbc.RubyJdbcConnection;
35
35
  import org.jruby.Ruby;
36
36
  import org.jruby.RubyClass;
37
37
  import org.jruby.runtime.ObjectAllocator;
38
+ import org.jruby.runtime.ThreadContext;
38
39
  import org.jruby.runtime.builtin.IRubyObject;
39
40
 
40
41
  /**
@@ -64,11 +65,11 @@ public class InformixRubyJdbcConnection extends RubyJdbcConnection {
64
65
  * Treat LONGVARCHAR as CLOB on Informix for purposes of converting a JDBC value to Ruby.
65
66
  */
66
67
  @Override
67
- protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
68
- throws SQLException {
69
- if (type == Types.LONGVARCHAR) {
70
- type = Types.CLOB;
71
- }
72
- return super.jdbcToRuby(runtime, column, type, resultSet);
68
+ protected IRubyObject jdbcToRuby(
69
+ final ThreadContext context, final Ruby runtime,
70
+ final int column, int type, final ResultSet resultSet)
71
+ throws SQLException {
72
+ if ( type == Types.LONGVARCHAR ) type = Types.CLOB;
73
+ return super.jdbcToRuby(context, runtime, column, type, resultSet);
73
74
  }
74
75
  }
@@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
29
29
  import java.io.IOException;
30
30
  import java.io.InputStream;
31
31
  import java.io.InputStreamReader;
32
+ import java.io.PrintStream;
32
33
  import java.io.Reader;
33
34
  import java.io.StringReader;
34
35
  import java.lang.reflect.InvocationTargetException;
@@ -45,13 +46,19 @@ import java.sql.SQLException;
45
46
  import java.sql.SQLXML;
46
47
  import java.sql.Statement;
47
48
  import java.sql.Date;
49
+ import java.sql.SQLFeatureNotSupportedException;
50
+ import java.sql.Savepoint;
48
51
  import java.sql.Time;
49
52
  import java.sql.Timestamp;
50
53
  import java.sql.Types;
51
54
  import java.util.ArrayList;
52
55
  import java.util.Calendar;
56
+ import java.util.LinkedHashMap;
53
57
  import java.util.List;
58
+ import java.util.Map;
59
+ import java.util.TimeZone;
54
60
 
61
+ import org.joda.time.DateTime;
55
62
  import org.jruby.Ruby;
56
63
  import org.jruby.RubyArray;
57
64
  import org.jruby.RubyBignum;
@@ -59,6 +66,7 @@ import org.jruby.RubyBoolean;
59
66
  import org.jruby.RubyClass;
60
67
  import org.jruby.RubyException;
61
68
  import org.jruby.RubyFixnum;
69
+ import org.jruby.RubyFloat;
62
70
  import org.jruby.RubyHash;
63
71
  import org.jruby.RubyIO;
64
72
  import org.jruby.RubyInteger;
@@ -84,10 +92,12 @@ import org.jruby.util.ByteList;
84
92
  * Part of our ActiveRecord::ConnectionAdapters::Connection impl.
85
93
  */
86
94
  public class RubyJdbcConnection extends RubyObject {
87
-
95
+
88
96
  private static final String[] TABLE_TYPE = new String[] { "TABLE" };
89
97
  private static final String[] TABLE_TYPES = new String[] { "TABLE", "VIEW", "SYNONYM" };
90
98
 
99
+ private JdbcConnectionFactory connectionFactory;
100
+
91
101
  protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
92
102
  super(runtime, metaClass);
93
103
  }
@@ -97,18 +107,18 @@ public class RubyJdbcConnection extends RubyObject {
97
107
  return new RubyJdbcConnection(runtime, klass);
98
108
  }
99
109
  };
100
-
110
+
101
111
  public static RubyClass createJdbcConnectionClass(final Ruby runtime) {
102
112
  RubyClass jdbcConnection = getConnectionAdapters(runtime).
103
113
  defineClassUnder("JdbcConnection", runtime.getObject(), JDBCCONNECTION_ALLOCATOR);
104
114
  jdbcConnection.defineAnnotatedMethods(RubyJdbcConnection.class);
105
115
  return jdbcConnection;
106
116
  }
107
-
117
+
108
118
  public static RubyClass getJdbcConnectionClass(final Ruby runtime) {
109
119
  return getConnectionAdapters(runtime).getClass("JdbcConnection");
110
120
  }
111
-
121
+
112
122
  /**
113
123
  * @param runtime
114
124
  * @return <code>ActiveRecord::ConnectionAdapters</code>
@@ -124,7 +134,15 @@ public class RubyJdbcConnection extends RubyObject {
124
134
  static RubyClass getResult(final Ruby runtime) {
125
135
  return runtime.getModule("ActiveRecord").getClass("Result");
126
136
  }
127
-
137
+
138
+ /**
139
+ * @param runtime
140
+ * @return <code>ActiveRecord::Base</code>
141
+ */
142
+ protected static RubyClass getBase(final Ruby runtime) {
143
+ return runtime.getModule("ActiveRecord").getClass("Base");
144
+ }
145
+
128
146
  /**
129
147
  * @param runtime
130
148
  * @return <code>ActiveRecord::ConnectionAdapters::IndexDefinition</code>
@@ -132,7 +150,7 @@ public class RubyJdbcConnection extends RubyObject {
132
150
  protected static RubyClass getIndexDefinition(final Ruby runtime) {
133
151
  return getConnectionAdapters(runtime).getClass("IndexDefinition");
134
152
  }
135
-
153
+
136
154
  /**
137
155
  * @param runtime
138
156
  * @return <code>ActiveRecord::JDBCError</code>
@@ -148,7 +166,7 @@ public class RubyJdbcConnection extends RubyObject {
148
166
  protected static RubyClass getConnectionNotEstablished(final Ruby runtime) {
149
167
  return runtime.getModule("ActiveRecord").getClass("ConnectionNotEstablished");
150
168
  }
151
-
169
+
152
170
  /**
153
171
  * NOTE: Only available since AR-4.0
154
172
  * @param runtime
@@ -157,7 +175,7 @@ public class RubyJdbcConnection extends RubyObject {
157
175
  protected static RubyClass getTransactionIsolationError(final Ruby runtime) {
158
176
  return (RubyClass) runtime.getModule("ActiveRecord").getConstant("TransactionIsolationError");
159
177
  }
160
-
178
+
161
179
  /**
162
180
  * @param runtime
163
181
  * @return <code>ActiveRecord::ConnectionAdapters::JdbcTypeConverter</code>
@@ -165,7 +183,7 @@ public class RubyJdbcConnection extends RubyObject {
165
183
  private static RubyClass getJdbcTypeConverter(final Ruby runtime) {
166
184
  return getConnectionAdapters(runtime).getClass("JdbcTypeConverter");
167
185
  }
168
-
186
+
169
187
  /*
170
188
  def transaction_isolation_levels
171
189
  {
@@ -174,20 +192,20 @@ public class RubyJdbcConnection extends RubyObject {
174
192
  repeatable_read: "REPEATABLE READ",
175
193
  serializable: "SERIALIZABLE"
176
194
  }
177
- end
195
+ end
178
196
  */
179
197
 
180
198
  public static int mapTransactionIsolationLevel(IRubyObject isolation) {
181
199
  if ( ! ( isolation instanceof RubySymbol ) ) {
182
- isolation = isolation.convertToString().callMethod("intern");
200
+ isolation = isolation.asString().callMethod("intern");
183
201
  }
184
-
202
+
185
203
  final Object isolationString = isolation.toString(); // RubySymbol.toString
186
204
  if ( isolationString == "read_uncommitted" ) return Connection.TRANSACTION_READ_UNCOMMITTED; // 1
187
205
  if ( isolationString == "read_committed" ) return Connection.TRANSACTION_READ_COMMITTED; // 2
188
206
  if ( isolationString == "repeatable_read" ) return Connection.TRANSACTION_REPEATABLE_READ; // 4
189
207
  if ( isolationString == "serializable" ) return Connection.TRANSACTION_SERIALIZABLE; // 8
190
-
208
+
191
209
  throw new IllegalArgumentException(
192
210
  "unexpected isolation level: " + isolation + " (" + isolationString + ")"
193
211
  );
@@ -197,7 +215,7 @@ public class RubyJdbcConnection extends RubyObject {
197
215
  public IRubyObject supports_transaction_isolation_p(final ThreadContext context,
198
216
  final IRubyObject[] args) throws SQLException {
199
217
  final IRubyObject isolation = args.length > 0 ? args[0] : null;
200
-
218
+
201
219
  return withConnection(context, new Callable<IRubyObject>() {
202
220
  public IRubyObject call(final Connection connection) throws SQLException {
203
221
  final DatabaseMetaData metaData = connection.getMetaData();
@@ -214,7 +232,7 @@ public class RubyJdbcConnection extends RubyObject {
214
232
  }
215
233
  });
216
234
  }
217
-
235
+
218
236
  @JRubyMethod(name = "begin", optional = 1) // optional isolation argument for AR-4.0
219
237
  public IRubyObject begin(final ThreadContext context, final IRubyObject[] args) {
220
238
  final IRubyObject isolation = args.length > 0 ? args[0] : null;
@@ -222,11 +240,12 @@ public class RubyJdbcConnection extends RubyObject {
222
240
  return withConnection(context, false, new Callable<IRubyObject>() {
223
241
  public IRubyObject call(final Connection connection) throws SQLException {
224
242
  connection.setAutoCommit(false);
243
+
225
244
  if ( isolation != null && ! isolation.isNil() ) {
226
245
  final int level = mapTransactionIsolationLevel(isolation);
227
246
  try {
228
247
  connection.setTransactionIsolation(level);
229
- }
248
+ }
230
249
  catch (SQLException e) {
231
250
  RubyClass txError = getTransactionIsolationError(context.getRuntime());
232
251
  if ( txError != null ) throw wrapException(context, txError, e);
@@ -241,7 +260,7 @@ public class RubyJdbcConnection extends RubyObject {
241
260
  return handleException(context, e);
242
261
  }
243
262
  }
244
-
263
+
245
264
  @JRubyMethod(name = "commit")
246
265
  public IRubyObject commit(final ThreadContext context) {
247
266
  final Connection connection = getConnection(true);
@@ -249,6 +268,7 @@ public class RubyJdbcConnection extends RubyObject {
249
268
  if ( ! connection.getAutoCommit() ) {
250
269
  try {
251
270
  connection.commit();
271
+ resetSavepoints(context); // if any
252
272
  return context.getRuntime().newBoolean(true);
253
273
  }
254
274
  finally {
@@ -269,6 +289,7 @@ public class RubyJdbcConnection extends RubyObject {
269
289
  if ( ! connection.getAutoCommit() ) {
270
290
  try {
271
291
  connection.rollback();
292
+ resetSavepoints(context); // if any
272
293
  return context.getRuntime().newBoolean(true);
273
294
  } finally {
274
295
  connection.setAutoCommit(true);
@@ -280,10 +301,168 @@ public class RubyJdbcConnection extends RubyObject {
280
301
  return handleException(context, e);
281
302
  }
282
303
  }
283
-
304
+
305
+ @JRubyMethod(name = "supports_savepoints?")
306
+ public IRubyObject supports_savepoints_p(final ThreadContext context) throws SQLException {
307
+ return withConnection(context, new Callable<IRubyObject>() {
308
+ public IRubyObject call(final Connection connection) throws SQLException {
309
+ final DatabaseMetaData metaData = connection.getMetaData();
310
+ return context.getRuntime().newBoolean( metaData.supportsSavepoints() );
311
+ }
312
+ });
313
+ }
314
+
315
+ @JRubyMethod(name = "create_savepoint", optional = 1)
316
+ public IRubyObject create_savepoint(final ThreadContext context, final IRubyObject[] args) {
317
+ IRubyObject name = args.length > 0 ? args[0] : null;
318
+ final Connection connection = getConnection(true);
319
+ try {
320
+ connection.setAutoCommit(false);
321
+
322
+ final Savepoint savepoint ;
323
+ // NOTE: this will auto-start a DB transaction even invoked outside
324
+ // of a AR (Ruby) transaction (`transaction { ... create_savepoint }`)
325
+ // it would be nice if AR knew about this TX although that's kind of
326
+ // "really advanced" functionality - likely not to be implemented ...
327
+ if ( name != null && ! name.isNil() ) {
328
+ savepoint = connection.setSavepoint(name.toString());
329
+ }
330
+ else {
331
+ savepoint = connection.setSavepoint();
332
+ name = RubyString.newString( context.getRuntime(),
333
+ Integer.toString( savepoint.getSavepointId() )
334
+ );
335
+ }
336
+ getSavepoints(context).put(name, savepoint);
337
+
338
+ return name;
339
+ }
340
+ catch (SQLException e) {
341
+ return handleException(context, e);
342
+ }
343
+ }
344
+
345
+ @JRubyMethod(name = "rollback_savepoint", required = 1)
346
+ public IRubyObject rollback_savepoint(final ThreadContext context, final IRubyObject name) {
347
+ if ( name == null || name.isNil() ) {
348
+ throw context.getRuntime().newArgumentError("nil savepoint name given");
349
+ }
350
+ final Connection connection = getConnection(true);
351
+ try {
352
+ Savepoint savepoint = getSavepoints(context).get(name);
353
+ if ( savepoint == null ) {
354
+ throw context.getRuntime().newRuntimeError("could not rollback savepoint: '" + name + "' (not set)");
355
+ }
356
+ connection.rollback(savepoint);
357
+ return context.getRuntime().getNil();
358
+ }
359
+ catch (SQLException e) {
360
+ return handleException(context, e);
361
+ }
362
+ }
363
+
364
+ @JRubyMethod(name = "release_savepoint", required = 1)
365
+ public IRubyObject release_savepoint(final ThreadContext context, final IRubyObject name) {
366
+ if ( name == null || name.isNil() ) {
367
+ throw context.getRuntime().newArgumentError("nil savepoint name given");
368
+ }
369
+ final Connection connection = getConnection(true);
370
+ try {
371
+ Object savepoint = getSavepoints(context).remove(name);
372
+ if ( savepoint == null ) {
373
+ throw context.getRuntime().newRuntimeError("could not release savepoint: '" + name + "' (not set)");
374
+ }
375
+ // NOTE: RubyHash.remove does not convert to Java as get does :
376
+ if ( ! ( savepoint instanceof Savepoint ) ) {
377
+ savepoint = ((IRubyObject) savepoint).toJava(Savepoint.class);
378
+ }
379
+ connection.releaseSavepoint((Savepoint) savepoint);
380
+ return context.getRuntime().getNil();
381
+ }
382
+ catch (SQLException e) {
383
+ return handleException(context, e);
384
+ }
385
+ }
386
+
387
+ // NOTE: this is iternal API - not to be used by user-code !
388
+ @JRubyMethod(name = "marked_savepoint_names")
389
+ public IRubyObject marked_savepoint_names(final ThreadContext context) {
390
+ if ( hasInstanceVariable("@savepoints") ) {
391
+ Map<IRubyObject, Savepoint> savepoints = getSavepoints(context);
392
+ final RubyArray names = context.getRuntime().newArray();
393
+ for ( Map.Entry<IRubyObject, ?> entry : savepoints.entrySet() ) {
394
+ names.add( entry.getKey() ); // keys are RubyString instances
395
+ }
396
+ return names;
397
+ }
398
+ else {
399
+ return context.getRuntime().newEmptyArray();
400
+ }
401
+ }
402
+
403
+ @SuppressWarnings("unchecked")
404
+ protected Map<IRubyObject, Savepoint> getSavepoints(final ThreadContext context) {
405
+ if ( hasInstanceVariable("@savepoints") ) {
406
+ IRubyObject savepoints = getInstanceVariable("@savepoints");
407
+ return (Map<IRubyObject, Savepoint>) savepoints.toJava(Map.class);
408
+ }
409
+ else { // not using a RubyHash to preserve order on Ruby 1.8 as well :
410
+ Map<IRubyObject, Savepoint> savepoints = new LinkedHashMap<IRubyObject, Savepoint>(4);
411
+ setInstanceVariable("@savepoints", convertJavaToRuby(savepoints));
412
+ return savepoints;
413
+ }
414
+ }
415
+
416
+ protected boolean resetSavepoints(final ThreadContext context) {
417
+ if ( hasInstanceVariable("@savepoints") ) {
418
+ removeInstanceVariable("@savepoints");
419
+ return true;
420
+ }
421
+ return false;
422
+ }
423
+
424
+ @JRubyMethod(name = "connection_factory")
425
+ public IRubyObject connection_factory(final ThreadContext context) {
426
+ return convertJavaToRuby( getConnectionFactory() );
427
+ }
428
+
429
+ @JRubyMethod(name = "connection_factory=", required = 1)
430
+ public IRubyObject set_connection_factory(final ThreadContext context, final IRubyObject factory) {
431
+ setConnectionFactory( (JdbcConnectionFactory) factory.toJava(JdbcConnectionFactory.class) );
432
+ return context.getRuntime().getNil();
433
+ }
434
+
435
+ /**
436
+ * Called during <code>initialize</code> after the connection factory
437
+ * has been set to check if we can connect and/or perform any initialization
438
+ * necessary.
439
+ * <br/>
440
+ * NOTE: connection has not been configured at this point,
441
+ * nor should we retry - we're creating a brand new JDBC connection
442
+ *
443
+ * @param context
444
+ * @return connection
445
+ */
446
+ @JRubyMethod(name = "init_connection")
447
+ public IRubyObject init_connection(final ThreadContext context) throws SQLException {
448
+ final IRubyObject jdbcConnection = setConnection( newConnection() );
449
+ final IRubyObject adapter = callMethod("adapter"); // self.adapter
450
+ if ( ! adapter.isNil() ) {
451
+ if ( adapter.respondsTo("init_connection") ) {
452
+ adapter.callMethod(context, "init_connection", jdbcConnection);
453
+ }
454
+ }
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)"));
459
+ }
460
+ return jdbcConnection;
461
+ }
462
+
284
463
  @JRubyMethod(name = "connection")
285
464
  public IRubyObject connection(final ThreadContext context) {
286
- if ( getConnection(false) == null ) {
465
+ if ( getConnection(false) == null ) {
287
466
  synchronized (this) {
288
467
  if ( getConnection(false) == null ) {
289
468
  reconnect(context);
@@ -297,12 +476,12 @@ public class RubyJdbcConnection extends RubyObject {
297
476
  public IRubyObject active_p(final ThreadContext context) {
298
477
  IRubyObject connection = getInstanceVariable("@connection");
299
478
  if ( connection != null && ! connection.isNil() ) {
300
- return isConnectionValid(context, getConnection(false)) ?
479
+ return isConnectionValid(context, getConnection(false)) ?
301
480
  context.getRuntime().getTrue() : context.getRuntime().getFalse();
302
481
  }
303
482
  return context.getRuntime().getFalse();
304
483
  }
305
-
484
+
306
485
  @JRubyMethod(name = "disconnect!")
307
486
  public IRubyObject disconnect(final ThreadContext context) {
308
487
  // TODO: only here to try resolving multi-thread issues :
@@ -312,27 +491,27 @@ public class RubyJdbcConnection extends RubyObject {
312
491
  final List<?> backtrace = createCallerBacktrace(context);
313
492
  final Ruby runtime = context.getRuntime();
314
493
  runtime.getOut().println(this + " connection.disconnect! occured: ");
315
- for ( Object element : backtrace ) {
494
+ for ( Object element : backtrace ) {
316
495
  runtime.getOut().println(element);
317
496
  }
318
497
  runtime.getOut().flush();
319
498
  }
320
499
  return setConnection(null);
321
500
  }
322
-
501
+
323
502
  @JRubyMethod(name = "reconnect!")
324
503
  public IRubyObject reconnect(final ThreadContext context) {
325
504
  try {
326
- final Connection connection = getConnectionFactory().newConnection();
505
+ final Connection connection = newConnection();
327
506
  final IRubyObject result = setConnection( connection );
328
- final IRubyObject adapter = this.callMethod("adapter");
507
+ final IRubyObject adapter = callMethod("adapter");
329
508
  if ( ! adapter.isNil() ) {
330
509
  if ( adapter.respondsTo("configure_connection") ) {
331
510
  adapter.callMethod(context, "configure_connection");
332
511
  }
333
512
  }
334
513
  else {
335
- // NOTE: we should probably warn here about adapter not set ?!?
514
+ // NOTE: we warn on init_connection - should be enough
336
515
  }
337
516
  return result;
338
517
  }
@@ -340,7 +519,7 @@ public class RubyJdbcConnection extends RubyObject {
340
519
  return handleException(context, e);
341
520
  }
342
521
  }
343
-
522
+
344
523
  @JRubyMethod(name = "database_name")
345
524
  public IRubyObject database_name(final ThreadContext context) throws SQLException {
346
525
  final Connection connection = getConnection(true);
@@ -363,9 +542,9 @@ public class RubyJdbcConnection extends RubyObject {
363
542
  try {
364
543
  statement = createStatement(context, connection);
365
544
  if ( doExecute(statement, query) ) {
366
- return unmarshalResults(context, connection.getMetaData(), statement, false);
545
+ return mapResults(context, connection.getMetaData(), statement, false);
367
546
  } else {
368
- return unmarshalKeysOrUpdateCount(context, connection, statement);
547
+ return mapGeneratedKeysOrUpdateCount(context, connection, statement);
369
548
  }
370
549
  }
371
550
  catch (final SQLException e) {
@@ -377,11 +556,11 @@ public class RubyJdbcConnection extends RubyObject {
377
556
  });
378
557
  }
379
558
 
380
- protected Statement createStatement(final ThreadContext context, final Connection connection)
559
+ protected Statement createStatement(final ThreadContext context, final Connection connection)
381
560
  throws SQLException {
382
561
  final Statement statement = connection.createStatement();
383
562
  IRubyObject statementEscapeProcessing = getConfigValue(context, "statement_escape_processing");
384
- // NOTE: disable (driver) escape processing by default, it's not really
563
+ // NOTE: disable (driver) escape processing by default, it's not really
385
564
  // needed for AR statements ... if users need it they might configure :
386
565
  if ( statementEscapeProcessing.isNil() ) {
387
566
  statement.setEscapeProcessing(false);
@@ -391,54 +570,110 @@ public class RubyJdbcConnection extends RubyObject {
391
570
  }
392
571
  return statement;
393
572
  }
394
-
573
+
395
574
  /**
396
575
  * Execute a query using the given statement.
397
576
  * @param statement
398
577
  * @param query
399
- * @return true if the first result is a <code>ResultSet</code>;
578
+ * @return true if the first result is a <code>ResultSet</code>;
400
579
  * false if it is an update count or there are no results
401
- * @throws SQLException
580
+ * @throws SQLException
402
581
  */
403
582
  protected boolean doExecute(final Statement statement, final String query) throws SQLException {
404
583
  return genericExecute(statement, query);
405
584
  }
406
-
585
+
407
586
  /**
408
587
  * @deprecated renamed to {@link #doExecute(Statement, String)}
409
588
  */
410
589
  @Deprecated
411
590
  protected boolean genericExecute(final Statement statement, final String query) throws SQLException {
412
- return statement.execute(query);
591
+ return statement.execute(query); // Statement.RETURN_GENERATED_KEYS
413
592
  }
414
-
415
- protected IRubyObject unmarshalKeysOrUpdateCount(final ThreadContext context,
416
- final Connection connection, final Statement statement) throws SQLException {
417
- final Ruby runtime = context.getRuntime();
418
- final IRubyObject key;
419
- if ( connection.getMetaData().supportsGetGeneratedKeys() ) {
420
- key = unmarshalIdResult(runtime, statement);
593
+
594
+ @JRubyMethod(name = "execute_insert", required = 1)
595
+ public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql)
596
+ throws SQLException {
597
+ final String query = sql.convertToString().getUnicodeValue();
598
+ return executeUpdate(context, query, true);
599
+ }
600
+
601
+ @JRubyMethod(name = "execute_insert", required = 2)
602
+ public IRubyObject execute_insert(final ThreadContext context,
603
+ final IRubyObject sql, final IRubyObject binds) throws SQLException {
604
+ final String query = sql.convertToString().getUnicodeValue();
605
+ if ( binds == null || binds.isNil() ) { // no prepared statements
606
+ return executeUpdate(context, query, true);
421
607
  }
422
- else {
423
- key = runtime.getNil();
608
+ else { // we allow prepared statements with empty binds parameters
609
+ return executePreparedUpdate(context, query, (List) binds, true);
424
610
  }
425
- return key.isNil() ? runtime.newFixnum( statement.getUpdateCount() ) : key;
426
611
  }
427
612
 
428
- @JRubyMethod(name = "execute_insert", required = 1)
429
- public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql)
613
+ /**
614
+ * Executes an UPDATE (DELETE) SQL statement.
615
+ * @param context
616
+ * @param sql
617
+ * @return affected row count
618
+ * @throws SQLException
619
+ */
620
+ @JRubyMethod(name = {"execute_update", "execute_delete"}, required = 1)
621
+ public IRubyObject execute_update(final ThreadContext context, final IRubyObject sql)
430
622
  throws SQLException {
623
+ final String query = sql.convertToString().getUnicodeValue();
624
+ return executeUpdate(context, query, false);
625
+ }
626
+
627
+ /**
628
+ * Executes an UPDATE (DELETE) SQL (prepared - if binds provided) statement.
629
+ * @param context
630
+ * @param sql
631
+ * @return affected row count
632
+ * @throws SQLException
633
+ *
634
+ * @see #execute_update(ThreadContext, IRubyObject)
635
+ */
636
+ @JRubyMethod(name = {"execute_update", "execute_delete"}, required = 2)
637
+ public IRubyObject execute_update(final ThreadContext context,
638
+ final IRubyObject sql, final IRubyObject binds) throws SQLException {
639
+
640
+ final String query = sql.convertToString().getUnicodeValue();
641
+ if ( binds == null || binds.isNil() ) { // no prepared statements
642
+ return executeUpdate(context, query, false);
643
+ }
644
+ else { // we allow prepared statements with empty binds parameters
645
+ return executePreparedUpdate(context, query, (List) binds, false);
646
+ }
647
+ }
648
+
649
+ /**
650
+ * @param context
651
+ * @param query
652
+ * @param returnGeneratedKeys
653
+ * @return row count or generated keys
654
+ *
655
+ * @see #execute_insert(ThreadContext, IRubyObject)
656
+ * @see #execute_update(ThreadContext, IRubyObject)
657
+ */
658
+ protected IRubyObject executeUpdate(final ThreadContext context, final String query,
659
+ final boolean returnGeneratedKeys) {
431
660
  return withConnection(context, new Callable<IRubyObject>() {
432
661
  public IRubyObject call(final Connection connection) throws SQLException {
433
662
  Statement statement = null;
434
- final String insertSQL = sql.convertToString().getUnicodeValue();
435
663
  try {
436
664
  statement = createStatement(context, connection);
437
- statement.executeUpdate(insertSQL, Statement.RETURN_GENERATED_KEYS);
438
- return unmarshalIdResult(context.getRuntime(), statement);
665
+ if ( returnGeneratedKeys ) {
666
+ statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
667
+ IRubyObject keys = mapGeneratedKeys(context.getRuntime(), connection, statement);
668
+ return keys == null ? context.getRuntime().getNil() : keys;
669
+ }
670
+ else {
671
+ final int rowCount = statement.executeUpdate(query);
672
+ return context.getRuntime().newFixnum(rowCount);
673
+ }
439
674
  }
440
675
  catch (final SQLException e) {
441
- debugErrorSQL(context, insertSQL);
676
+ debugErrorSQL(context, query);
442
677
  throw e;
443
678
  }
444
679
  finally { close(statement); }
@@ -446,38 +681,46 @@ public class RubyJdbcConnection extends RubyObject {
446
681
  });
447
682
  }
448
683
 
449
- @JRubyMethod(name = {"execute_update", "execute_delete"}, required = 1)
450
- public IRubyObject execute_update(final ThreadContext context, final IRubyObject sql)
451
- throws SQLException {
452
- return withConnection(context, new Callable<RubyInteger>() {
453
- public RubyInteger call(final Connection connection) throws SQLException {
454
- Statement statement = null;
455
- final String updateSQL = sql.convertToString().getUnicodeValue();
684
+ private IRubyObject executePreparedUpdate(final ThreadContext context, final String query,
685
+ final List<?> binds, final boolean returnGeneratedKeys) {
686
+ return withConnection(context, new Callable<IRubyObject>() {
687
+ public IRubyObject call(final Connection connection) throws SQLException {
688
+ PreparedStatement statement = null;
456
689
  try {
457
- statement = createStatement(context, connection);
458
- final int rowCount = statement.executeUpdate(updateSQL);
459
- return context.getRuntime().newFixnum(rowCount);
690
+ if ( returnGeneratedKeys ) {
691
+ statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
692
+ setStatementParameters(context, connection, statement, binds);
693
+ statement.executeUpdate();
694
+ IRubyObject keys = mapGeneratedKeys(context.getRuntime(), connection, statement);
695
+ return keys == null ? context.getRuntime().getNil() : keys;
696
+ }
697
+ else {
698
+ statement = connection.prepareStatement(query);
699
+ setStatementParameters(context, connection, statement, binds);
700
+ final int rowCount = statement.executeUpdate();
701
+ return context.getRuntime().newFixnum(rowCount);
702
+ }
460
703
  }
461
704
  catch (final SQLException e) {
462
- debugErrorSQL(context, updateSQL);
705
+ debugErrorSQL(context, query);
463
706
  throw e;
464
707
  }
465
708
  finally { close(statement); }
466
709
  }
467
710
  });
468
711
  }
469
-
712
+
470
713
  /**
471
714
  * NOTE: since 1.3 this behaves like <code>execute_query</code> in AR-JDBC 1.2
472
715
  * @param context
473
716
  * @param sql
474
717
  * @param block (optional) block to yield row values
475
718
  * @return raw query result as a name => value Hash (unless block given)
476
- * @throws SQLException
477
- * @see #execute_query_raw(ThreadContext, IRubyObject[], Block)
719
+ * @throws SQLException
720
+ * @see #execute_query_raw(ThreadContext, IRubyObject[], Block)
478
721
  */
479
722
  @JRubyMethod(name = "execute_query_raw", required = 1) // optional block
480
- public IRubyObject execute_query_raw(final ThreadContext context,
723
+ public IRubyObject execute_query_raw(final ThreadContext context,
481
724
  final IRubyObject sql, final Block block) throws SQLException {
482
725
  final String query = sql.convertToString().getUnicodeValue();
483
726
  return executeQueryRaw(context, query, 0, block);
@@ -489,11 +732,11 @@ public class RubyJdbcConnection extends RubyObject {
489
732
  * @param args
490
733
  * @param block (optional) block to yield row values
491
734
  * @return raw query result as a name => value Hash (unless block given)
492
- * @throws SQLException
735
+ * @throws SQLException
493
736
  */
494
737
  @JRubyMethod(name = "execute_query_raw", required = 2, optional = 1)
495
738
  // @JRubyMethod(name = "execute_query_raw", required = 1, optional = 2)
496
- public IRubyObject execute_query_raw(final ThreadContext context,
739
+ public IRubyObject execute_query_raw(final ThreadContext context,
497
740
  final IRubyObject[] args, final Block block) throws SQLException {
498
741
  // args: (sql), (sql, max_rows), (sql, binds), (sql, max_rows, binds)
499
742
  final String query = args[0].convertToString().getUnicodeValue(); // sql
@@ -515,7 +758,7 @@ public class RubyJdbcConnection extends RubyObject {
515
758
  }
516
759
  }
517
760
  }
518
-
761
+
519
762
  if ( binds == null || binds.isNil() ) { // no prepared statements
520
763
  return executeQueryRaw(context, query, maxRows, block);
521
764
  }
@@ -523,33 +766,33 @@ public class RubyJdbcConnection extends RubyObject {
523
766
  return executePreparedQueryRaw(context, query, (List) binds, maxRows, block);
524
767
  }
525
768
  }
526
-
769
+
527
770
  /**
528
771
  * @param context
529
772
  * @param query
530
773
  * @param maxRows
531
774
  * @param block
532
775
  * @return raw query result (in case no block was given)
533
- *
776
+ *
534
777
  * @see #execute_query_raw(ThreadContext, IRubyObject[], Block)
535
778
  */
536
- protected IRubyObject executeQueryRaw(final ThreadContext context,
779
+ protected IRubyObject executeQueryRaw(final ThreadContext context,
537
780
  final String query, final int maxRows, final Block block) {
538
781
  return doExecuteQueryRaw(context, query, maxRows, block, null); // binds == null
539
782
  }
540
783
 
541
- protected IRubyObject executePreparedQueryRaw(final ThreadContext context,
784
+ protected IRubyObject executePreparedQueryRaw(final ThreadContext context,
542
785
  final String query, final List<?> binds, final int maxRows, final Block block) {
543
786
  return doExecuteQueryRaw(context, query, maxRows, block, binds);
544
787
  }
545
-
546
- private IRubyObject doExecuteQueryRaw(final ThreadContext context,
547
- final String query, final int maxRows, final Block block, final List<?> binds) {
788
+
789
+ private IRubyObject doExecuteQueryRaw(final ThreadContext context,
790
+ final String query, final int maxRows, final Block block, final List<?> binds) {
548
791
  return withConnection(context, new Callable<IRubyObject>() {
549
792
  public IRubyObject call(final Connection connection) throws SQLException {
550
793
  final Ruby runtime = context.getRuntime();
551
794
  final DatabaseMetaData metaData = connection.getMetaData();
552
-
795
+
553
796
  Statement statement = null; ResultSet resultSet = null;
554
797
  try {
555
798
  if ( binds == null ) { // plain statement
@@ -564,13 +807,13 @@ public class RubyJdbcConnection extends RubyObject {
564
807
  setStatementParameters(context, connection, prepStatement, binds);
565
808
  resultSet = prepStatement.executeQuery();
566
809
  }
567
-
810
+
568
811
  if ( block != null && block.isGiven() ) {
569
812
  // yield(id1, name1) ... row 1 result data
570
813
  // yield(id2, name2) ... row 2 result data
571
814
  return yieldResultRows(context, runtime, metaData, resultSet, block);
572
815
  }
573
-
816
+
574
817
  return mapToRawResult(context, runtime, metaData, resultSet, false);
575
818
  }
576
819
  catch (final SQLException e) {
@@ -581,34 +824,34 @@ public class RubyJdbcConnection extends RubyObject {
581
824
  }
582
825
  });
583
826
  }
584
-
827
+
585
828
  /**
586
829
  * Executes a query and returns the (AR) result.
587
830
  * @param context
588
831
  * @param sql
589
832
  * @return raw query result as a name => value Hash (unless block given)
590
- * @throws SQLException
591
- * @see #execute_query(ThreadContext, IRubyObject[], Block)
833
+ * @throws SQLException
834
+ * @see #execute_query(ThreadContext, IRubyObject[], Block)
592
835
  */
593
836
  @JRubyMethod(name = "execute_query", required = 1)
594
- public IRubyObject execute_query(final ThreadContext context,
837
+ public IRubyObject execute_query(final ThreadContext context,
595
838
  final IRubyObject sql) throws SQLException {
596
839
  final String query = sql.convertToString().getUnicodeValue();
597
840
  return executeQuery(context, query, 0);
598
841
  }
599
-
842
+
600
843
  /**
601
844
  * Executes a query and returns the (AR) result.
602
845
  * @param context
603
846
  * @param args
604
847
  * @return and <code>ActiveRecord::Result</code>
605
- * @throws SQLException
606
- *
848
+ * @throws SQLException
849
+ *
607
850
  * @see #execute_query(ThreadContext, IRubyObject, IRubyObject, Block)
608
851
  */
609
852
  @JRubyMethod(name = "execute_query", required = 2, optional = 1)
610
853
  // @JRubyMethod(name = "execute_query", required = 1, optional = 2)
611
- public IRubyObject execute_query(final ThreadContext context,
854
+ public IRubyObject execute_query(final ThreadContext context,
612
855
  final IRubyObject[] args) throws SQLException {
613
856
  // args: (sql), (sql, max_rows), (sql, binds), (sql, max_rows, binds)
614
857
  final String query = args[0].convertToString().getUnicodeValue(); // sql
@@ -630,7 +873,7 @@ public class RubyJdbcConnection extends RubyObject {
630
873
  }
631
874
  }
632
875
  }
633
-
876
+
634
877
  if ( binds == null || binds.isNil() ) { // no prepared statements
635
878
  return executeQuery(context, query, maxRows);
636
879
  }
@@ -638,19 +881,19 @@ public class RubyJdbcConnection extends RubyObject {
638
881
  return executePreparedQuery(context, query, (List) binds, maxRows);
639
882
  }
640
883
  }
641
-
884
+
642
885
  /**
643
- * NOTE: This methods behavior changed in AR-JDBC 1.3 the old behavior is
886
+ * NOTE: This methods behavior changed in AR-JDBC 1.3 the old behavior is
644
887
  * achievable using {@link #executeQueryRaw(ThreadContext, String, int, Block)}.
645
- *
888
+ *
646
889
  * @param context
647
890
  * @param query
648
891
  * @param maxRows
649
892
  * @return AR (mapped) query result
650
- *
893
+ *
651
894
  * @see #execute_query(ThreadContext, IRubyObject)
652
895
  * @see #execute_query(ThreadContext, IRubyObject, IRubyObject)
653
- * @see #mapToResult(ThreadContext, Ruby, DatabaseMetaData, ResultSet, RubyJdbcConnection.ColumnData[])
896
+ * @see #mapToResult(ThreadContext, Ruby, DatabaseMetaData, ResultSet, RubyJdbcConnection.ColumnData[])
654
897
  */
655
898
  protected IRubyObject executeQuery(final ThreadContext context, final String query, final int maxRows) {
656
899
  return withConnection(context, new Callable<IRubyObject>() {
@@ -670,8 +913,8 @@ public class RubyJdbcConnection extends RubyObject {
670
913
  }
671
914
  });
672
915
  }
673
-
674
- protected IRubyObject executePreparedQuery(final ThreadContext context, final String query,
916
+
917
+ protected IRubyObject executePreparedQuery(final ThreadContext context, final String query,
675
918
  final List<?> binds, final int maxRows) {
676
919
  return withConnection(context, new Callable<IRubyObject>() {
677
920
  public IRubyObject call(final Connection connection) throws SQLException {
@@ -691,8 +934,8 @@ public class RubyJdbcConnection extends RubyObject {
691
934
  }
692
935
  });
693
936
  }
694
-
695
- private IRubyObject mapQueryResult(final ThreadContext context,
937
+
938
+ private IRubyObject mapQueryResult(final ThreadContext context,
696
939
  final Connection connection, final ResultSet resultSet) throws SQLException {
697
940
  final Ruby runtime = context.getRuntime();
698
941
  final DatabaseMetaData metaData = connection.getMetaData();
@@ -700,9 +943,17 @@ public class RubyJdbcConnection extends RubyObject {
700
943
  return mapToResult(context, runtime, metaData, resultSet, columns);
701
944
  }
702
945
 
946
+ /**
947
+ * @deprecated please do not use this method
948
+ */
949
+ @Deprecated // only used by Oracle adapter - also it's really a bad idea
703
950
  @JRubyMethod(name = "execute_id_insert", required = 2)
704
- public IRubyObject execute_id_insert(final ThreadContext context,
951
+ public IRubyObject execute_id_insert(final ThreadContext context,
705
952
  final IRubyObject sql, final IRubyObject id) throws SQLException {
953
+ final Ruby runtime = context.getRuntime();
954
+
955
+ callMethod("warn", RubyString.newUnicodeString(runtime, "DEPRECATED: execute_id_insert(sql, id) will be removed"));
956
+
706
957
  return withConnection(context, new Callable<IRubyObject>() {
707
958
  public IRubyObject call(final Connection connection) throws SQLException {
708
959
  PreparedStatement statement = null;
@@ -721,12 +972,19 @@ public class RubyJdbcConnection extends RubyObject {
721
972
  }
722
973
  });
723
974
  }
724
-
725
- @JRubyMethod(name = "native_database_types", frame = false)
726
- public IRubyObject native_database_types() {
727
- return getInstanceVariable("@native_database_types");
728
- }
729
975
 
976
+ @JRubyMethod(name = "supported_data_types")
977
+ public IRubyObject supported_data_types(final ThreadContext context) throws SQLException {
978
+ final Ruby runtime = context.getRuntime();
979
+ final DatabaseMetaData metaData = getConnection(true).getMetaData();
980
+ final IRubyObject types; final ResultSet typeDesc = metaData.getTypeInfo();
981
+ try {
982
+ types = mapToRawResult(context, runtime, metaData, typeDesc, true);
983
+ }
984
+ finally { close(typeDesc); }
985
+
986
+ return types;
987
+ }
730
988
 
731
989
  @JRubyMethod(name = "primary_keys", required = 1)
732
990
  public IRubyObject primary_keys(ThreadContext context, IRubyObject tableName) throws SQLException {
@@ -736,7 +994,7 @@ public class RubyJdbcConnection extends RubyObject {
736
994
  }
737
995
 
738
996
  private static final int PRIMARY_KEYS_COLUMN_NAME = 4;
739
-
997
+
740
998
  protected List<RubyString> primaryKeys(final ThreadContext context, final String tableName) {
741
999
  return withConnection(context, new Callable<List<RubyString>>() {
742
1000
  public List<RubyString> call(final Connection connection) throws SQLException {
@@ -760,23 +1018,7 @@ public class RubyJdbcConnection extends RubyObject {
760
1018
  }
761
1019
  });
762
1020
  }
763
-
764
- @JRubyMethod(name = "set_native_database_types")
765
- public IRubyObject set_native_database_types(final ThreadContext context) throws SQLException {
766
- final Ruby runtime = context.getRuntime();
767
- final DatabaseMetaData metaData = getConnection(true).getMetaData();
768
- final IRubyObject types; final ResultSet typeDesc = metaData.getTypeInfo();
769
- try {
770
- types = mapToRawResult(context, runtime, metaData, typeDesc, true);
771
- }
772
- finally { close(typeDesc); }
773
-
774
- final IRubyObject typeConverter = getJdbcTypeConverter(runtime).callMethod("new", types);
775
- setInstanceVariable("@native_types", typeConverter.callMethod(context, "choose_best_types"));
776
1021
 
777
- return runtime.getNil();
778
- }
779
-
780
1022
  @JRubyMethod(name = "tables")
781
1023
  public IRubyObject tables(ThreadContext context) {
782
1024
  return tables(context, null, null, null, TABLE_TYPE);
@@ -802,7 +1044,7 @@ public class RubyJdbcConnection extends RubyObject {
802
1044
  return tables(context, toStringOrNull(args[0]), toStringOrNull(args[1]), toStringOrNull(args[2]), getTypes(args[3]));
803
1045
  }
804
1046
 
805
- protected IRubyObject tables(final ThreadContext context,
1047
+ protected IRubyObject tables(final ThreadContext context,
806
1048
  final String catalog, final String schemaPattern, final String tablePattern, final String[] types) {
807
1049
  return withConnection(context, new Callable<IRubyObject>() {
808
1050
  public IRubyObject call(final Connection connection) throws SQLException {
@@ -821,10 +1063,10 @@ public class RubyJdbcConnection extends RubyObject {
821
1063
  throw context.getRuntime().newArgumentError("nil table name");
822
1064
  }
823
1065
  final String tableName = table.toString();
824
-
1066
+
825
1067
  return tableExists(context, null, tableName);
826
1068
  }
827
-
1069
+
828
1070
  @JRubyMethod(name = "table_exists?")
829
1071
  public IRubyObject table_exists_p(final ThreadContext context, IRubyObject table, IRubyObject schema) {
830
1072
  if ( table.isNil() ) {
@@ -832,10 +1074,10 @@ public class RubyJdbcConnection extends RubyObject {
832
1074
  }
833
1075
  final String tableName = table.toString();
834
1076
  final String defaultSchema = schema.isNil() ? null : schema.toString();
835
-
1077
+
836
1078
  return tableExists(context, defaultSchema, tableName);
837
1079
  }
838
-
1080
+
839
1081
  protected IRubyObject tableExists(final ThreadContext context,
840
1082
  final String defaultSchema, final String tableName) {
841
1083
  final Ruby runtime = context.getRuntime();
@@ -846,7 +1088,7 @@ public class RubyJdbcConnection extends RubyObject {
846
1088
  }
847
1089
  });
848
1090
  }
849
-
1091
+
850
1092
  @JRubyMethod(name = {"columns", "columns_internal"}, required = 1, optional = 2)
851
1093
  public IRubyObject columns_internal(final ThreadContext context, final IRubyObject[] args)
852
1094
  throws SQLException {
@@ -858,7 +1100,7 @@ public class RubyJdbcConnection extends RubyObject {
858
1100
  // optionals (NOTE: catalog argumnet was never used before 1.3.0) :
859
1101
  final String catalog = args.length > 1 ? toStringOrNull(args[1]) : null;
860
1102
  final String defaultSchema = args.length > 2 ? toStringOrNull(args[2]) : null;
861
-
1103
+
862
1104
  final TableName components;
863
1105
  if ( catalog == null ) { // backwards-compatibility with < 1.3.0
864
1106
  components = extractTableName(connection, defaultSchema, tableName);
@@ -866,7 +1108,7 @@ public class RubyJdbcConnection extends RubyObject {
866
1108
  else {
867
1109
  components = extractTableName(connection, catalog, defaultSchema, tableName);
868
1110
  }
869
-
1111
+
870
1112
  if ( ! tableExists(context.getRuntime(), connection, components) ) {
871
1113
  throw new SQLException("table: " + tableName + " does not exist");
872
1114
  }
@@ -888,12 +1130,12 @@ public class RubyJdbcConnection extends RubyObject {
888
1130
  public IRubyObject indexes(final ThreadContext context, IRubyObject tableName, IRubyObject name) {
889
1131
  return indexes(context, toStringOrNull(tableName), toStringOrNull(name), null);
890
1132
  }
891
-
1133
+
892
1134
  @JRubyMethod(name = "indexes")
893
1135
  public IRubyObject indexes(final ThreadContext context, IRubyObject tableName, IRubyObject name, IRubyObject schemaName) {
894
1136
  return indexes(context, toStringOrNull(tableName), toStringOrNull(name), toStringOrNull(schemaName));
895
1137
  }
896
-
1138
+
897
1139
  // NOTE: metaData.getIndexInfo row mappings :
898
1140
  private static final int INDEX_INFO_TABLE_NAME = 3;
899
1141
  private static final int INDEX_INFO_NON_UNIQUE = 4;
@@ -913,11 +1155,11 @@ public class RubyJdbcConnection extends RubyObject {
913
1155
  public IRubyObject call(final Connection connection) throws SQLException {
914
1156
  final Ruby runtime = context.getRuntime();
915
1157
  final RubyClass indexDefinition = getIndexDefinition(runtime);
916
-
1158
+
917
1159
  final DatabaseMetaData metaData = connection.getMetaData();
918
1160
  String _tableName = caseConvertIdentifierForJdbc(metaData, tableName);
919
1161
  String _schemaName = caseConvertIdentifierForJdbc(metaData, schemaName);
920
-
1162
+
921
1163
  final List<RubyString> primaryKeys = primaryKeys(context, _tableName);
922
1164
  ResultSet indexInfoSet = null;
923
1165
  final List<IRubyObject> indexes = new ArrayList<IRubyObject>();
@@ -942,9 +1184,9 @@ public class RubyJdbcConnection extends RubyObject {
942
1184
 
943
1185
  String indexTableName = indexInfoSet.getString(INDEX_INFO_TABLE_NAME);
944
1186
  indexTableName = caseConvertIdentifierForRails(metaData, indexTableName);
945
-
1187
+
946
1188
  final boolean nonUnique = indexInfoSet.getBoolean(INDEX_INFO_NON_UNIQUE);
947
-
1189
+
948
1190
  IRubyObject[] args = new IRubyObject[] {
949
1191
  RubyString.newUnicodeString(runtime, indexTableName), // table_name
950
1192
  RubyString.newUnicodeString(runtime, indexName), // index_name
@@ -964,12 +1206,12 @@ public class RubyJdbcConnection extends RubyObject {
964
1206
  }
965
1207
 
966
1208
  return runtime.newArray(indexes);
967
-
1209
+
968
1210
  } finally { close(indexInfoSet); }
969
1211
  }
970
1212
  });
971
1213
  }
972
-
1214
+
973
1215
  // NOTE: this seems to be not used ... at all ?!
974
1216
  /*
975
1217
  * sql, values (array), types (column.type array), name = nil, pk = nil, id_value = nil, sequence_name = nil
@@ -986,13 +1228,13 @@ public class RubyJdbcConnection extends RubyObject {
986
1228
  statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
987
1229
  setPreparedStatementValues(context, connection, statement, args[1], args[2]);
988
1230
  statement.executeUpdate();
989
- return unmarshalIdResult(runtime, statement);
1231
+ return mapGeneratedKeys(runtime, connection, statement);
990
1232
  }
991
1233
  finally { close(statement); }
992
1234
  }
993
1235
  });
994
1236
  }
995
-
1237
+
996
1238
  // NOTE: this seems to be not used ... at all ?!
997
1239
  /*
998
1240
  * sql, values (array), types (column.type array), name = nil
@@ -1027,41 +1269,75 @@ public class RubyJdbcConnection extends RubyObject {
1027
1269
  }
1028
1270
 
1029
1271
  /*
1030
- * (is binary?, colname, tablename, primary_key, id, lob_value)
1272
+ * (binary?, column_name, table_name, id_key, id_value, value)
1031
1273
  */
1274
+ @Deprecated
1032
1275
  @JRubyMethod(name = "write_large_object", required = 6)
1033
1276
  public IRubyObject write_large_object(final ThreadContext context, final IRubyObject[] args)
1034
1277
  throws SQLException {
1035
-
1036
- final boolean isBinary = args[0].isTrue();
1278
+
1279
+ final boolean binary = args[0].isTrue();
1037
1280
  final String columnName = args[1].toString();
1038
1281
  final String tableName = args[2].toString();
1039
1282
  final String idKey = args[3].toString();
1040
- final String idVal = args[4].toString();
1283
+ final IRubyObject idVal = args[4];
1041
1284
  final IRubyObject lobValue = args[5];
1042
-
1043
- final Ruby runtime = context.getRuntime();
1044
- return withConnection(context, new Callable<IRubyObject>() {
1045
- public IRubyObject call(final Connection connection) throws SQLException {
1046
- final String sql = "UPDATE "+ tableName +
1047
- " SET "+ columnName +" = ? WHERE "+ idKey +" = "+ idVal;
1285
+
1286
+ int count = updateLobValue(context, tableName, columnName, null, idKey, idVal, null, lobValue, binary);
1287
+ return context.getRuntime().newFixnum(count);
1288
+ }
1289
+
1290
+ @JRubyMethod(name = "update_lob_value", required = 3)
1291
+ public IRubyObject update_lob_value(final ThreadContext context,
1292
+ final IRubyObject record, final IRubyObject column, final IRubyObject value)
1293
+ throws SQLException {
1294
+
1295
+ final boolean binary = // column.type == :binary
1296
+ column.callMethod(context, "type").toString() == (Object) "binary";
1297
+
1298
+ final RubyClass recordClass = record.getMetaClass(); // record.class
1299
+ final IRubyObject connection = record.callMethod(context, "connection");
1300
+
1301
+ IRubyObject columnName = column.callMethod(context, "name");
1302
+ columnName = connection.callMethod(context, "quote_column_name", columnName);
1303
+ IRubyObject tableName = recordClass.callMethod(context, "table_name");
1304
+ tableName = connection.callMethod(context, "quote_table_name", tableName);
1305
+ final IRubyObject idKey = recordClass.callMethod(context, "primary_key"); // 'id'
1306
+ // callMethod(context, "quote", primaryKey);
1307
+ final IRubyObject idColumn = // record.class.columns_hash['id']
1308
+ recordClass.callMethod(context, "columns_hash").callMethod(context, "[]", idKey);
1309
+
1310
+ final IRubyObject id = record.callMethod(context, "id"); // record.id
1311
+
1312
+ int count = updateLobValue(context,
1313
+ tableName.toString(), columnName.toString(), column,
1314
+ idKey.toString(), id, idColumn, value, binary
1315
+ );
1316
+ return context.getRuntime().newFixnum(count);
1317
+ }
1318
+
1319
+ private int updateLobValue(final ThreadContext context,
1320
+ final String tableName, final String columnName, final IRubyObject column,
1321
+ final String idKey, final IRubyObject idValue, final IRubyObject idColumn,
1322
+ final IRubyObject value, final boolean binary) {
1323
+
1324
+ final String sql = "UPDATE "+ tableName +" SET "+ columnName +" = ? WHERE "+ idKey +" = ?" ;
1325
+
1326
+ return withConnection(context, new Callable<Integer>() {
1327
+ public Integer call(final Connection connection) throws SQLException {
1048
1328
  PreparedStatement statement = null;
1049
1329
  try {
1050
1330
  statement = connection.prepareStatement(sql);
1051
- if ( isBinary ) { // binary
1052
- final ByteList blob = lobValue.convertToString().getByteList();
1053
- final int realSize = blob.getRealSize();
1054
- statement.setBinaryStream(1,
1055
- new ByteArrayInputStream(blob.unsafeBytes(), blob.getBegin(), realSize), realSize
1056
- );
1057
- } else { // clob
1058
- String clob = lobValue.convertToString().getUnicodeValue();
1059
- statement.setCharacterStream(1, new StringReader(clob), clob.length());
1331
+ if ( binary ) { // blob
1332
+ setBlobParameter(context, connection, statement, 1, value, column, Types.BLOB);
1060
1333
  }
1061
- statement.executeUpdate();
1334
+ else { // clob
1335
+ setClobParameter(context, connection, statement, 1, value, column, Types.CLOB);
1336
+ }
1337
+ setStatementParameter(context, context.getRuntime(), connection, statement, 2, idValue, idColumn);
1338
+ return statement.executeUpdate();
1062
1339
  }
1063
1340
  finally { close(statement); }
1064
- return runtime.getNil();
1065
1341
  }
1066
1342
  });
1067
1343
  }
@@ -1080,7 +1356,7 @@ public class RubyJdbcConnection extends RubyObject {
1080
1356
  protected static String caseConvertIdentifierForRails(final DatabaseMetaData metaData, final String value)
1081
1357
  throws SQLException {
1082
1358
  if ( value == null ) return null;
1083
-
1359
+
1084
1360
  return metaData.storesUpperCaseIdentifiers() ? value.toLowerCase() : value;
1085
1361
  }
1086
1362
 
@@ -1092,7 +1368,7 @@ public class RubyJdbcConnection extends RubyObject {
1092
1368
  protected String caseConvertIdentifierForJdbc(final DatabaseMetaData metaData, final String value)
1093
1369
  throws SQLException {
1094
1370
  if ( value == null ) return null;
1095
-
1371
+
1096
1372
  if ( metaData.storesUpperCaseIdentifiers() ) {
1097
1373
  return value.toUpperCase();
1098
1374
  }
@@ -1103,11 +1379,11 @@ public class RubyJdbcConnection extends RubyObject {
1103
1379
  return value;
1104
1380
  }
1105
1381
 
1106
- protected IRubyObject getConfigValue(final ThreadContext context, final String key) {
1107
- final IRubyObject config = getInstanceVariable("@config");
1382
+ protected final IRubyObject getConfigValue(final ThreadContext context, final String key) {
1383
+ final IRubyObject config = callMethod(context, "config");
1108
1384
  return config.callMethod(context, "[]", context.getRuntime().newSymbol(key));
1109
1385
  }
1110
-
1386
+
1111
1387
  /**
1112
1388
  * @deprecated renamed to {@link #getConfigValue(ThreadContext, String)}
1113
1389
  */
@@ -1115,39 +1391,45 @@ public class RubyJdbcConnection extends RubyObject {
1115
1391
  protected IRubyObject config_value(ThreadContext context, String key) {
1116
1392
  return getConfigValue(context, key);
1117
1393
  }
1118
-
1394
+
1119
1395
  private static String toStringOrNull(final IRubyObject arg) {
1120
1396
  return arg.isNil() ? null : arg.toString();
1121
1397
  }
1122
1398
 
1123
- protected IRubyObject getAdapter(final ThreadContext context) {
1399
+ protected final IRubyObject getAdapter(final ThreadContext context) {
1124
1400
  return callMethod(context, "adapter");
1125
1401
  }
1126
1402
 
1127
- protected IRubyObject getJdbcColumnClass(final ThreadContext context) {
1403
+ protected final IRubyObject getJdbcColumnClass(final ThreadContext context) {
1128
1404
  return getAdapter(context).callMethod(context, "jdbc_column_class");
1129
1405
  }
1130
1406
 
1131
1407
  protected JdbcConnectionFactory getConnectionFactory() throws RaiseException {
1132
- IRubyObject connection_factory = getInstanceVariable("@connection_factory");
1133
- if ( connection_factory == null ) {
1134
- throw getRuntime().newRuntimeError("@connection_factory not set");
1135
- }
1136
- JdbcConnectionFactory connectionFactory;
1137
- try {
1138
- connectionFactory = (JdbcConnectionFactory)
1139
- connection_factory.toJava(JdbcConnectionFactory.class);
1140
- }
1141
- catch (Exception e) {
1142
- throw getRuntime().newRuntimeError("@connection_factory not set properly: " + e);
1408
+ if ( connectionFactory == null ) {
1409
+ // throw new IllegalStateException("connection factory not set");
1410
+ // NOTE: only for (backwards) compatibility - no likely that anyone
1411
+ // overriden this - thus can likely be safely deleted (no needed) :
1412
+ IRubyObject connection_factory = getInstanceVariable("@connection_factory");
1413
+ if ( connection_factory == null ) {
1414
+ throw getRuntime().newRuntimeError("@connection_factory not set");
1415
+ }
1416
+ connectionFactory = (JdbcConnectionFactory) connection_factory.toJava(JdbcConnectionFactory.class);
1143
1417
  }
1144
1418
  return connectionFactory;
1145
1419
  }
1146
1420
 
1421
+ public void setConnectionFactory(JdbcConnectionFactory connectionFactory) {
1422
+ this.connectionFactory = connectionFactory;
1423
+ }
1424
+
1425
+ protected Connection newConnection() throws RaiseException, SQLException {
1426
+ return getConnectionFactory().newConnection();
1427
+ }
1428
+
1147
1429
  private static String[] getTypes(final IRubyObject typeArg) {
1148
1430
  if ( typeArg instanceof RubyArray ) {
1149
1431
  IRubyObject[] rubyTypes = ((RubyArray) typeArg).toJavaArray();
1150
-
1432
+
1151
1433
  final String[] types = new String[rubyTypes.length];
1152
1434
  for ( int i = 0; i < types.length; i++ ) {
1153
1435
  types[i] = rubyTypes[i].toString();
@@ -1158,15 +1440,15 @@ public class RubyJdbcConnection extends RubyObject {
1158
1440
  }
1159
1441
 
1160
1442
  /**
1161
- * @deprecated this method is no longer used, instead consider overriding
1443
+ * @deprecated this method is no longer used, instead consider overriding
1162
1444
  * {@link #mapToResult(ThreadContext, Ruby, DatabaseMetaData, ResultSet, RubyJdbcConnection.ColumnData[])}
1163
1445
  */
1164
1446
  @Deprecated
1165
1447
  protected void populateFromResultSet(
1166
- final ThreadContext context, final Ruby runtime,
1167
- final List<IRubyObject> results, final ResultSet resultSet,
1448
+ final ThreadContext context, final Ruby runtime,
1449
+ final List<IRubyObject> results, final ResultSet resultSet,
1168
1450
  final ColumnData[] columns) throws SQLException {
1169
- final ResultHandler resultHandler = ResultHandler.getInstance(context);
1451
+ final ResultHandler resultHandler = ResultHandler.getInstance(runtime);
1170
1452
  while ( resultSet.next() ) {
1171
1453
  results.add( resultHandler.mapRawRow(context, runtime, columns, resultSet, this) );
1172
1454
  }
@@ -1180,81 +1462,91 @@ public class RubyJdbcConnection extends RubyObject {
1180
1462
  * @param resultSet
1181
1463
  * @param columns
1182
1464
  * @return since 3.1 expected to return a <code>ActiveRecord::Result</code>
1183
- * @throws SQLException
1465
+ * @throws SQLException
1184
1466
  */
1185
1467
  protected IRubyObject mapToResult(final ThreadContext context, final Ruby runtime,
1186
- final DatabaseMetaData metaData, final ResultSet resultSet,
1468
+ final DatabaseMetaData metaData, final ResultSet resultSet,
1187
1469
  final ColumnData[] columns) throws SQLException {
1188
1470
 
1189
- final ResultHandler resultHandler = ResultHandler.getInstance(context);
1471
+ final ResultHandler resultHandler = ResultHandler.getInstance(runtime);
1190
1472
  final RubyArray resultRows = runtime.newArray();
1191
-
1473
+
1192
1474
  while ( resultSet.next() ) {
1193
1475
  resultRows.add( resultHandler.mapRow(context, runtime, columns, resultSet, this) );
1194
1476
  }
1195
-
1477
+
1196
1478
  return resultHandler.newResult(context, runtime, columns, resultRows);
1197
1479
  }
1198
-
1199
- protected IRubyObject jdbcToRuby(final Ruby runtime, final int column,
1200
- final int type, final ResultSet resultSet) throws SQLException {
1480
+
1481
+ @Deprecated
1482
+ protected IRubyObject jdbcToRuby(final Ruby runtime,
1483
+ final int column, final int type, final ResultSet resultSet)
1484
+ throws SQLException {
1485
+ return jdbcToRuby(runtime.getCurrentContext(), runtime, column, type, resultSet);
1486
+ }
1487
+
1488
+ protected IRubyObject jdbcToRuby(
1489
+ final ThreadContext context, final Ruby runtime,
1490
+ final int column, final int type, final ResultSet resultSet)
1491
+ throws SQLException {
1492
+
1201
1493
  try {
1202
1494
  switch (type) {
1203
1495
  case Types.BLOB:
1204
1496
  case Types.BINARY:
1205
1497
  case Types.VARBINARY:
1206
1498
  case Types.LONGVARBINARY:
1207
- return streamToRuby(runtime, resultSet, column);
1499
+ return streamToRuby(context, runtime, resultSet, column);
1208
1500
  case Types.CLOB:
1209
1501
  case Types.NCLOB: // JDBC 4.0
1210
- return readerToRuby(runtime, resultSet, column);
1502
+ return readerToRuby(context, runtime, resultSet, column);
1211
1503
  case Types.LONGVARCHAR:
1212
1504
  case Types.LONGNVARCHAR: // JDBC 4.0
1213
1505
  if ( runtime.is1_9() ) {
1214
- return readerToRuby(runtime, resultSet, column);
1506
+ return readerToRuby(context, runtime, resultSet, column);
1215
1507
  }
1216
1508
  else {
1217
- return streamToRuby(runtime, resultSet, column);
1509
+ return streamToRuby(context, runtime, resultSet, column);
1218
1510
  }
1219
1511
  case Types.TINYINT:
1220
1512
  case Types.SMALLINT:
1221
1513
  case Types.INTEGER:
1222
- return integerToRuby(runtime, resultSet, column);
1514
+ return integerToRuby(context, runtime, resultSet, column);
1223
1515
  case Types.REAL:
1224
1516
  case Types.FLOAT:
1225
1517
  case Types.DOUBLE:
1226
- return doubleToRuby(runtime, resultSet, column);
1518
+ return doubleToRuby(context, runtime, resultSet, column);
1227
1519
  case Types.BIGINT:
1228
- return bigIntegerToRuby(runtime, resultSet, column);
1520
+ return bigIntegerToRuby(context, runtime, resultSet, column);
1229
1521
  case Types.NUMERIC:
1230
1522
  case Types.DECIMAL:
1231
- return decimalToRuby(runtime, resultSet, column);
1523
+ return decimalToRuby(context, runtime, resultSet, column);
1232
1524
  case Types.DATE:
1233
- return dateToRuby(runtime, resultSet, column);
1525
+ return dateToRuby(context, runtime, resultSet, column);
1234
1526
  case Types.TIME:
1235
- return timeToRuby(runtime, resultSet, column);
1527
+ return timeToRuby(context, runtime, resultSet, column);
1236
1528
  case Types.TIMESTAMP:
1237
- return timestampToRuby(runtime, resultSet, column);
1529
+ return timestampToRuby(context, runtime, resultSet, column);
1238
1530
  case Types.BIT:
1239
1531
  case Types.BOOLEAN:
1240
- return booleanToRuby(runtime, resultSet, column);
1532
+ return booleanToRuby(context, runtime, resultSet, column);
1241
1533
  case Types.SQLXML: // JDBC 4.0
1242
- return xmlToRuby(runtime, resultSet, column);
1534
+ return xmlToRuby(context, runtime, resultSet, column);
1243
1535
  case Types.ARRAY: // we handle JDBC Array into (Ruby) []
1244
- return arrayToRuby(runtime, resultSet, column);
1536
+ return arrayToRuby(context, runtime, resultSet, column);
1245
1537
  case Types.NULL:
1246
1538
  return runtime.getNil();
1247
1539
  // NOTE: (JDBC) exotic stuff just cause it's so easy with JRuby :)
1248
1540
  case Types.JAVA_OBJECT:
1249
1541
  case Types.OTHER:
1250
- return objectToRuby(runtime, resultSet, column);
1542
+ return objectToRuby(context, runtime, resultSet, column);
1251
1543
  // (default) String
1252
1544
  case Types.CHAR:
1253
1545
  case Types.VARCHAR:
1254
1546
  case Types.NCHAR: // JDBC 4.0
1255
1547
  case Types.NVARCHAR: // JDBC 4.0
1256
1548
  default:
1257
- return stringToRuby(runtime, resultSet, column);
1549
+ return stringToRuby(context, runtime, resultSet, column);
1258
1550
  }
1259
1551
  // NOTE: not mapped types :
1260
1552
  //case Types.DISTINCT:
@@ -1267,14 +1559,14 @@ public class RubyJdbcConnection extends RubyObject {
1267
1559
  }
1268
1560
  }
1269
1561
 
1270
- protected IRubyObject integerToRuby(
1562
+ protected IRubyObject integerToRuby(final ThreadContext context,
1271
1563
  final Ruby runtime, final ResultSet resultSet, final int column)
1272
1564
  throws SQLException {
1273
1565
  final long value = resultSet.getLong(column);
1274
1566
  if ( value == 0 && resultSet.wasNull() ) return runtime.getNil();
1275
1567
  return integerToRuby(runtime, resultSet, value);
1276
1568
  }
1277
-
1569
+
1278
1570
  @Deprecated
1279
1571
  protected IRubyObject integerToRuby(
1280
1572
  final Ruby runtime, final ResultSet resultSet, final long longValue)
@@ -1284,28 +1576,30 @@ public class RubyJdbcConnection extends RubyObject {
1284
1576
  return runtime.newFixnum(longValue);
1285
1577
  }
1286
1578
 
1287
- protected IRubyObject doubleToRuby(Ruby runtime, ResultSet resultSet, final int column)
1579
+ protected IRubyObject doubleToRuby(final ThreadContext context,
1580
+ final Ruby runtime, final ResultSet resultSet, final int column)
1288
1581
  throws SQLException {
1289
1582
  final double value = resultSet.getDouble(column);
1290
1583
  if ( value == 0 && resultSet.wasNull() ) return runtime.getNil();
1291
1584
  return doubleToRuby(runtime, resultSet, value);
1292
1585
  }
1293
-
1586
+
1294
1587
  @Deprecated
1295
- protected IRubyObject doubleToRuby(Ruby runtime, ResultSet resultSet, double doubleValue)
1588
+ protected IRubyObject doubleToRuby(
1589
+ final Ruby runtime, final ResultSet resultSet, double doubleValue)
1296
1590
  throws SQLException {
1297
1591
  if ( doubleValue == 0 && resultSet.wasNull() ) return runtime.getNil();
1298
1592
  return runtime.newFloat(doubleValue);
1299
1593
  }
1300
1594
 
1301
- protected IRubyObject stringToRuby(
1595
+ protected IRubyObject stringToRuby(final ThreadContext context,
1302
1596
  final Ruby runtime, final ResultSet resultSet, final int column)
1303
1597
  throws SQLException {
1304
1598
  final String value = resultSet.getString(column);
1305
1599
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1306
1600
  return stringToRuby(runtime, resultSet, value);
1307
1601
  }
1308
-
1602
+
1309
1603
  @Deprecated
1310
1604
  protected IRubyObject stringToRuby(
1311
1605
  final Ruby runtime, final ResultSet resultSet, final String string)
@@ -1315,83 +1609,133 @@ public class RubyJdbcConnection extends RubyObject {
1315
1609
  return RubyString.newUnicodeString(runtime, string);
1316
1610
  }
1317
1611
 
1318
- protected IRubyObject bigIntegerToRuby(
1319
- final Ruby runtime, final ResultSet resultSet, final int column)
1612
+ protected IRubyObject bigIntegerToRuby(final ThreadContext context,
1613
+ final Ruby runtime, final ResultSet resultSet, final int column)
1320
1614
  throws SQLException {
1321
1615
  final String value = resultSet.getString(column);
1322
1616
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1323
1617
  return bigIntegerToRuby(runtime, resultSet, value);
1324
1618
  }
1325
-
1619
+
1326
1620
  @Deprecated
1327
1621
  protected IRubyObject bigIntegerToRuby(
1328
- final Ruby runtime, final ResultSet resultSet, final String intValue)
1622
+ final Ruby runtime, final ResultSet resultSet, final String intValue)
1329
1623
  throws SQLException {
1330
1624
  if ( intValue == null && resultSet.wasNull() ) return runtime.getNil();
1331
1625
 
1332
1626
  return RubyBignum.bignorm(runtime, new BigInteger(intValue));
1333
1627
  }
1334
1628
 
1335
- protected IRubyObject decimalToRuby(
1336
- final Ruby runtime, final ResultSet resultSet, final int column)
1629
+ protected IRubyObject decimalToRuby(final ThreadContext context,
1630
+ final Ruby runtime, final ResultSet resultSet, final int column)
1337
1631
  throws SQLException {
1338
1632
  final String value = resultSet.getString(column);
1339
1633
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1340
1634
  // NOTE: JRuby 1.6 -> 1.7 API change : moved org.jruby.RubyBigDecimal
1341
1635
  return runtime.getKernel().callMethod("BigDecimal", runtime.newString(value));
1342
1636
  }
1343
-
1344
- private static boolean parseDateTime = false; // TODO
1345
-
1346
- protected IRubyObject dateToRuby( // TODO
1637
+
1638
+ protected static boolean rawDateTime = Boolean.getBoolean("arjdbc.datetime.raw");
1639
+
1640
+ @JRubyMethod(name = "raw_date_time?")
1641
+ public static IRubyObject useRawDateTime(final ThreadContext context, final IRubyObject self) {
1642
+ return context.getRuntime().newBoolean(rawDateTime);
1643
+ }
1644
+
1645
+ @JRubyMethod(name = "raw_date_time=")
1646
+ public static IRubyObject setRawDateTime(final IRubyObject self, final IRubyObject value) {
1647
+ if ( value instanceof RubyBoolean ) {
1648
+ rawDateTime = ((RubyBoolean) value).isTrue();
1649
+ }
1650
+ else {
1651
+ rawDateTime = value.isNil();
1652
+ }
1653
+ return value;
1654
+ }
1655
+
1656
+ protected IRubyObject dateToRuby(final ThreadContext context,
1347
1657
  final Ruby runtime, final ResultSet resultSet, final int column)
1348
1658
  throws SQLException {
1659
+
1349
1660
  final Date value = resultSet.getDate(column);
1350
- if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1351
- return RubyString.newUnicodeString(runtime, value.toString());
1661
+ if ( value == null ) {
1662
+ if ( resultSet.wasNull() ) return runtime.getNil();
1663
+ return runtime.newString(); // ""
1664
+ }
1665
+
1666
+ final RubyString strValue = RubyString.newUnicodeString(runtime, value.toString());
1667
+ if ( rawDateTime ) return strValue;
1668
+
1669
+ final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
1670
+ if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
1671
+ return adapter.callMethod(context, "_string_to_date", strValue);
1352
1672
  }
1353
1673
 
1354
- protected IRubyObject timeToRuby( // TODO
1674
+ protected IRubyObject timeToRuby(final ThreadContext context,
1355
1675
  final Ruby runtime, final ResultSet resultSet, final int column)
1356
1676
  throws SQLException {
1677
+
1357
1678
  final Time value = resultSet.getTime(column);
1358
- if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1359
- return RubyString.newUnicodeString(runtime, value.toString());
1679
+ if ( value == null ) {
1680
+ if ( resultSet.wasNull() ) return runtime.getNil();
1681
+ return runtime.newString(); // ""
1682
+ }
1683
+
1684
+ final RubyString strValue = RubyString.newUnicodeString(runtime, value.toString());
1685
+ if ( rawDateTime ) return strValue;
1686
+
1687
+ final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
1688
+ if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
1689
+ return adapter.callMethod(context, "_string_to_time", strValue);
1360
1690
  }
1361
1691
 
1362
- protected IRubyObject timestampToRuby(
1692
+ protected IRubyObject timestampToRuby(final ThreadContext context, // TODO
1363
1693
  final Ruby runtime, final ResultSet resultSet, final int column)
1364
1694
  throws SQLException {
1695
+
1365
1696
  final Timestamp value = resultSet.getTimestamp(column);
1366
- if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1367
- return timestampToRuby(runtime, resultSet, value);
1697
+ if ( value == null ) {
1698
+ if ( resultSet.wasNull() ) return runtime.getNil();
1699
+ return runtime.newString(); // ""
1700
+ }
1701
+
1702
+ final RubyString strValue = timestampToRubyString(runtime, value.toString());
1703
+ if ( rawDateTime ) return strValue;
1704
+
1705
+ final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
1706
+ if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
1707
+ return adapter.callMethod(context, "_string_to_timestamp", strValue);
1708
+ }
1709
+
1710
+ protected static RubyString timestampToRubyString(final Ruby runtime, String value) {
1711
+ // Timestamp's format: yyyy-mm-dd hh:mm:ss.fffffffff
1712
+ String suffix; // assumes java.sql.Timestamp internals :
1713
+ if ( value.endsWith( suffix = " 00:00:00.0" ) ) {
1714
+ value = value.substring( 0, value.length() - suffix.length() );
1715
+ }
1716
+ else if ( value.endsWith( suffix = ".0" ) ) {
1717
+ value = value.substring( 0, value.length() - suffix.length() );
1718
+ }
1719
+ return RubyString.newUnicodeString(runtime, value);
1368
1720
  }
1369
-
1721
+
1370
1722
  @Deprecated
1371
1723
  protected IRubyObject timestampToRuby(
1372
1724
  final Ruby runtime, final ResultSet resultSet, final Timestamp value)
1373
1725
  throws SQLException {
1374
1726
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1375
-
1376
- String format = value.toString(); // yyyy-mm-dd hh:mm:ss.fffffffff
1377
- if ( format.endsWith(" 00:00:00.0") ) {
1378
- format = format.substring(0, format.length() - (" 00:00:00.0".length()));
1379
- }
1380
- if ( format.endsWith(".0") ) {
1381
- format = format.substring(0, format.length() - (".0".length()));
1382
- }
1383
-
1384
- return RubyString.newUnicodeString(runtime, format);
1727
+
1728
+ return timestampToRubyString(runtime, value.toString());
1385
1729
  }
1386
1730
 
1387
- protected IRubyObject booleanToRuby(
1731
+ protected IRubyObject booleanToRuby(final ThreadContext context,
1388
1732
  final Ruby runtime, final ResultSet resultSet, final int column)
1389
1733
  throws SQLException {
1390
1734
  final boolean value = resultSet.getBoolean(column);
1391
1735
  if ( resultSet.wasNull() ) return runtime.getNil();
1392
1736
  return booleanToRuby(runtime, resultSet, value);
1393
1737
  }
1394
-
1738
+
1395
1739
  @Deprecated
1396
1740
  protected IRubyObject booleanToRuby(
1397
1741
  final Ruby runtime, final ResultSet resultSet, final boolean value)
@@ -1399,10 +1743,10 @@ public class RubyJdbcConnection extends RubyObject {
1399
1743
  if ( value == false && resultSet.wasNull() ) return runtime.getNil();
1400
1744
  return runtime.newBoolean(value);
1401
1745
  }
1402
-
1746
+
1403
1747
  protected static int streamBufferSize = 2048;
1404
1748
 
1405
- protected IRubyObject streamToRuby(
1749
+ protected IRubyObject streamToRuby(final ThreadContext context,
1406
1750
  final Ruby runtime, final ResultSet resultSet, final int column)
1407
1751
  throws SQLException, IOException {
1408
1752
  final InputStream stream = resultSet.getBinaryStream(column);
@@ -1412,7 +1756,7 @@ public class RubyJdbcConnection extends RubyObject {
1412
1756
  }
1413
1757
  finally { if ( stream != null ) stream.close(); }
1414
1758
  }
1415
-
1759
+
1416
1760
  @Deprecated
1417
1761
  protected IRubyObject streamToRuby(
1418
1762
  final Ruby runtime, final ResultSet resultSet, final InputStream stream)
@@ -1421,7 +1765,7 @@ public class RubyJdbcConnection extends RubyObject {
1421
1765
 
1422
1766
  final int bufSize = streamBufferSize;
1423
1767
  final ByteList string = new ByteList(bufSize);
1424
-
1768
+
1425
1769
  final byte[] buf = new byte[bufSize];
1426
1770
  for (int len = stream.read(buf); len != -1; len = stream.read(buf)) {
1427
1771
  string.append(buf, 0, len);
@@ -1430,7 +1774,7 @@ public class RubyJdbcConnection extends RubyObject {
1430
1774
  return runtime.newString(string);
1431
1775
  }
1432
1776
 
1433
- protected IRubyObject readerToRuby(
1777
+ protected IRubyObject readerToRuby(final ThreadContext context,
1434
1778
  final Ruby runtime, final ResultSet resultSet, final int column)
1435
1779
  throws SQLException, IOException {
1436
1780
  final Reader reader = resultSet.getCharacterStream(column);
@@ -1440,7 +1784,7 @@ public class RubyJdbcConnection extends RubyObject {
1440
1784
  }
1441
1785
  finally { if ( reader != null ) reader.close(); }
1442
1786
  }
1443
-
1787
+
1444
1788
  @Deprecated
1445
1789
  protected IRubyObject readerToRuby(
1446
1790
  final Ruby runtime, final ResultSet resultSet, final Reader reader)
@@ -1449,46 +1793,45 @@ public class RubyJdbcConnection extends RubyObject {
1449
1793
 
1450
1794
  final int bufSize = streamBufferSize;
1451
1795
  final StringBuilder string = new StringBuilder(bufSize);
1452
-
1796
+
1453
1797
  final char[] buf = new char[bufSize];
1454
1798
  for (int len = reader.read(buf); len != -1; len = reader.read(buf)) {
1455
1799
  string.append(buf, 0, len);
1456
1800
  }
1457
-
1801
+
1458
1802
  return RubyString.newUnicodeString(runtime, string.toString());
1459
1803
  }
1460
1804
 
1461
- protected IRubyObject objectToRuby(
1805
+ protected IRubyObject objectToRuby(final ThreadContext context,
1462
1806
  final Ruby runtime, final ResultSet resultSet, final int column)
1463
1807
  throws SQLException {
1464
1808
  final Object value = resultSet.getObject(column);
1465
-
1809
+
1466
1810
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1467
-
1811
+
1468
1812
  return JavaUtil.convertJavaToRuby(runtime, value);
1469
1813
  }
1470
-
1471
- protected IRubyObject arrayToRuby(
1814
+
1815
+ protected IRubyObject arrayToRuby(final ThreadContext context,
1472
1816
  final Ruby runtime, final ResultSet resultSet, final int column)
1473
1817
  throws SQLException {
1474
1818
  final Array value = resultSet.getArray(column);
1475
1819
  try {
1476
1820
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
1477
-
1821
+
1478
1822
  final RubyArray array = runtime.newArray();
1479
1823
 
1480
1824
  final ResultSet arrayResult = value.getResultSet(); // 1: index, 2: value
1481
1825
  final int baseType = value.getBaseType();
1482
1826
  while ( arrayResult.next() ) {
1483
- IRubyObject element = jdbcToRuby(runtime, 2, baseType, arrayResult);
1484
- array.append(element);
1827
+ array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
1485
1828
  }
1486
1829
  return array;
1487
1830
  }
1488
1831
  finally { value.free(); }
1489
1832
  }
1490
-
1491
- protected IRubyObject xmlToRuby(
1833
+
1834
+ protected IRubyObject xmlToRuby(final ThreadContext context,
1492
1835
  final Ruby runtime, final ResultSet resultSet, final int column)
1493
1836
  throws SQLException {
1494
1837
  final SQLXML xml = resultSet.getSQLXML(column);
@@ -1497,19 +1840,19 @@ public class RubyJdbcConnection extends RubyObject {
1497
1840
  }
1498
1841
  finally { xml.free(); }
1499
1842
  }
1500
-
1501
- /* protected */ void setStatementParameters(final ThreadContext context,
1502
- final Connection connection, final PreparedStatement statement,
1843
+
1844
+ protected void setStatementParameters(final ThreadContext context,
1845
+ final Connection connection, final PreparedStatement statement,
1503
1846
  final List<?> binds) throws SQLException {
1504
-
1847
+
1505
1848
  final Ruby runtime = context.getRuntime();
1506
-
1849
+
1507
1850
  for ( int i = 0; i < binds.size(); i++ ) {
1508
1851
  // [ [ column1, param1 ], [ column2, param2 ], ... ]
1509
1852
  Object param = binds.get(i); IRubyObject column = null;
1510
1853
  if ( param.getClass() == RubyArray.class ) {
1511
1854
  final RubyArray _param = (RubyArray) param;
1512
- column = _param.eltInternal(0); param = _param.eltInternal(1);
1855
+ column = _param.eltInternal(0); param = _param.eltInternal(1);
1513
1856
  }
1514
1857
  else if ( param instanceof List ) {
1515
1858
  final List<?> _param = (List<?>) param;
@@ -1519,7 +1862,7 @@ public class RubyJdbcConnection extends RubyObject {
1519
1862
  final Object[] _param = (Object[]) param;
1520
1863
  column = (IRubyObject) _param[0]; param = _param[1];
1521
1864
  }
1522
-
1865
+
1523
1866
  final IRubyObject type;
1524
1867
  if ( column != null && ! column.isNil() ) {
1525
1868
  type = column.callMethod(context, "type");
@@ -1527,85 +1870,84 @@ public class RubyJdbcConnection extends RubyObject {
1527
1870
  else {
1528
1871
  type = null;
1529
1872
  }
1530
-
1873
+
1531
1874
  setStatementParameter(context, runtime, connection, statement, i + 1, param, type);
1532
1875
  }
1533
1876
  }
1534
1877
 
1535
- /* protected */ void setStatementParameter(final ThreadContext context,
1536
- final Ruby runtime, final Connection connection,
1878
+ protected void setStatementParameter(final ThreadContext context,
1879
+ final Ruby runtime, final Connection connection,
1537
1880
  final PreparedStatement statement, final int index,
1538
1881
  final Object value, final IRubyObject column) throws SQLException {
1539
-
1540
- final RubySymbol columnType = resolveColumnType(context, runtime, column);
1541
- final int type = jdbcTypeFor(runtime, column, columnType, value);
1542
-
1543
- // TODO pass column with (JDBC) type to methods :
1544
-
1882
+
1883
+ final int type = jdbcTypeFor(context, runtime, column, value);
1884
+
1545
1885
  switch (type) {
1546
1886
  case Types.TINYINT:
1547
1887
  case Types.SMALLINT:
1548
1888
  case Types.INTEGER:
1549
- if ( value instanceof RubyBignum ) {
1550
- setBigIntegerParameter(runtime, connection, statement, index, type, (RubyBignum) value);
1889
+ if ( value instanceof RubyBignum ) { // e.g. HSQLDB / H2 report JDBC type 4
1890
+ setBigIntegerParameter(context, connection, statement, index, (RubyBignum) value, column, type);
1891
+ }
1892
+ else {
1893
+ setIntegerParameter(context, connection, statement, index, value, column, type);
1551
1894
  }
1552
- setIntegerParameter(runtime, connection, statement, index, type, value);
1553
1895
  break;
1554
1896
  case Types.BIGINT:
1555
- setBigIntegerParameter(runtime, connection, statement, index, type, value);
1897
+ setBigIntegerParameter(context, connection, statement, index, value, column, type);
1556
1898
  break;
1557
1899
  case Types.REAL:
1558
1900
  case Types.FLOAT:
1559
1901
  case Types.DOUBLE:
1560
- setDoubleParameter(runtime, connection, statement, index, type, value);
1902
+ setDoubleParameter(context, connection, statement, index, value, column, type);
1561
1903
  break;
1562
1904
  case Types.NUMERIC:
1563
1905
  case Types.DECIMAL:
1564
- setDecimalParameter(runtime, connection, statement, index, type, value);
1906
+ setDecimalParameter(context, connection, statement, index, value, column, type);
1565
1907
  break;
1566
1908
  case Types.DATE:
1567
- setDateParameter(runtime, connection, statement, index, type, value);
1909
+ setDateParameter(context, connection, statement, index, value, column, type);
1568
1910
  break;
1569
1911
  case Types.TIME:
1570
- setTimeParameter(runtime, connection, statement, index, type, value);
1912
+ setTimeParameter(context, connection, statement, index, value, column, type);
1571
1913
  break;
1572
1914
  case Types.TIMESTAMP:
1573
- setTimestampParameter(runtime, connection, statement, index, type, value);
1915
+ setTimestampParameter(context, connection, statement, index, value, column, type);
1574
1916
  break;
1575
1917
  case Types.BIT:
1576
1918
  case Types.BOOLEAN:
1577
- setBooleanParameter(runtime, connection, statement, index, type, value);
1919
+ setBooleanParameter(context, connection, statement, index, value, column, type);
1578
1920
  break;
1579
1921
  case Types.SQLXML:
1580
- setXmlParameter(runtime, connection, statement, index, type, value);
1922
+ setXmlParameter(context, connection, statement, index, value, column, type);
1581
1923
  break;
1582
1924
  case Types.ARRAY:
1583
- setArrayParameter(runtime, connection, statement, index, type, value);
1925
+ setArrayParameter(context, connection, statement, index, value, column, type);
1584
1926
  break;
1585
1927
  case Types.JAVA_OBJECT:
1586
1928
  case Types.OTHER:
1587
- setObjectParameter(runtime, connection, statement, index, type, value);
1929
+ setObjectParameter(context, connection, statement, index, value, column, type);
1588
1930
  break;
1589
1931
  case Types.BINARY:
1590
1932
  case Types.VARBINARY:
1591
1933
  case Types.LONGVARBINARY:
1592
1934
  case Types.BLOB:
1593
- setBlobParameter(runtime, connection, statement, index, type, value);
1935
+ setBlobParameter(context, connection, statement, index, value, column, type);
1594
1936
  break;
1595
1937
  case Types.CLOB:
1596
1938
  case Types.NCLOB: // JDBC 4.0
1597
- setClobParameter(runtime, connection, statement, index, type, value);
1939
+ setClobParameter(context, connection, statement, index, value, column, type);
1598
1940
  break;
1599
1941
  case Types.CHAR:
1600
1942
  case Types.VARCHAR:
1601
1943
  case Types.NCHAR: // JDBC 4.0
1602
1944
  case Types.NVARCHAR: // JDBC 4.0
1603
1945
  default:
1604
- setStringParameter(runtime, connection, statement, index, type, value);
1946
+ setStringParameter(context, connection, statement, index, value, column, type);
1605
1947
  }
1606
1948
  }
1607
1949
 
1608
- @Deprecated
1950
+ @Deprecated // NOTE: only used from deprecated methods
1609
1951
  private void setPreparedStatementValues(final ThreadContext context,
1610
1952
  final Connection connection, final PreparedStatement statement,
1611
1953
  final IRubyObject valuesArg, final IRubyObject typesArg) throws SQLException {
@@ -1614,13 +1956,13 @@ public class RubyJdbcConnection extends RubyObject {
1614
1956
  final RubyArray types = (RubyArray) typesArg; // column types
1615
1957
  for( int i = 0, j = values.getLength(); i < j; i++ ) {
1616
1958
  setStatementParameter(
1617
- context, runtime, connection, statement, i + 1,
1959
+ context, runtime, connection, statement, i + 1,
1618
1960
  values.eltInternal(i), types.eltInternal(i)
1619
1961
  );
1620
1962
  }
1621
1963
  }
1622
1964
 
1623
- private RubySymbol resolveColumnType(final ThreadContext context, final Ruby runtime,
1965
+ private RubySymbol resolveColumnType(final ThreadContext context, final Ruby runtime,
1624
1966
  final IRubyObject column) {
1625
1967
  if ( column instanceof RubySymbol ) { // deprecated behavior
1626
1968
  return (RubySymbol) column;
@@ -1633,17 +1975,35 @@ public class RubyJdbcConnection extends RubyObject {
1633
1975
  return ( (RubyString) column ).intern();
1634
1976
  }
1635
1977
  }
1636
-
1978
+
1637
1979
  if ( column == null || column.isNil() ) {
1638
- throw runtime.newArgumentError("nil column passed");
1980
+ throw runtime.newArgumentError("nil column passed");
1639
1981
  }
1640
1982
  return (RubySymbol) column.callMethod(context, "type");
1641
1983
  }
1642
-
1643
- /* protected */ int jdbcTypeFor(final Ruby runtime, final IRubyObject column,
1644
- final RubySymbol columnType, final Object value) throws SQLException {
1645
-
1646
- final String internedType = columnType.asJavaString();
1984
+
1985
+ protected int jdbcTypeFor(final ThreadContext context, final Ruby runtime,
1986
+ final IRubyObject column, final Object value) throws SQLException {
1987
+
1988
+ final String internedType;
1989
+ if ( column != null && ! column.isNil() ) {
1990
+ final RubySymbol columnType = resolveColumnType(context, runtime, column);
1991
+ internedType = columnType.asJavaString();
1992
+ }
1993
+ else {
1994
+ if ( value instanceof RubyInteger ) {
1995
+ internedType = "integer";
1996
+ }
1997
+ else if ( value instanceof RubyNumeric ) {
1998
+ internedType = "float";
1999
+ }
2000
+ else if ( value instanceof RubyTime ) {
2001
+ internedType = "timestamp";
2002
+ }
2003
+ else {
2004
+ internedType = "string";
2005
+ }
2006
+ }
1647
2007
 
1648
2008
  if ( internedType == (Object) "string" ) return Types.VARCHAR;
1649
2009
  else if ( internedType == (Object) "text" ) return Types.CLOB;
@@ -1652,7 +2012,7 @@ public class RubyJdbcConnection extends RubyObject {
1652
2012
  else if ( internedType == (Object) "float" ) return Types.FLOAT;
1653
2013
  else if ( internedType == (Object) "date" ) return Types.DATE;
1654
2014
  else if ( internedType == (Object) "time" ) return Types.TIME;
1655
- else if ( internedType == (Object) "datetime") return Types.TIMESTAMP;
2015
+ else if ( internedType == (Object) "datetime" ) return Types.TIMESTAMP;
1656
2016
  else if ( internedType == (Object) "timestamp" ) return Types.TIMESTAMP;
1657
2017
  else if ( internedType == (Object) "binary" ) return Types.BLOB;
1658
2018
  else if ( internedType == (Object) "boolean" ) return Types.BOOLEAN;
@@ -1660,12 +2020,13 @@ public class RubyJdbcConnection extends RubyObject {
1660
2020
  else if ( internedType == (Object) "array" ) return Types.ARRAY;
1661
2021
  else return Types.OTHER; // -1 as well as 0 are used in Types
1662
2022
  }
1663
-
1664
- /* protected */ void setIntegerParameter(final Ruby runtime, final Connection connection,
1665
- final PreparedStatement statement, final int index, final int type,
1666
- final Object value) throws SQLException {
2023
+
2024
+ protected void setIntegerParameter(final ThreadContext context,
2025
+ final Connection connection, final PreparedStatement statement,
2026
+ final int index, final Object value,
2027
+ final IRubyObject column, final int type) throws SQLException {
1667
2028
  if ( value instanceof IRubyObject ) {
1668
- setIntegerParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2029
+ setIntegerParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1669
2030
  }
1670
2031
  else {
1671
2032
  if ( value == null ) statement.setNull(index, Types.INTEGER);
@@ -1675,25 +2036,32 @@ public class RubyJdbcConnection extends RubyObject {
1675
2036
  }
1676
2037
  }
1677
2038
 
1678
- /* protected */ void setIntegerParameter(final Ruby runtime, final Connection connection,
1679
- final PreparedStatement statement, final int index, final int type,
1680
- final IRubyObject value) throws SQLException {
2039
+ protected void setIntegerParameter(final ThreadContext context,
2040
+ final Connection connection, final PreparedStatement statement,
2041
+ final int index, final IRubyObject value,
2042
+ final IRubyObject column, final int type) throws SQLException {
1681
2043
  if ( value.isNil() ) statement.setNull(index, Types.INTEGER);
1682
2044
  else {
1683
2045
  if ( value instanceof RubyFixnum ) {
1684
2046
  statement.setLong(index, ((RubyFixnum) value).getLongValue());
1685
2047
  }
1686
- else {
2048
+ else if ( value instanceof RubyNumeric ) {
2049
+ // NOTE: fix2int will call value.convertToIngeter for non-numeric
2050
+ // types which won't work for Strings since it uses `to_int` ...
1687
2051
  statement.setInt(index, RubyNumeric.fix2int(value));
1688
2052
  }
2053
+ else {
2054
+ statement.setLong(index, value.convertToInteger("to_i").getLongValue());
2055
+ }
1689
2056
  }
1690
2057
  }
1691
-
1692
- /* protected */ void setBigIntegerParameter(final Ruby runtime, final Connection connection,
1693
- final PreparedStatement statement, final int index, final int type,
1694
- final Object value) throws SQLException {
2058
+
2059
+ protected void setBigIntegerParameter(final ThreadContext context,
2060
+ final Connection connection, final PreparedStatement statement,
2061
+ final int index, final Object value,
2062
+ final IRubyObject column, final int type) throws SQLException {
1695
2063
  if ( value instanceof IRubyObject ) {
1696
- setBigIntegerParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2064
+ setBigIntegerParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1697
2065
  }
1698
2066
  else {
1699
2067
  if ( value == null ) statement.setNull(index, Types.BIGINT);
@@ -1710,25 +2078,29 @@ public class RubyJdbcConnection extends RubyObject {
1710
2078
  }
1711
2079
  }
1712
2080
  }
1713
-
1714
- /* protected */ void setBigIntegerParameter(final Ruby runtime, final Connection connection,
1715
- final PreparedStatement statement, final int index, final int type,
1716
- final IRubyObject value) throws SQLException {
2081
+
2082
+ protected void setBigIntegerParameter(final ThreadContext context,
2083
+ final Connection connection, final PreparedStatement statement,
2084
+ final int index, final IRubyObject value,
2085
+ final IRubyObject column, final int type) throws SQLException {
1717
2086
  if ( value.isNil() ) statement.setNull(index, Types.INTEGER);
1718
2087
  else {
1719
2088
  if ( value instanceof RubyBignum ) {
1720
2089
  setLongOrDecimalParameter(statement, index, ((RubyBignum) value).getValue());
1721
2090
  }
1722
- else {
2091
+ else if ( value instanceof RubyInteger ) {
1723
2092
  statement.setLong(index, ((RubyInteger) value).getLongValue());
1724
2093
  }
2094
+ else {
2095
+ setLongOrDecimalParameter(statement, index, value.convertToInteger("to_i").getBigIntegerValue());
2096
+ }
1725
2097
  }
1726
2098
  }
1727
-
2099
+
1728
2100
  private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
1729
2101
  private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
1730
-
1731
- /* protected */ static void setLongOrDecimalParameter(final PreparedStatement statement,
2102
+
2103
+ protected static void setLongOrDecimalParameter(final PreparedStatement statement,
1732
2104
  final int index, final BigInteger value) throws SQLException {
1733
2105
  if ( value.compareTo(MAX_LONG) <= 0 // -1 intValue < MAX_VALUE
1734
2106
  && value.compareTo(MIN_LONG) >= 0 ) {
@@ -1738,12 +2110,13 @@ public class RubyJdbcConnection extends RubyObject {
1738
2110
  statement.setBigDecimal(index, new BigDecimal(value));
1739
2111
  }
1740
2112
  }
1741
-
1742
- /* protected */ void setDoubleParameter(final Ruby runtime, final Connection connection,
1743
- final PreparedStatement statement, final int index, final int type,
1744
- final Object value) throws SQLException {
2113
+
2114
+ protected void setDoubleParameter(final ThreadContext context,
2115
+ final Connection connection, final PreparedStatement statement,
2116
+ final int index, final Object value,
2117
+ final IRubyObject column, final int type) throws SQLException {
1745
2118
  if ( value instanceof IRubyObject ) {
1746
- setDoubleParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2119
+ setDoubleParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1747
2120
  }
1748
2121
  else {
1749
2122
  if ( value == null ) statement.setNull(index, Types.DOUBLE);
@@ -1753,20 +2126,27 @@ public class RubyJdbcConnection extends RubyObject {
1753
2126
  }
1754
2127
  }
1755
2128
 
1756
- /* protected */ void setDoubleParameter(final Ruby runtime, final Connection connection,
1757
- final PreparedStatement statement, final int index, final int type,
1758
- final IRubyObject value) throws SQLException {
2129
+ protected void setDoubleParameter(final ThreadContext context,
2130
+ final Connection connection, final PreparedStatement statement,
2131
+ final int index, final IRubyObject value,
2132
+ final IRubyObject column, final int type) throws SQLException {
1759
2133
  if ( value.isNil() ) statement.setNull(index, Types.DOUBLE);
1760
2134
  else {
1761
- statement.setDouble(index, ((RubyNumeric) value).getDoubleValue());
2135
+ if ( value instanceof RubyNumeric ) {
2136
+ statement.setDouble(index, ((RubyNumeric) value).getDoubleValue());
2137
+ }
2138
+ else {
2139
+ statement.setDouble(index, value.convertToFloat().getDoubleValue());
2140
+ }
1762
2141
  }
1763
2142
  }
1764
-
1765
- /* protected */ void setDecimalParameter(final Ruby runtime, final Connection connection,
1766
- final PreparedStatement statement, final int index, final int type,
1767
- final Object value) throws SQLException {
2143
+
2144
+ protected void setDecimalParameter(final ThreadContext context,
2145
+ final Connection connection, final PreparedStatement statement,
2146
+ final int index, final Object value,
2147
+ final IRubyObject column, final int type) throws SQLException {
1768
2148
  if ( value instanceof IRubyObject ) {
1769
- setDecimalParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2149
+ setDecimalParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1770
2150
  }
1771
2151
  else {
1772
2152
  if ( value == null ) statement.setNull(index, Types.DECIMAL);
@@ -1784,40 +2164,49 @@ public class RubyJdbcConnection extends RubyObject {
1784
2164
  }
1785
2165
  }
1786
2166
 
1787
- /* protected */ void setDecimalParameter(final Ruby runtime, final Connection connection,
1788
- final PreparedStatement statement, final int index, final int type,
1789
- final IRubyObject value) throws SQLException {
2167
+ protected void setDecimalParameter(final ThreadContext context,
2168
+ final Connection connection, final PreparedStatement statement,
2169
+ final int index, final IRubyObject value,
2170
+ final IRubyObject column, final int type) throws SQLException {
1790
2171
  if ( value.isNil() ) statement.setNull(index, Types.DECIMAL);
1791
2172
  else {
1792
2173
  // NOTE: RubyBigDecimal moved into org.jruby.ext.bigdecimal (1.6 -> 1.7)
1793
2174
  if ( value.getMetaClass().getName().indexOf("BigDecimal") != -1 ) {
1794
- try { // reflect ((RubyBigDecimal) value).getValue() :
1795
- BigDecimal decValue = (BigDecimal) value.getClass().
1796
- getMethod("getValue", (Class<?>[]) null).
1797
- invoke(value, (Object[]) null);
1798
- statement.setBigDecimal(index, decValue);
1799
- }
1800
- catch (NoSuchMethodException e) {
1801
- throw new RuntimeException(e);
1802
- }
1803
- catch (IllegalAccessException e) {
1804
- throw new RuntimeException(e);
1805
- }
1806
- catch (InvocationTargetException e) {
1807
- throw new RuntimeException(e.getCause() != null ? e.getCause() : e);
1808
- }
2175
+ statement.setBigDecimal(index, getBigDecimalValue(value));
1809
2176
  }
1810
- else {
2177
+ else if ( value instanceof RubyNumeric ) {
1811
2178
  statement.setDouble(index, ((RubyNumeric) value).getDoubleValue());
1812
2179
  }
2180
+ else { // e.g. `BigDecimal '42.00000000000000000001'`
2181
+ IRubyObject v = callMethod(context, "BigDecimal", value);
2182
+ statement.setBigDecimal(index, getBigDecimalValue(v));
2183
+ }
2184
+ }
2185
+ }
2186
+
2187
+ private static BigDecimal getBigDecimalValue(final IRubyObject value) {
2188
+ try { // reflect ((RubyBigDecimal) value).getValue() :
2189
+ return (BigDecimal) value.getClass().
2190
+ getMethod("getValue", (Class<?>[]) null).
2191
+ invoke(value, (Object[]) null);
2192
+ }
2193
+ catch (NoSuchMethodException e) {
2194
+ throw new RuntimeException(e);
2195
+ }
2196
+ catch (IllegalAccessException e) {
2197
+ throw new RuntimeException(e);
2198
+ }
2199
+ catch (InvocationTargetException e) {
2200
+ throw new RuntimeException(e.getCause() != null ? e.getCause() : e);
1813
2201
  }
1814
2202
  }
1815
-
1816
- /* protected */ void setTimestampParameter(final Ruby runtime, final Connection connection,
1817
- final PreparedStatement statement, final int index, final int type,
1818
- final Object value) throws SQLException {
2203
+
2204
+ protected void setTimestampParameter(final ThreadContext context,
2205
+ final Connection connection, final PreparedStatement statement,
2206
+ final int index, final Object value,
2207
+ final IRubyObject column, final int type) throws SQLException {
1819
2208
  if ( value instanceof IRubyObject ) {
1820
- setTimestampParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2209
+ setTimestampParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1821
2210
  }
1822
2211
  else {
1823
2212
  if ( value == null ) statement.setNull(index, Types.TIMESTAMP);
@@ -1834,40 +2223,95 @@ public class RubyJdbcConnection extends RubyObject {
1834
2223
  }
1835
2224
  }
1836
2225
  }
1837
-
1838
- /* protected */ void setTimestampParameter(final Ruby runtime, final Connection connection,
1839
- final PreparedStatement statement, final int index, final int type,
1840
- final IRubyObject value) throws SQLException {
2226
+
2227
+ protected void setTimestampParameter(final ThreadContext context,
2228
+ final Connection connection, final PreparedStatement statement,
2229
+ final int index, IRubyObject value,
2230
+ final IRubyObject column, final int type) throws SQLException {
1841
2231
  if ( value.isNil() ) statement.setNull(index, Types.TIMESTAMP);
1842
2232
  else {
2233
+ value = getTimeInDefaultTimeZone(context, value);
1843
2234
  if ( value instanceof RubyTime ) {
1844
2235
  final RubyTime timeValue = (RubyTime) value;
1845
- final java.util.Date dateValue = timeValue.getJavaDate();
1846
-
1847
- long millis = dateValue.getTime();
1848
- Timestamp timestamp = new Timestamp(millis);
1849
- Calendar calendar = Calendar.getInstance();
1850
- calendar.setTime(dateValue);
1851
- if ( type != Types.DATE ) {
1852
- int micros = (int) timeValue.microseconds();
1853
- timestamp.setNanos( micros * 1000 ); // time.nsec ~ time.usec * 1000
2236
+ final DateTime dateTime = timeValue.getDateTime();
2237
+
2238
+ final Timestamp timestamp = new Timestamp( dateTime.getMillis() );
2239
+ if ( type != Types.DATE ) { // 1942-11-30T01:02:03.123_456
2240
+ // getMillis already set nanos to: 123_000_000
2241
+ final int usec = (int) timeValue.getUSec(); // 456 on JRuby
2242
+ if ( usec >= 0 ) {
2243
+ timestamp.setNanos( timestamp.getNanos() + usec * 1000 );
2244
+ }
1854
2245
  }
1855
- statement.setTimestamp( index, timestamp, calendar );
2246
+ statement.setTimestamp( index, timestamp, getTimeZoneCalendar(dateTime.getZone().getID()) );
1856
2247
  }
1857
- else {
1858
- final String stringValue = value.convertToString().toString();
1859
- // yyyy-[m]m-[d]d hh:mm:ss[.f...]
1860
- final Timestamp timestamp = Timestamp.valueOf( stringValue );
1861
- statement.setTimestamp( index, timestamp, Calendar.getInstance() );
2248
+ else if ( value instanceof RubyString ) { // yyyy-[m]m-[d]d hh:mm:ss[.f...]
2249
+ final Timestamp timestamp = Timestamp.valueOf( value.toString() );
2250
+ statement.setTimestamp( index, timestamp ); // assume local time-zone
2251
+ }
2252
+ else { // DateTime ( ActiveSupport::TimeWithZone.to_time )
2253
+ final RubyFloat timeValue = value.convertToFloat(); // to_f
2254
+ final Timestamp timestamp = convertToTimestamp(timeValue);
2255
+
2256
+ statement.setTimestamp( index, timestamp, getTimeZoneCalendar("GMT") );
1862
2257
  }
1863
2258
  }
1864
2259
  }
1865
-
1866
- /* protected */ void setTimeParameter(final Ruby runtime, final Connection connection,
1867
- final PreparedStatement statement, final int index, final int type,
1868
- final Object value) throws SQLException {
2260
+
2261
+ protected static Timestamp convertToTimestamp(final RubyFloat value) {
2262
+ final Timestamp timestamp = new Timestamp(value.getLongValue() * 1000); // millis
2263
+
2264
+ // for usec we shall not use: ((long) floatValue * 1000000) % 1000
2265
+ // if ( usec >= 0 ) timestamp.setNanos( timestamp.getNanos() + usec * 1000 );
2266
+ // due doubles inaccurate precision it's better to parse to_s :
2267
+ final ByteList strValue = ((RubyString) value.to_s()).getByteList();
2268
+ final int dot1 = strValue.lastIndexOf('.') + 1, dot4 = dot1 + 3;
2269
+ final int len = strValue.getRealSize() - strValue.getBegin();
2270
+ if ( dot1 > 0 && dot4 < len ) { // skip .123 but handle .1234
2271
+ final int end = Math.min( len - dot4, 3 );
2272
+ CharSequence usecSeq = strValue.subSequence(dot4, end);
2273
+ final int usec = Integer.parseInt( usecSeq.toString() );
2274
+ if ( usec < 10 ) { // 0.1234 ~> 4
2275
+ timestamp.setNanos( timestamp.getNanos() + usec * 100 );
2276
+ }
2277
+ else if ( usec < 100 ) { // 0.12345 ~> 45
2278
+ timestamp.setNanos( timestamp.getNanos() + usec * 10 );
2279
+ }
2280
+ else { // if ( usec < 1000 ) { // 0.123456 ~> 456
2281
+ timestamp.setNanos( timestamp.getNanos() + usec );
2282
+ }
2283
+ }
2284
+
2285
+ return timestamp;
2286
+ }
2287
+
2288
+ protected static IRubyObject getTimeInDefaultTimeZone(final ThreadContext context, IRubyObject value) {
2289
+ if ( value.respondsTo("to_time") ) {
2290
+ value = value.callMethod(context, "to_time");
2291
+ }
2292
+ final String method = isDefaultTimeZoneUTC(context) ? "getutc" : "getlocal";
2293
+ if ( value.respondsTo(method) ) {
2294
+ value = value.callMethod(context, method);
2295
+ }
2296
+ return value;
2297
+ }
2298
+
2299
+ protected static boolean isDefaultTimeZoneUTC(final ThreadContext context) {
2300
+ final RubyClass base = getBase(context.getRuntime());
2301
+ final String tz = base.callMethod(context, "default_timezone").toString(); // :utc
2302
+ return "utc".equalsIgnoreCase(tz);
2303
+ }
2304
+
2305
+ private static Calendar getTimeZoneCalendar(final String ID) {
2306
+ return Calendar.getInstance( TimeZone.getTimeZone(ID) );
2307
+ }
2308
+
2309
+ protected void setTimeParameter(final ThreadContext context,
2310
+ final Connection connection, final PreparedStatement statement,
2311
+ final int index, final Object value,
2312
+ final IRubyObject column, final int type) throws SQLException {
1869
2313
  if ( value instanceof IRubyObject ) {
1870
- setTimeParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2314
+ setTimeParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1871
2315
  }
1872
2316
  else {
1873
2317
  if ( value == null ) statement.setNull(index, Types.TIME);
@@ -1886,34 +2330,39 @@ public class RubyJdbcConnection extends RubyObject {
1886
2330
  }
1887
2331
  }
1888
2332
 
1889
- /* protected */ void setTimeParameter(final Ruby runtime, final Connection connection,
1890
- final PreparedStatement statement, final int index, final int type,
1891
- final IRubyObject value) throws SQLException {
2333
+ protected void setTimeParameter(final ThreadContext context,
2334
+ final Connection connection, final PreparedStatement statement,
2335
+ final int index, IRubyObject value,
2336
+ final IRubyObject column, final int type) throws SQLException {
1892
2337
  if ( value.isNil() ) statement.setNull(index, Types.TIME);
1893
2338
  else {
1894
- // setTimestampParameter(runtime, connection, statement, index, type, value);
2339
+ value = getTimeInDefaultTimeZone(context, value);
1895
2340
  if ( value instanceof RubyTime ) {
1896
2341
  final RubyTime timeValue = (RubyTime) value;
1897
- final java.util.Date dateValue = timeValue.getJavaDate();
2342
+ final DateTime dateTime = timeValue.getDateTime();
1898
2343
 
1899
- Time time = new Time(dateValue.getTime());
1900
- Calendar calendar = Calendar.getInstance();
1901
- calendar.setTime(dateValue);
1902
- statement.setTime( index, time, calendar );
2344
+ final Time time = new Time( dateTime.getMillis() );
2345
+ statement.setTime( index, time, getTimeZoneCalendar(dateTime.getZone().getID()) );
1903
2346
  }
1904
- else {
1905
- final String stringValue = value.convertToString().toString();
1906
- final Time time = Time.valueOf( stringValue );
1907
- statement.setTime( index, time, Calendar.getInstance() );
2347
+ else if ( value instanceof RubyString ) {
2348
+ final Time time = Time.valueOf( value.toString() );
2349
+ statement.setTime( index, time ); // assume local time-zone
2350
+ }
2351
+ else { // DateTime ( ActiveSupport::TimeWithZone.to_time )
2352
+ final RubyFloat timeValue = value.convertToFloat(); // to_f
2353
+ final Time time = new Time(timeValue.getLongValue() * 1000); // millis
2354
+ // java.sql.Time is expected to be only up to second precision
2355
+ statement.setTime( index, time, getTimeZoneCalendar("GMT") );
1908
2356
  }
1909
2357
  }
1910
2358
  }
1911
-
1912
- /* protected */ void setDateParameter(final Ruby runtime, final Connection connection,
1913
- final PreparedStatement statement, final int index, final int type,
1914
- final Object value) throws SQLException {
2359
+
2360
+ protected void setDateParameter(final ThreadContext context,
2361
+ final Connection connection, final PreparedStatement statement,
2362
+ final int index, final Object value,
2363
+ final IRubyObject column, final int type) throws SQLException {
1915
2364
  if ( value instanceof IRubyObject ) {
1916
- setDateParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2365
+ setDateParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1917
2366
  }
1918
2367
  else {
1919
2368
  if ( value == null ) statement.setNull(index, Types.DATE);
@@ -1932,34 +2381,33 @@ public class RubyJdbcConnection extends RubyObject {
1932
2381
  }
1933
2382
  }
1934
2383
 
1935
- /* protected */ void setDateParameter(final Ruby runtime, final Connection connection,
1936
- final PreparedStatement statement, final int index, final int type,
1937
- final IRubyObject value) throws SQLException {
2384
+ protected void setDateParameter(final ThreadContext context,
2385
+ final Connection connection, final PreparedStatement statement,
2386
+ final int index, IRubyObject value,
2387
+ final IRubyObject column, final int type) throws SQLException {
1938
2388
  if ( value.isNil() ) statement.setNull(index, Types.DATE);
1939
2389
  else {
1940
- // setTimestampParameter(runtime, connection, statement, index, type, value);
1941
- if ( value instanceof RubyTime ) {
1942
- final RubyTime timeValue = (RubyTime) value;
1943
- final java.util.Date dateValue = timeValue.getJavaDate();
1944
-
1945
- Date date = new Date(dateValue.getTime());
1946
- Calendar calendar = Calendar.getInstance();
1947
- calendar.setTime(dateValue);
1948
- statement.setDate( index, date, calendar );
1949
- }
1950
- else {
1951
- final String stringValue = value.convertToString().toString();
1952
- final Date date = Date.valueOf( stringValue );
1953
- statement.setDate( index, date, Calendar.getInstance() );
2390
+ //if ( value instanceof RubyString ) {
2391
+ // final Date date = Date.valueOf( value.toString() );
2392
+ // statement.setDate( index, date ); // assume local time-zone
2393
+ // return;
2394
+ //}
2395
+ if ( ! "Date".equals( value.getMetaClass().getName() ) ) {
2396
+ if ( value.respondsTo("to_date") ) {
2397
+ value = value.callMethod(context, "to_date");
2398
+ }
1954
2399
  }
2400
+ final Date date = Date.valueOf( value.asString().toString() ); // to_s
2401
+ statement.setDate( index, date /*, getTimeZoneCalendar("GMT") */ );
1955
2402
  }
1956
2403
  }
1957
-
1958
- /* protected */ void setBooleanParameter(final Ruby runtime, final Connection connection,
1959
- final PreparedStatement statement, final int index, final int type,
1960
- final Object value) throws SQLException {
2404
+
2405
+ protected void setBooleanParameter(final ThreadContext context,
2406
+ final Connection connection, final PreparedStatement statement,
2407
+ final int index, final Object value,
2408
+ final IRubyObject column, final int type) throws SQLException {
1961
2409
  if ( value instanceof IRubyObject ) {
1962
- setBooleanParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2410
+ setBooleanParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1963
2411
  }
1964
2412
  else {
1965
2413
  if ( value == null ) statement.setNull(index, Types.BOOLEAN);
@@ -1969,20 +2417,22 @@ public class RubyJdbcConnection extends RubyObject {
1969
2417
  }
1970
2418
  }
1971
2419
 
1972
- /* protected */ void setBooleanParameter(final Ruby runtime, final Connection connection,
1973
- final PreparedStatement statement, final int index, final int type,
1974
- final IRubyObject value) throws SQLException {
2420
+ protected void setBooleanParameter(final ThreadContext context,
2421
+ final Connection connection, final PreparedStatement statement,
2422
+ final int index, final IRubyObject value,
2423
+ final IRubyObject column, final int type) throws SQLException {
1975
2424
  if ( value.isNil() ) statement.setNull(index, Types.BOOLEAN);
1976
2425
  else {
1977
2426
  statement.setBoolean(index, value.isTrue());
1978
2427
  }
1979
2428
  }
1980
2429
 
1981
- /* protected */ void setStringParameter(final Ruby runtime, final Connection connection,
1982
- final PreparedStatement statement, final int index, final int type,
1983
- final Object value) throws SQLException {
2430
+ protected void setStringParameter(final ThreadContext context,
2431
+ final Connection connection, final PreparedStatement statement,
2432
+ final int index, final Object value,
2433
+ final IRubyObject column, final int type) throws SQLException {
1984
2434
  if ( value instanceof IRubyObject ) {
1985
- setStringParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2435
+ setStringParameter(context, connection, statement, index, (IRubyObject) value, column, type);
1986
2436
  }
1987
2437
  else {
1988
2438
  if ( value == null ) statement.setNull(index, Types.VARCHAR);
@@ -1992,20 +2442,22 @@ public class RubyJdbcConnection extends RubyObject {
1992
2442
  }
1993
2443
  }
1994
2444
 
1995
- /* protected */ void setStringParameter(final Ruby runtime, final Connection connection,
1996
- final PreparedStatement statement, final int index, final int type,
1997
- final IRubyObject value) throws SQLException {
2445
+ protected void setStringParameter(final ThreadContext context,
2446
+ final Connection connection, final PreparedStatement statement,
2447
+ final int index, final IRubyObject value,
2448
+ final IRubyObject column, final int type) throws SQLException {
1998
2449
  if ( value.isNil() ) statement.setNull(index, Types.VARCHAR);
1999
2450
  else {
2000
- statement.setString(index, value.convertToString().toString());
2451
+ statement.setString(index, value.asString().toString());
2001
2452
  }
2002
2453
  }
2003
2454
 
2004
- /* protected */ void setArrayParameter(final Ruby runtime, final Connection connection,
2005
- final PreparedStatement statement, final int index, final int type,
2006
- final Object value) throws SQLException {
2455
+ /* protected */ void setArrayParameter(final ThreadContext context,
2456
+ final Connection connection, final PreparedStatement statement,
2457
+ final int index, final Object value,
2458
+ final IRubyObject column, final int type) throws SQLException {
2007
2459
  if ( value instanceof IRubyObject ) {
2008
- setArrayParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2460
+ setArrayParameter(context, connection, statement, index, (IRubyObject) value, column, type);
2009
2461
  }
2010
2462
  else {
2011
2463
  if ( value == null ) statement.setNull(index, Types.ARRAY);
@@ -2017,9 +2469,10 @@ public class RubyJdbcConnection extends RubyObject {
2017
2469
  }
2018
2470
  }
2019
2471
 
2020
- /* protected */ void setArrayParameter(final Ruby runtime, final Connection connection,
2021
- final PreparedStatement statement, final int index, final int type,
2022
- final IRubyObject value) throws SQLException {
2472
+ /* protected */ void setArrayParameter(final ThreadContext context,
2473
+ final Connection connection, final PreparedStatement statement,
2474
+ final int index, final IRubyObject value,
2475
+ final IRubyObject column, final int type) throws SQLException {
2023
2476
  if ( value.isNil() ) statement.setNull(index, Types.ARRAY);
2024
2477
  else {
2025
2478
  // TODO get array element type name ?!
@@ -2027,12 +2480,13 @@ public class RubyJdbcConnection extends RubyObject {
2027
2480
  statement.setArray(index, array);
2028
2481
  }
2029
2482
  }
2030
-
2031
- /* protected */ void setXmlParameter(final Ruby runtime, final Connection connection,
2032
- final PreparedStatement statement, final int index, final int type,
2033
- final Object value) throws SQLException {
2483
+
2484
+ protected void setXmlParameter(final ThreadContext context,
2485
+ final Connection connection, final PreparedStatement statement,
2486
+ final int index, final Object value,
2487
+ final IRubyObject column, final int type) throws SQLException {
2034
2488
  if ( value instanceof IRubyObject ) {
2035
- setXmlParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2489
+ setXmlParameter(context, connection, statement, index, (IRubyObject) value, column, type);
2036
2490
  }
2037
2491
  else {
2038
2492
  if ( value == null ) statement.setNull(index, Types.SQLXML);
@@ -2044,50 +2498,64 @@ public class RubyJdbcConnection extends RubyObject {
2044
2498
  }
2045
2499
  }
2046
2500
 
2047
- /* protected */ void setXmlParameter(final Ruby runtime, final Connection connection,
2048
- final PreparedStatement statement, final int index, final int type,
2049
- final IRubyObject value) throws SQLException {
2501
+ protected void setXmlParameter(final ThreadContext context,
2502
+ final Connection connection, final PreparedStatement statement,
2503
+ final int index, final IRubyObject value,
2504
+ final IRubyObject column, final int type) throws SQLException {
2050
2505
  if ( value.isNil() ) statement.setNull(index, Types.SQLXML);
2051
2506
  else {
2052
2507
  SQLXML xml = connection.createSQLXML();
2053
- xml.setString(value.convertToString().toString());
2508
+ xml.setString(value.asString().toString());
2054
2509
  statement.setSQLXML(index, xml);
2055
2510
  }
2056
2511
  }
2057
2512
 
2058
- /* protected */ void setBlobParameter(final Ruby runtime, final Connection connection,
2059
- final PreparedStatement statement, final int index, final int type,
2060
- final Object value) throws SQLException {
2513
+ protected void setBlobParameter(final ThreadContext context,
2514
+ final Connection connection, final PreparedStatement statement,
2515
+ final int index, final Object value,
2516
+ final IRubyObject column, final int type) throws SQLException {
2061
2517
  if ( value instanceof IRubyObject ) {
2062
- setBlobParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2518
+ setBlobParameter(context, connection, statement, index, (IRubyObject) value, column, type);
2063
2519
  }
2064
2520
  else {
2065
2521
  if ( value == null ) statement.setNull(index, Types.BLOB);
2066
2522
  else {
2067
- statement.setBlob(index, (InputStream) value);
2523
+ //statement.setBlob(index, (InputStream) value);
2524
+ statement.setBinaryStream(index, (InputStream) value);
2068
2525
  }
2069
2526
  }
2070
2527
  }
2071
2528
 
2072
- /* protected */ void setBlobParameter(final Ruby runtime, final Connection connection,
2073
- final PreparedStatement statement, final int index, final int type,
2074
- final IRubyObject value) throws SQLException {
2529
+ protected void setBlobParameter(final ThreadContext context,
2530
+ final Connection connection, final PreparedStatement statement,
2531
+ final int index, final IRubyObject value,
2532
+ final IRubyObject column, final int type) throws SQLException {
2075
2533
  if ( value.isNil() ) statement.setNull(index, Types.BLOB);
2076
2534
  else {
2077
- if ( value instanceof RubyString ) {
2078
- statement.setBlob(index, new ByteArrayInputStream(((RubyString) value).getBytes()));
2535
+ if ( value instanceof RubyIO ) { // IO/File
2536
+ //statement.setBlob(index, ((RubyIO) value).getInStream());
2537
+ statement.setBinaryStream(index, ((RubyIO) value).getInStream());
2079
2538
  }
2080
- else { // assume IO/File
2081
- statement.setBlob(index, ((RubyIO) value).getInStream());
2539
+ else { // should be a RubyString
2540
+ final ByteList blob = value.asString().getByteList();
2541
+ statement.setBinaryStream(index,
2542
+ new ByteArrayInputStream(blob.unsafeBytes(), blob.getBegin(), blob.getRealSize()),
2543
+ blob.getRealSize() // length
2544
+ );
2545
+ // JDBC 4.0 :
2546
+ //statement.setBlob(index,
2547
+ // new ByteArrayInputStream(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize())
2548
+ //);
2082
2549
  }
2083
2550
  }
2084
2551
  }
2085
-
2086
- /* protected */ void setClobParameter(final Ruby runtime, final Connection connection,
2087
- final PreparedStatement statement, final int index, final int type,
2088
- final Object value) throws SQLException {
2552
+
2553
+ protected void setClobParameter(final ThreadContext context,
2554
+ final Connection connection, final PreparedStatement statement,
2555
+ final int index, final Object value,
2556
+ final IRubyObject column, final int type) throws SQLException {
2089
2557
  if ( value instanceof IRubyObject ) {
2090
- setClobParameter(runtime, connection, statement, index, type, (IRubyObject) value);
2558
+ setClobParameter(context, connection, statement, index, (IRubyObject) value, column, type);
2091
2559
  }
2092
2560
  else {
2093
2561
  if ( value == null ) statement.setNull(index, Types.CLOB);
@@ -2097,30 +2565,35 @@ public class RubyJdbcConnection extends RubyObject {
2097
2565
  }
2098
2566
  }
2099
2567
 
2100
- /* protected */ void setClobParameter(final Ruby runtime, final Connection connection,
2101
- final PreparedStatement statement, final int index, final int type,
2102
- final IRubyObject value) throws SQLException {
2568
+ protected void setClobParameter(final ThreadContext context,
2569
+ final Connection connection, final PreparedStatement statement,
2570
+ final int index, final IRubyObject value,
2571
+ final IRubyObject column, final int type) throws SQLException {
2103
2572
  if ( value.isNil() ) statement.setNull(index, Types.CLOB);
2104
2573
  else {
2105
- if ( value instanceof RubyString ) {
2106
- statement.setClob(index, new StringReader(((RubyString) value).decodeString()));
2107
- }
2108
- else { // assume IO/File
2574
+ if ( value instanceof RubyIO ) { // IO/File
2109
2575
  statement.setClob(index, new InputStreamReader(((RubyIO) value).getInStream()));
2110
2576
  }
2577
+ else { // should be a RubyString
2578
+ final String clob = value.asString().decodeString();
2579
+ statement.setCharacterStream(index, new StringReader(clob), clob.length());
2580
+ // JDBC 4.0 :
2581
+ //statement.setClob(index, new StringReader(clob));
2582
+ }
2111
2583
  }
2112
2584
  }
2113
-
2114
- /* protected */ void setObjectParameter(final Ruby runtime, final Connection connection,
2115
- final PreparedStatement statement, final int index, final int type,
2116
- Object value) throws SQLException {
2585
+
2586
+ protected void setObjectParameter(final ThreadContext context,
2587
+ final Connection connection, final PreparedStatement statement,
2588
+ final int index, Object value,
2589
+ final IRubyObject column, final int type) throws SQLException {
2117
2590
  if (value instanceof IRubyObject) {
2118
2591
  value = ((IRubyObject) value).toJava(Object.class);
2119
2592
  }
2120
2593
  if ( value == null ) statement.setNull(index, Types.JAVA_OBJECT);
2121
2594
  statement.setObject(index, value);
2122
2595
  }
2123
-
2596
+
2124
2597
  protected final Connection getConnection() {
2125
2598
  return getConnection(false);
2126
2599
  }
@@ -2133,11 +2606,11 @@ public class RubyJdbcConnection extends RubyObject {
2133
2606
  }
2134
2607
  return connection;
2135
2608
  }
2136
-
2609
+
2137
2610
  private synchronized IRubyObject setConnection(final Connection connection) {
2138
2611
  close( getConnection(false) ); // close previously open connection if there is one
2139
-
2140
- final IRubyObject rubyConnectionObject =
2612
+
2613
+ final IRubyObject rubyConnectionObject =
2141
2614
  connection != null ? convertJavaToRuby(connection) : getRuntime().getNil();
2142
2615
  setInstanceVariable( "@connection", rubyConnectionObject );
2143
2616
  dataWrapStruct(connection);
@@ -2166,16 +2639,16 @@ public class RubyJdbcConnection extends RubyObject {
2166
2639
  }
2167
2640
  finally { close(statement); }
2168
2641
  }
2169
-
2170
- private boolean tableExists(final Ruby runtime,
2642
+
2643
+ private boolean tableExists(final Ruby runtime,
2171
2644
  final Connection connection, final TableName tableName) throws SQLException {
2172
- final IRubyObject matchedTables =
2645
+ final IRubyObject matchedTables =
2173
2646
  matchTables(runtime, connection, tableName.catalog, tableName.schema, tableName.name, getTableTypes(), true);
2174
2647
  // NOTE: allow implementers to ignore checkExistsOnly paramater - empty array means does not exists
2175
2648
  return matchedTables != null && ! matchedTables.isNil() &&
2176
2649
  ( ! (matchedTables instanceof RubyArray) || ! ((RubyArray) matchedTables).isEmpty() );
2177
2650
  }
2178
-
2651
+
2179
2652
  /**
2180
2653
  * Match table names for given table name (pattern).
2181
2654
  * @param runtime
@@ -2184,25 +2657,25 @@ public class RubyJdbcConnection extends RubyObject {
2184
2657
  * @param schemaPattern
2185
2658
  * @param tablePattern
2186
2659
  * @param types table types
2187
- * @param checkExistsOnly an optimization flag (that might be ignored by sub-classes)
2188
- * whether the result really matters if true no need to map table names and a truth-y
2660
+ * @param checkExistsOnly an optimization flag (that might be ignored by sub-classes)
2661
+ * whether the result really matters if true no need to map table names and a truth-y
2189
2662
  * value is sufficient (except for an empty array which is considered that the table
2190
2663
  * did not exists).
2191
2664
  * @return matched (and Ruby mapped) table names
2192
- * @see #mapTables(Ruby, DatabaseMetaData, String, String, String, ResultSet)
2193
- * @throws SQLException
2665
+ * @see #mapTables(Ruby, DatabaseMetaData, String, String, String, ResultSet)
2666
+ * @throws SQLException
2194
2667
  */
2195
- protected IRubyObject matchTables(final Ruby runtime,
2668
+ protected IRubyObject matchTables(final Ruby runtime,
2196
2669
  final Connection connection,
2197
2670
  final String catalog, final String schemaPattern,
2198
2671
  final String tablePattern, final String[] types,
2199
2672
  final boolean checkExistsOnly) throws SQLException {
2200
-
2673
+
2201
2674
  final DatabaseMetaData metaData = connection.getMetaData();
2202
-
2675
+
2203
2676
  final String _tablePattern = caseConvertIdentifierForJdbc(metaData, tablePattern);
2204
2677
  final String _schemaPattern = caseConvertIdentifierForJdbc(metaData, schemaPattern);
2205
-
2678
+
2206
2679
  ResultSet tablesSet = null;
2207
2680
  try {
2208
2681
  tablesSet = metaData.getTables(catalog, _schemaPattern, _tablePattern, types);
@@ -2215,13 +2688,13 @@ public class RubyJdbcConnection extends RubyObject {
2215
2688
  }
2216
2689
  finally { close(tablesSet); }
2217
2690
  }
2218
-
2691
+
2219
2692
  // NOTE java.sql.DatabaseMetaData.getTables :
2220
2693
  protected final static int TABLES_TABLE_CAT = 1;
2221
2694
  protected final static int TABLES_TABLE_SCHEM = 2;
2222
2695
  protected final static int TABLES_TABLE_NAME = 3;
2223
2696
  protected final static int TABLES_TABLE_TYPE = 4;
2224
-
2697
+
2225
2698
  /**
2226
2699
  * @param runtime
2227
2700
  * @param metaData
@@ -2230,10 +2703,10 @@ public class RubyJdbcConnection extends RubyObject {
2230
2703
  * @param tablePattern
2231
2704
  * @param tablesSet
2232
2705
  * @return List<RubyString>
2233
- * @throws SQLException
2706
+ * @throws SQLException
2234
2707
  */
2235
- protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
2236
- final String catalog, final String schemaPattern, final String tablePattern,
2708
+ protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
2709
+ final String catalog, final String schemaPattern, final String tablePattern,
2237
2710
  final ResultSet tablesSet) throws SQLException {
2238
2711
  final RubyArray tables = runtime.newArray();
2239
2712
  while ( tablesSet.next() ) {
@@ -2246,8 +2719,8 @@ public class RubyJdbcConnection extends RubyObject {
2246
2719
 
2247
2720
  /**
2248
2721
  * NOTE: since 1.3.0 only present for binary compatibility (with extensions).
2249
- *
2250
- * @depreacated no longer used - replaced with
2722
+ *
2723
+ * @depreacated no longer used - replaced with
2251
2724
  * {@link #matchTables(Ruby, Connection, String, String, String, String[], boolean)}
2252
2725
  * please update your sub-class esp. if you're overriding this method !
2253
2726
  */
@@ -2262,7 +2735,7 @@ public class RubyJdbcConnection extends RubyObject {
2262
2735
  }
2263
2736
  };
2264
2737
  }
2265
-
2738
+
2266
2739
  protected static final int COLUMN_NAME = 4;
2267
2740
  protected static final int DATA_TYPE = 5;
2268
2741
  protected static final int TYPE_NAME = 6;
@@ -2272,7 +2745,7 @@ public class RubyJdbcConnection extends RubyObject {
2272
2745
  protected static final int IS_NULLABLE = 18;
2273
2746
 
2274
2747
  /**
2275
- * Create a string which represents a SQL type usable by Rails from the
2748
+ * Create a string which represents a SQL type usable by Rails from the
2276
2749
  * resultSet column meta-data
2277
2750
  * @param resultSet.
2278
2751
  */
@@ -2283,7 +2756,7 @@ public class RubyJdbcConnection extends RubyObject {
2283
2756
  final String type = resultSet.getString(TYPE_NAME);
2284
2757
  return formatTypeWithPrecisionAndScale(type, precision, scale);
2285
2758
  }
2286
-
2759
+
2287
2760
  protected static int intFromResultSet(
2288
2761
  final ResultSet resultSet, final int column) throws SQLException {
2289
2762
  final int precision = resultSet.getInt(column);
@@ -2292,7 +2765,7 @@ public class RubyJdbcConnection extends RubyObject {
2292
2765
 
2293
2766
  protected static String formatTypeWithPrecisionAndScale(
2294
2767
  final String type, final int precision, final int scale) {
2295
-
2768
+
2296
2769
  if ( precision <= 0 ) return type;
2297
2770
 
2298
2771
  final StringBuilder typeStr = new StringBuilder().append(type);
@@ -2307,12 +2780,11 @@ public class RubyJdbcConnection extends RubyObject {
2307
2780
  return defaultValue == null ? runtime.getNil() : RubyString.newUnicodeString(runtime, defaultValue);
2308
2781
  }
2309
2782
 
2310
- private IRubyObject unmarshalColumns(final ThreadContext context,
2311
- final DatabaseMetaData metaData, final ResultSet results, final ResultSet primaryKeys)
2783
+ private IRubyObject unmarshalColumns(final ThreadContext context,
2784
+ final DatabaseMetaData metaData, final ResultSet results, final ResultSet primaryKeys)
2312
2785
  throws SQLException {
2313
-
2786
+
2314
2787
  final Ruby runtime = context.getRuntime();
2315
- // RubyHash types = (RubyHash) native_database_types();
2316
2788
  final IRubyObject jdbcColumn = getJdbcColumnClass(context);
2317
2789
 
2318
2790
  final List<String> primarykeyNames = new ArrayList<String>();
@@ -2321,11 +2793,12 @@ public class RubyJdbcConnection extends RubyObject {
2321
2793
  }
2322
2794
 
2323
2795
  final List<IRubyObject> columns = new ArrayList<IRubyObject>();
2796
+ final IRubyObject config = getInstanceVariable("@config");
2324
2797
  while ( results.next() ) {
2325
2798
  final String colName = results.getString(COLUMN_NAME);
2326
2799
  IRubyObject column = jdbcColumn.callMethod(context, "new",
2327
2800
  new IRubyObject[] {
2328
- getInstanceVariable("@config"),
2801
+ config,
2329
2802
  RubyString.newUnicodeString( runtime, caseConvertIdentifierForRails(metaData, colName) ),
2330
2803
  defaultValueFromResultSet( runtime, results ),
2331
2804
  RubyString.newUnicodeString( runtime, typeFromResultSet(results) ),
@@ -2340,22 +2813,99 @@ public class RubyJdbcConnection extends RubyObject {
2340
2813
  return runtime.newArray(columns);
2341
2814
  }
2342
2815
 
2343
- protected static IRubyObject unmarshalIdResult(
2344
- final Ruby runtime, final Statement statement) throws SQLException {
2345
- final ResultSet genKeys = statement.getGeneratedKeys();
2346
- try {
2347
- if (genKeys.next() && genKeys.getMetaData().getColumnCount() > 0) {
2348
- return runtime.newFixnum( genKeys.getLong(1) );
2816
+ protected IRubyObject mapGeneratedKeys(
2817
+ final Ruby runtime, final Connection connection,
2818
+ final Statement statement) throws SQLException {
2819
+ return mapGeneratedKeys(runtime, connection, statement, null);
2820
+ }
2821
+
2822
+ protected IRubyObject mapGeneratedKeys(
2823
+ final Ruby runtime, final Connection connection,
2824
+ final Statement statement, final Boolean singleResult)
2825
+ throws SQLException {
2826
+ if ( supportsGeneratedKeys(connection) ) {
2827
+ ResultSet genKeys = null;
2828
+ try {
2829
+ genKeys = statement.getGeneratedKeys();
2830
+ // drivers might report a non-result statement without keys
2831
+ // e.g. on derby with SQL: 'SET ISOLATION = SERIALIZABLE'
2832
+ if ( genKeys == null ) return runtime.getNil();
2833
+ return doMapGeneratedKeys(runtime, genKeys, singleResult);
2349
2834
  }
2350
- return runtime.getNil();
2835
+ catch (SQLFeatureNotSupportedException e) {
2836
+ return null; // statement.getGeneratedKeys()
2837
+ }
2838
+ finally { close(genKeys); }
2351
2839
  }
2352
- finally { close(genKeys); }
2840
+ return null; // not supported
2841
+ }
2842
+
2843
+ protected final IRubyObject doMapGeneratedKeys(final Ruby runtime,
2844
+ final ResultSet genKeys, final Boolean singleResult)
2845
+ throws SQLException {
2846
+
2847
+ IRubyObject firstKey = null;
2848
+ // no generated keys - e.g. INSERT statement for a table that does
2849
+ // not have and auto-generated ID column :
2850
+ boolean next = genKeys.next() && genKeys.getMetaData().getColumnCount() > 0;
2851
+ // singleResult == null - guess if only single key returned
2852
+ if ( singleResult == null || singleResult.booleanValue() ) {
2853
+ if ( next ) {
2854
+ firstKey = mapGeneratedKey(runtime, genKeys);
2855
+ if ( singleResult != null || ! genKeys.next() ) {
2856
+ return firstKey;
2857
+ }
2858
+ next = true; // 2nd genKeys.next() returned true
2859
+ }
2860
+ else {
2861
+ /* if ( singleResult != null ) */ return runtime.getNil();
2862
+ }
2863
+ }
2864
+
2865
+ final RubyArray keys = runtime.newArray();
2866
+ if ( firstKey != null ) keys.append(firstKey); // singleResult == null
2867
+ while ( next ) {
2868
+ keys.append( mapGeneratedKey(runtime, genKeys) );
2869
+ next = genKeys.next();
2870
+ }
2871
+ return keys;
2872
+ }
2873
+
2874
+ protected IRubyObject mapGeneratedKey(final Ruby runtime, final ResultSet genKeys)
2875
+ throws SQLException {
2876
+ return runtime.newFixnum( genKeys.getLong(1) );
2877
+ }
2878
+
2879
+ protected IRubyObject mapGeneratedKeysOrUpdateCount(final ThreadContext context,
2880
+ final Connection connection, final Statement statement) throws SQLException {
2881
+ final Ruby runtime = context.getRuntime();
2882
+ final IRubyObject key = mapGeneratedKeys(runtime, connection, statement);
2883
+ return ( key == null || key.isNil() ) ? runtime.newFixnum( statement.getUpdateCount() ) : key;
2884
+ }
2885
+
2886
+ @Deprecated
2887
+ protected IRubyObject unmarshalKeysOrUpdateCount(final ThreadContext context,
2888
+ final Connection connection, final Statement statement) throws SQLException {
2889
+ return mapGeneratedKeysOrUpdateCount(context, connection, statement);
2890
+ }
2891
+
2892
+ private Boolean supportsGeneratedKeys;
2893
+
2894
+ protected boolean supportsGeneratedKeys(final Connection connection) throws SQLException {
2895
+ if (supportsGeneratedKeys == null) {
2896
+ synchronized(this) {
2897
+ if (supportsGeneratedKeys == null) {
2898
+ supportsGeneratedKeys = connection.getMetaData().supportsGetGeneratedKeys();
2899
+ }
2900
+ }
2901
+ }
2902
+ return supportsGeneratedKeys.booleanValue();
2353
2903
  }
2354
-
2904
+
2355
2905
  /**
2356
- * @deprecated no longer used - kept for binary compatibility, this method
2357
- * is confusing since it closes the result set it receives and thus was
2358
- * replaced with {@link #unmarshalIdResult(Ruby, Statement)}
2906
+ * @deprecated no longer used - kept for binary compatibility, this method
2907
+ * is confusing since it closes the result set it receives and thus was
2908
+ * replaced with {@link #mapGeneratedKeys(Ruby, Connection, Statement)}
2359
2909
  */
2360
2910
  @Deprecated
2361
2911
  public static IRubyObject unmarshal_id_result(
@@ -2369,10 +2919,10 @@ public class RubyJdbcConnection extends RubyObject {
2369
2919
  finally { close(genKeys); }
2370
2920
  }
2371
2921
 
2372
- protected IRubyObject unmarshalResults(final ThreadContext context,
2373
- final DatabaseMetaData metaData, final Statement statement,
2922
+ protected IRubyObject mapResults(final ThreadContext context,
2923
+ final DatabaseMetaData metaData, final Statement statement,
2374
2924
  final boolean downCase) throws SQLException {
2375
-
2925
+
2376
2926
  final Ruby runtime = context.getRuntime();
2377
2927
  IRubyObject result;
2378
2928
  ResultSet resultSet = statement.getResultSet();
@@ -2380,19 +2930,19 @@ public class RubyJdbcConnection extends RubyObject {
2380
2930
  result = mapToRawResult(context, runtime, metaData, resultSet, downCase);
2381
2931
  }
2382
2932
  finally { close(resultSet); }
2383
-
2933
+
2384
2934
  if ( ! statement.getMoreResults() ) return result;
2385
-
2935
+
2386
2936
  final List<IRubyObject> results = new ArrayList<IRubyObject>();
2387
2937
  results.add(result);
2388
-
2938
+
2389
2939
  do {
2390
2940
  resultSet = statement.getResultSet();
2391
2941
  try {
2392
2942
  result = mapToRawResult(context, runtime, metaData, resultSet, downCase);
2393
2943
  }
2394
2944
  finally { close(resultSet); }
2395
-
2945
+
2396
2946
  results.add(result);
2397
2947
  }
2398
2948
  while ( statement.getMoreResults() );
@@ -2405,11 +2955,11 @@ public class RubyJdbcConnection extends RubyObject {
2405
2955
  */
2406
2956
  @Deprecated
2407
2957
  protected IRubyObject unmarshalResult(final ThreadContext context,
2408
- final DatabaseMetaData metaData, final ResultSet resultSet,
2958
+ final DatabaseMetaData metaData, final ResultSet resultSet,
2409
2959
  final boolean downCase) throws SQLException {
2410
2960
  return mapToRawResult(context, context.getRuntime(), metaData, resultSet, downCase);
2411
2961
  }
2412
-
2962
+
2413
2963
  /**
2414
2964
  * Converts a JDBC result set into an array (rows) of hashes (row).
2415
2965
  *
@@ -2417,9 +2967,9 @@ public class RubyJdbcConnection extends RubyObject {
2417
2967
  */
2418
2968
  @SuppressWarnings("unchecked")
2419
2969
  private IRubyObject mapToRawResult(final ThreadContext context, final Ruby runtime,
2420
- final DatabaseMetaData metaData, final ResultSet resultSet,
2970
+ final DatabaseMetaData metaData, final ResultSet resultSet,
2421
2971
  final boolean downCase) throws SQLException {
2422
-
2972
+
2423
2973
  final ColumnData[] columns = extractColumns(runtime, metaData, resultSet, downCase);
2424
2974
 
2425
2975
  final RubyArray results = runtime.newArray();
@@ -2429,23 +2979,23 @@ public class RubyJdbcConnection extends RubyObject {
2429
2979
  }
2430
2980
 
2431
2981
  private IRubyObject yieldResultRows(final ThreadContext context, final Ruby runtime,
2432
- final DatabaseMetaData metaData, final ResultSet resultSet,
2982
+ final DatabaseMetaData metaData, final ResultSet resultSet,
2433
2983
  final Block block) throws SQLException {
2434
-
2984
+
2435
2985
  final ColumnData[] columns = extractColumns(runtime, metaData, resultSet, false);
2436
-
2986
+
2437
2987
  final IRubyObject[] blockArgs = new IRubyObject[columns.length];
2438
2988
  while ( resultSet.next() ) {
2439
2989
  for ( int i = 0; i < columns.length; i++ ) {
2440
2990
  final ColumnData column = columns[i];
2441
- blockArgs[i] = jdbcToRuby(runtime, column.index, column.type, resultSet);
2991
+ blockArgs[i] = jdbcToRuby(context, runtime, column.index, column.type, resultSet);
2442
2992
  }
2443
2993
  block.call( context, blockArgs );
2444
2994
  }
2445
-
2995
+
2446
2996
  return runtime.getNil(); // yielded result rows
2447
2997
  }
2448
-
2998
+
2449
2999
  /**
2450
3000
  * Extract columns from result set.
2451
3001
  * @param runtime
@@ -2453,25 +3003,25 @@ public class RubyJdbcConnection extends RubyObject {
2453
3003
  * @param resultSet
2454
3004
  * @param downCase
2455
3005
  * @return columns data
2456
- * @throws SQLException
3006
+ * @throws SQLException
2457
3007
  */
2458
- protected ColumnData[] extractColumns(final Ruby runtime,
2459
- final DatabaseMetaData metaData, final ResultSet resultSet,
3008
+ protected ColumnData[] extractColumns(final Ruby runtime,
3009
+ final DatabaseMetaData metaData, final ResultSet resultSet,
2460
3010
  final boolean downCase) throws SQLException {
2461
3011
  return setupColumns(runtime, metaData, resultSet.getMetaData(), downCase);
2462
3012
  }
2463
-
3013
+
2464
3014
  /**
2465
3015
  * @deprecated renamed and parameterized to {@link #withConnection(ThreadContext, SQLBlock)}
2466
3016
  */
2467
3017
  @Deprecated
2468
3018
  @SuppressWarnings("unchecked")
2469
- protected Object withConnectionAndRetry(final ThreadContext context, final SQLBlock block)
3019
+ protected Object withConnectionAndRetry(final ThreadContext context, final SQLBlock block)
2470
3020
  throws RaiseException {
2471
3021
  return withConnection(context, block);
2472
3022
  }
2473
-
2474
- protected <T> T withConnection(final ThreadContext context, final Callable<T> block)
3023
+
3024
+ protected <T> T withConnection(final ThreadContext context, final Callable<T> block)
2475
3025
  throws RaiseException {
2476
3026
  try {
2477
3027
  return withConnection(context, true, block);
@@ -2480,13 +3030,15 @@ public class RubyJdbcConnection extends RubyObject {
2480
3030
  return handleException(context, e); // should never happen
2481
3031
  }
2482
3032
  }
2483
-
2484
- private <T> T withConnection(final ThreadContext context, final boolean handleException, final Callable<T> block)
3033
+
3034
+ private <T> T withConnection(final ThreadContext context, final boolean handleException, final Callable<T> block)
2485
3035
  throws RaiseException, RuntimeException, SQLException {
2486
-
2487
- Throwable exception = null; int tries = 1; int i = 0;
2488
-
2489
- while ( i++ < tries ) {
3036
+
3037
+ Throwable exception = null; int retry = 0; int i = 0;
3038
+
3039
+ do {
3040
+ if ( retry > 0 ) reconnect(context); // we're retrying running block
3041
+
2490
3042
  final Connection connection = getConnection(true);
2491
3043
  boolean autoCommit = true; // retry in-case getAutoCommit throws
2492
3044
  try {
@@ -2497,18 +3049,21 @@ public class RubyJdbcConnection extends RubyObject {
2497
3049
  exception = e;
2498
3050
 
2499
3051
  if ( autoCommit ) { // do not retry if (inside) transactions
2500
- if ( i == 1 ) {
3052
+ if ( i == 0 ) {
2501
3053
  IRubyObject retryCount = getConfigValue(context, "retry_count");
2502
- tries = (int) retryCount.convertToInteger().getLongValue();
2503
- if ( tries <= 0 ) tries = 1;
3054
+ if ( ! retryCount.isNil() ) {
3055
+ retry = (int) retryCount.convertToInteger().getLongValue();
3056
+ }
2504
3057
  }
2505
- if ( ! isConnectionValid(context, connection) ) {
2506
- reconnect(context); continue; // retry connection (block) again
3058
+ if ( isConnectionValid(context, connection) ) {
3059
+ break; // connection not broken yet failed (do not retry)
2507
3060
  }
2508
- break; // connection not broken yet failed
3061
+ // we'll reconnect and retry calling block again
2509
3062
  }
3063
+ else break;
2510
3064
  }
2511
- }
3065
+ } while ( i++ < retry ); // i == 0, retry == 1 means we should retry once
3066
+
2512
3067
  // (retry) loop ended and we did not return ... exception != null
2513
3068
  if ( handleException ) {
2514
3069
  return handleException(context, getCause(exception)); // throws
@@ -2535,7 +3090,7 @@ public class RubyJdbcConnection extends RubyObject {
2535
3090
  return exception;
2536
3091
  }
2537
3092
 
2538
- protected <T> T handleException(final ThreadContext context, Throwable exception)
3093
+ protected <T> T handleException(final ThreadContext context, Throwable exception)
2539
3094
  throws RaiseException {
2540
3095
  // NOTE: we shall not wrap unchecked (runtime) exceptions into AR::Error
2541
3096
  // if it's really a misbehavior of the driver throwing a RuntimeExcepion
@@ -2546,7 +3101,7 @@ public class RubyJdbcConnection extends RubyObject {
2546
3101
  debugStackTrace(context, exception);
2547
3102
  throw wrapException(context, exception);
2548
3103
  }
2549
-
3104
+
2550
3105
  /**
2551
3106
  * @deprecated use {@link #wrapException(ThreadContext, Throwable)} instead
2552
3107
  * for overriding how exceptions are handled use {@link #handleException(ThreadContext, Throwable)}
@@ -2555,11 +3110,11 @@ public class RubyJdbcConnection extends RubyObject {
2555
3110
  protected RuntimeException wrap(final ThreadContext context, final Throwable exception) {
2556
3111
  return wrapException(context, exception);
2557
3112
  }
2558
-
3113
+
2559
3114
  protected RaiseException wrapException(final ThreadContext context, final Throwable exception) {
2560
3115
  final Ruby runtime = context.getRuntime();
2561
3116
  if ( exception instanceof SQLException ) {
2562
- final String message = SQLException.class == exception.getClass() ?
3117
+ final String message = SQLException.class == exception.getClass() ?
2563
3118
  exception.getMessage() : exception.toString(); // useful to easily see type on Ruby side
2564
3119
  final RaiseException error = wrapException(context, getJDBCError(runtime), exception, message);
2565
3120
  final int errorCode = ((SQLException) exception).getErrorCode();
@@ -2571,20 +3126,20 @@ public class RubyJdbcConnection extends RubyObject {
2571
3126
  return wrapException(context, getJDBCError(runtime), exception);
2572
3127
  }
2573
3128
 
2574
- protected static RaiseException wrapException(final ThreadContext context,
3129
+ protected static RaiseException wrapException(final ThreadContext context,
2575
3130
  final RubyClass errorClass, final Throwable exception) {
2576
3131
  return wrapException(context, errorClass, exception, exception.toString());
2577
3132
  }
2578
3133
 
2579
- protected static RaiseException wrapException(final ThreadContext context,
3134
+ protected static RaiseException wrapException(final ThreadContext context,
2580
3135
  final RubyClass errorClass, final Throwable exception, final String message) {
2581
3136
  final RaiseException error = new RaiseException(context.getRuntime(), errorClass, message, true);
2582
3137
  error.initCause(exception);
2583
3138
  return error;
2584
3139
  }
2585
-
2586
- private IRubyObject convertJavaToRuby(final Connection connection) {
2587
- return JavaUtil.convertJavaToRuby( getRuntime(), connection );
3140
+
3141
+ private IRubyObject convertJavaToRuby(final Object object) {
3142
+ return JavaUtil.convertJavaToRuby( getRuntime(), object );
2588
3143
  }
2589
3144
 
2590
3145
  /**
@@ -2599,25 +3154,25 @@ public class RubyJdbcConnection extends RubyObject {
2599
3154
  private static final byte[] WITH = new byte[] { 'w','i','t','h' };
2600
3155
  private static final byte[] SHOW = new byte[] { 's','h','o','w' };
2601
3156
  private static final byte[] CALL = new byte[]{ 'c','a','l','l' };
2602
-
3157
+
2603
3158
  @JRubyMethod(name = "select?", required = 1, meta = true, frame = false)
2604
- public static IRubyObject select_p(final ThreadContext context,
3159
+ public static IRubyObject select_p(final ThreadContext context,
2605
3160
  final IRubyObject self, final IRubyObject sql) {
2606
3161
  return context.getRuntime().newBoolean( isSelect(sql.convertToString()) );
2607
3162
  }
2608
3163
 
2609
3164
  private static boolean isSelect(final RubyString sql) {
2610
3165
  final ByteList sqlBytes = sql.getByteList();
2611
- return startsWithIgnoreCase(sqlBytes, SELECT) ||
3166
+ return startsWithIgnoreCase(sqlBytes, SELECT) ||
2612
3167
  startsWithIgnoreCase(sqlBytes, WITH) ||
2613
- startsWithIgnoreCase(sqlBytes, SHOW) ||
3168
+ startsWithIgnoreCase(sqlBytes, SHOW) ||
2614
3169
  startsWithIgnoreCase(sqlBytes, CALL);
2615
3170
  }
2616
-
3171
+
2617
3172
  private static final byte[] INSERT = new byte[] { 'i','n','s','e','r','t' };
2618
-
3173
+
2619
3174
  @JRubyMethod(name = "insert?", required = 1, meta = true, frame = false)
2620
- public static IRubyObject insert_p(final ThreadContext context,
3175
+ public static IRubyObject insert_p(final ThreadContext context,
2621
3176
  final IRubyObject self, final IRubyObject sql) {
2622
3177
  final ByteList sqlBytes = sql.convertToString().getByteList();
2623
3178
  return context.getRuntime().newBoolean(startsWithIgnoreCase(sqlBytes, INSERT));
@@ -2642,12 +3197,12 @@ public class RubyJdbcConnection extends RubyObject {
2642
3197
  }
2643
3198
  return end;
2644
3199
  }
2645
-
3200
+
2646
3201
  /**
2647
- * JDBC connection helper that handles mapping results to
3202
+ * JDBC connection helper that handles mapping results to
2648
3203
  * <code>ActiveRecord::Result</code> (available since AR-3.1).
2649
- *
2650
- * @see #populateFromResultSet(ThreadContext, Ruby, List, ResultSet, RubyJdbcConnection.ColumnData[])
3204
+ *
3205
+ * @see #populateFromResultSet(ThreadContext, Ruby, List, ResultSet, RubyJdbcConnection.ColumnData[])
2651
3206
  * @author kares
2652
3207
  */
2653
3208
  protected static class ResultHandler {
@@ -2662,11 +3217,11 @@ public class RubyJdbcConnection extends RubyObject {
2662
3217
 
2663
3218
  private static volatile ResultHandler instance;
2664
3219
 
2665
- public static ResultHandler getInstance(final ThreadContext context) {
3220
+ public static ResultHandler getInstance(final Ruby runtime) {
2666
3221
  if ( instance == null ) {
2667
3222
  synchronized(ResultHandler.class) {
2668
3223
  if ( instance == null ) { // fine to initialize twice
2669
- setInstance( new ResultHandler(context) );
3224
+ setInstance( new ResultHandler(runtime) );
2670
3225
  }
2671
3226
  }
2672
3227
  }
@@ -2677,24 +3232,23 @@ public class RubyJdbcConnection extends RubyObject {
2677
3232
  ResultHandler.instance = instance;
2678
3233
  }
2679
3234
 
2680
- protected ResultHandler(final ThreadContext context) {
2681
- final Ruby runtime = context.getRuntime();
3235
+ protected ResultHandler(final Ruby runtime) {
2682
3236
  final RubyClass result = getResult(runtime);
2683
3237
  USE_RESULT = result != null && result != runtime.getNilClass();
2684
3238
  }
2685
3239
 
2686
- public IRubyObject mapRow(final ThreadContext context, final Ruby runtime,
2687
- final ColumnData[] columns, final ResultSet resultSet,
3240
+ public IRubyObject mapRow(final ThreadContext context, final Ruby runtime,
3241
+ final ColumnData[] columns, final ResultSet resultSet,
2688
3242
  final RubyJdbcConnection connection) throws SQLException {
2689
-
3243
+
2690
3244
  if ( USE_RESULT ) { // maps a AR::Result row
2691
3245
  final RubyArray row = runtime.newArray(columns.length);
2692
-
3246
+
2693
3247
  for ( int i = 0; i < columns.length; i++ ) {
2694
3248
  final ColumnData column = columns[i];
2695
- row.append( connection.jdbcToRuby(runtime, column.index, column.type, resultSet) );
3249
+ row.append( connection.jdbcToRuby(context, runtime, column.index, column.type, resultSet) );
2696
3250
  }
2697
-
3251
+
2698
3252
  return row;
2699
3253
  }
2700
3254
  else {
@@ -2702,21 +3256,23 @@ public class RubyJdbcConnection extends RubyObject {
2702
3256
  }
2703
3257
  }
2704
3258
 
2705
- IRubyObject mapRawRow(final ThreadContext context, final Ruby runtime,
2706
- final ColumnData[] columns, final ResultSet resultSet,
3259
+ IRubyObject mapRawRow(final ThreadContext context, final Ruby runtime,
3260
+ final ColumnData[] columns, final ResultSet resultSet,
2707
3261
  final RubyJdbcConnection connection) throws SQLException {
2708
-
3262
+
2709
3263
  final RubyHash row = RubyHash.newHash(runtime);
2710
3264
 
2711
3265
  for ( int i = 0; i < columns.length; i++ ) {
2712
3266
  final ColumnData column = columns[i];
2713
- row.op_aset( context, column.name, connection.jdbcToRuby(runtime, column.index, column.type, resultSet) );
3267
+ row.op_aset( context, column.name,
3268
+ connection.jdbcToRuby(context, runtime, column.index, column.type, resultSet)
3269
+ );
2714
3270
  }
2715
3271
 
2716
3272
  return row;
2717
3273
  }
2718
-
2719
- public IRubyObject newResult(final ThreadContext context, final Ruby runtime,
3274
+
3275
+ public IRubyObject newResult(final ThreadContext context, final Ruby runtime,
2720
3276
  final ColumnData[] columns, final IRubyObject rows) { // rows array
2721
3277
  if ( USE_RESULT ) { // ActiveRecord::Result.new(columns, rows)
2722
3278
  final RubyClass result = getResult(runtime);
@@ -2724,14 +3280,14 @@ public class RubyJdbcConnection extends RubyObject {
2724
3280
  }
2725
3281
  return rows; // contains { 'col1' => 1, ... } Hash-es
2726
3282
  }
2727
-
2728
- private IRubyObject[] initArgs(final Ruby runtime,
3283
+
3284
+ private IRubyObject[] initArgs(final Ruby runtime,
2729
3285
  final ColumnData[] columns, final IRubyObject rows) {
2730
-
3286
+
2731
3287
  final IRubyObject[] args;
2732
-
3288
+
2733
3289
  final RubyArray cols = runtime.newArray(columns.length);
2734
-
3290
+
2735
3291
  if ( INIT_COLUMN_TYPES ) { // NOTE: NOT IMPLEMENTED
2736
3292
  for ( int i=0; i<columns.length; i++ ) {
2737
3293
  cols.add( columns[i].name );
@@ -2746,12 +3302,12 @@ public class RubyJdbcConnection extends RubyObject {
2746
3302
  }
2747
3303
  return args;
2748
3304
  }
2749
-
3305
+
2750
3306
  }
2751
3307
 
2752
-
3308
+
2753
3309
  protected static final class TableName {
2754
-
3310
+
2755
3311
  public final String catalog, schema, name;
2756
3312
 
2757
3313
  public TableName(String catalog, String schema, String table) {
@@ -2762,27 +3318,27 @@ public class RubyJdbcConnection extends RubyObject {
2762
3318
 
2763
3319
  @Override
2764
3320
  public String toString() {
2765
- return getClass().getName() +
3321
+ return getClass().getName() +
2766
3322
  "{catalog=" + catalog + ",schema=" + schema + ",name=" + name + "}";
2767
3323
  }
2768
-
3324
+
2769
3325
  }
2770
-
3326
+
2771
3327
  /**
2772
3328
  * Extract the table name components for the given name e.g. "mycat.sys.entries"
2773
- *
3329
+ *
2774
3330
  * @param connection
2775
- * @param catalog (optional) catalog to use if table name does not contain
3331
+ * @param catalog (optional) catalog to use if table name does not contain
2776
3332
  * the catalog prefix
2777
3333
  * @param schema (optional) schema to use if table name does not have one
2778
3334
  * @param tableName the table name
2779
3335
  * @return (parsed) table name
2780
- *
3336
+ *
2781
3337
  * @throws IllegalArgumentException for invalid table name format
2782
- * @throws SQLException
3338
+ * @throws SQLException
2783
3339
  */
2784
3340
  protected TableName extractTableName(
2785
- final Connection connection, String catalog, String schema,
3341
+ final Connection connection, String catalog, String schema,
2786
3342
  final String tableName) throws IllegalArgumentException, SQLException {
2787
3343
 
2788
3344
  final String[] nameParts = tableName.split("\\.");
@@ -2791,7 +3347,7 @@ public class RubyJdbcConnection extends RubyObject {
2791
3347
  }
2792
3348
 
2793
3349
  String name = tableName;
2794
-
3350
+
2795
3351
  if ( nameParts.length == 2 ) {
2796
3352
  schema = nameParts[0];
2797
3353
  name = nameParts[1];
@@ -2801,9 +3357,9 @@ public class RubyJdbcConnection extends RubyObject {
2801
3357
  schema = nameParts[1];
2802
3358
  name = nameParts[2];
2803
3359
  }
2804
-
3360
+
2805
3361
  final DatabaseMetaData metaData = connection.getMetaData();
2806
-
3362
+
2807
3363
  if (schema != null) {
2808
3364
  schema = caseConvertIdentifierForJdbc(metaData, schema);
2809
3365
  }
@@ -2816,19 +3372,19 @@ public class RubyJdbcConnection extends RubyObject {
2816
3372
 
2817
3373
  return new TableName(catalog, schema, name);
2818
3374
  }
2819
-
3375
+
2820
3376
  /**
2821
3377
  * @deprecated use {@link #extractTableName(Connection, String, String, String)}
2822
3378
  */
2823
3379
  @Deprecated
2824
3380
  protected TableName extractTableName(
2825
- final Connection connection, final String schema,
3381
+ final Connection connection, final String schema,
2826
3382
  final String tableName) throws IllegalArgumentException, SQLException {
2827
3383
  return extractTableName(connection, null, schema, tableName);
2828
3384
  }
2829
3385
 
2830
3386
  protected static final class ColumnData {
2831
-
3387
+
2832
3388
  public final RubyString name;
2833
3389
  public final int index;
2834
3390
  public final int type;
@@ -2838,13 +3394,13 @@ public class RubyJdbcConnection extends RubyObject {
2838
3394
  this.type = type;
2839
3395
  this.index = idx;
2840
3396
  }
2841
-
3397
+
2842
3398
  }
2843
-
3399
+
2844
3400
  private static ColumnData[] setupColumns(
2845
- final Ruby runtime,
3401
+ final Ruby runtime,
2846
3402
  final DatabaseMetaData metaData,
2847
- final ResultSetMetaData resultMetaData,
3403
+ final ResultSetMetaData resultMetaData,
2848
3404
  final boolean downCase) throws SQLException {
2849
3405
 
2850
3406
  final int columnCount = resultMetaData.getColumnCount();
@@ -2864,9 +3420,9 @@ public class RubyJdbcConnection extends RubyObject {
2864
3420
 
2865
3421
  return columns;
2866
3422
  }
2867
-
3423
+
2868
3424
  // JDBC API Helpers :
2869
-
3425
+
2870
3426
  protected static void close(final Connection connection) {
2871
3427
  if ( connection != null ) {
2872
3428
  try { connection.close(); }
@@ -2887,39 +3443,58 @@ public class RubyJdbcConnection extends RubyObject {
2887
3443
  catch (final Exception e) { /* NOOP */ }
2888
3444
  }
2889
3445
  }
2890
-
3446
+
2891
3447
  // DEBUG-ing helpers :
2892
-
3448
+
2893
3449
  private static boolean debug = Boolean.getBoolean("arjdbc.debug");
2894
3450
 
2895
3451
  public static boolean isDebug() { return debug; }
2896
-
3452
+
2897
3453
  public static void setDebug(boolean debug) {
2898
3454
  RubyJdbcConnection.debug = debug;
2899
3455
  }
2900
-
3456
+
3457
+ public static void debugMessage(final String msg) {
3458
+ debugMessage(null, msg);
3459
+ }
3460
+
2901
3461
  public static void debugMessage(final ThreadContext context, final String msg) {
2902
- if ( debug || context.runtime.isDebug() ) {
2903
- context.runtime.getOut().println(msg);
3462
+ if ( debug || ( context != null && context.runtime.isDebug() ) ) {
3463
+ final PrintStream out = context != null ? context.runtime.getOut() : System.out;
3464
+ out.println(msg);
2904
3465
  }
2905
3466
  }
2906
-
3467
+
2907
3468
  protected static void debugErrorSQL(final ThreadContext context, final String sql) {
2908
- if ( debug || context.runtime.isDebug() ) {
2909
- context.runtime.getOut().println("Error SQL: " + sql);
3469
+ if ( debug || ( context != null && context.runtime.isDebug() ) ) {
3470
+ final PrintStream out = context != null ? context.runtime.getOut() : System.out;
3471
+ out.println("Error SQL: '" + sql + "'");
2910
3472
  }
2911
3473
  }
2912
-
3474
+
3475
+ // disables full (Java) traces to be printed while DEBUG is on
3476
+ private static final Boolean debugStackTrace;
3477
+ static {
3478
+ String debugTrace = System.getProperty("arjdbc.debug.trace");
3479
+ debugStackTrace = debugTrace == null ? null : Boolean.parseBoolean(debugTrace);
3480
+ }
3481
+
2913
3482
  public static void debugStackTrace(final ThreadContext context, final Throwable e) {
2914
- if ( debug || context.runtime.isDebug() ) {
2915
- e.printStackTrace(context.runtime.getOut());
3483
+ if ( debug || ( context != null && context.runtime.isDebug() ) ) {
3484
+ final PrintStream out = context != null ? context.runtime.getOut() : System.out;
3485
+ if ( debugStackTrace == null || debugStackTrace.booleanValue() ) {
3486
+ e.printStackTrace(out);
3487
+ }
3488
+ else {
3489
+ out.println(e);
3490
+ }
2916
3491
  }
2917
3492
  }
2918
-
3493
+
2919
3494
  private static RubyArray createCallerBacktrace(final ThreadContext context) {
2920
3495
  final Ruby runtime = context.getRuntime();
2921
3496
  runtime.incrementCallerCount();
2922
-
3497
+
2923
3498
  Method gatherCallerBacktrace; RubyStackTraceElement[] trace;
2924
3499
  try {
2925
3500
  gatherCallerBacktrace = context.getClass().getMethod("gatherCallerBacktrace");
@@ -2937,15 +3512,15 @@ public class RubyJdbcConnection extends RubyObject {
2937
3512
  catch (IllegalAccessException e) { throw new RuntimeException(e); }
2938
3513
  catch (InvocationTargetException e) { throw new RuntimeException(e.getTargetException()); }
2939
3514
  // RubyStackTraceElement[] trace = context.gatherCallerBacktrace(level);
2940
-
3515
+
2941
3516
  final RubyArray backtrace = runtime.newArray(trace.length);
2942
3517
  for (int i = 0; i < trace.length; i++) {
2943
3518
  RubyStackTraceElement element = trace[i];
2944
- backtrace.append( RubyString.newString(runtime,
3519
+ backtrace.append( RubyString.newString(runtime,
2945
3520
  element.getFileName() + ":" + element.getLineNumber() + ":in `" + element.getMethodName() + "'"
2946
3521
  ) );
2947
3522
  }
2948
3523
  return backtrace;
2949
3524
  }
2950
-
3525
+
2951
3526
  }