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
@@ -35,21 +35,21 @@ import org.jruby.runtime.builtin.IRubyObject;
35
35
 
36
36
  /**
37
37
  * ArJdbc::Oracle
38
- *
38
+ *
39
39
  * @author kares
40
40
  */
41
41
  public class OracleModule {
42
-
42
+
43
43
  public static RubyModule load(final RubyModule arJdbc) {
44
44
  RubyModule oracle = arJdbc.defineModuleUnder("Oracle");
45
45
  oracle.defineAnnotatedMethods( OracleModule.class );
46
46
  return oracle;
47
47
  }
48
-
49
- @JRubyMethod(name = "quote_string", required = 1, frame = false)
48
+
49
+ @JRubyMethod(name = "quote_string", required = 1)
50
50
  public static IRubyObject quote_string(
51
- final ThreadContext context,
52
- final IRubyObject self,
51
+ final ThreadContext context,
52
+ final IRubyObject self,
53
53
  final IRubyObject string) { // string.gsub("'", "''") :
54
54
  final char single = '\'';
55
55
  final RubyString quoted = quoteCharWith(
@@ -57,19 +57,19 @@ public class OracleModule {
57
57
  );
58
58
  return quoted;
59
59
  }
60
-
61
- @JRubyMethod(name = "quoted_true", required = 0, frame = false)
60
+
61
+ @JRubyMethod(name = "quoted_true", required = 0)
62
62
  public static IRubyObject quoted_true(
63
- final ThreadContext context,
63
+ final ThreadContext context,
64
64
  final IRubyObject self) {
65
65
  return RubyString.newString(context.getRuntime(), BYTES_1);
66
66
  }
67
-
68
- @JRubyMethod(name = "quoted_false", required = 0, frame = false)
67
+
68
+ @JRubyMethod(name = "quoted_false", required = 0)
69
69
  public static IRubyObject quoted_false(
70
- final ThreadContext context,
70
+ final ThreadContext context,
71
71
  final IRubyObject self) {
72
72
  return RubyString.newString(context.getRuntime(), BYTES_0);
73
73
  }
74
-
74
+
75
75
  }
@@ -25,11 +25,18 @@
25
25
  ***** END LICENSE BLOCK *****/
26
26
  package arjdbc.oracle;
27
27
 
28
+ import arjdbc.jdbc.Callable;
28
29
  import arjdbc.jdbc.RubyJdbcConnection;
30
+ import java.io.IOException;
31
+ import java.io.Reader;
29
32
 
33
+ import java.sql.Connection;
30
34
  import java.sql.ResultSet;
31
35
  import java.sql.SQLException;
32
36
  import java.sql.DatabaseMetaData;
37
+ import java.sql.PreparedStatement;
38
+ import java.sql.Statement;
39
+ import java.sql.Types;
33
40
  import java.util.ArrayList;
34
41
  import java.util.List;
35
42
 
@@ -37,7 +44,9 @@ import org.jruby.Ruby;
37
44
  import org.jruby.RubyArray;
38
45
  import org.jruby.RubyClass;
39
46
  import org.jruby.RubyString;
47
+ import org.jruby.anno.JRubyMethod;
40
48
  import org.jruby.runtime.ObjectAllocator;
49
+ import org.jruby.runtime.ThreadContext;
41
50
  import org.jruby.runtime.builtin.IRubyObject;
42
51
 
43
52
  /**
@@ -45,11 +54,11 @@ import org.jruby.runtime.builtin.IRubyObject;
45
54
  * @author nicksieger
46
55
  */
47
56
  public class OracleRubyJdbcConnection extends RubyJdbcConnection {
48
-
57
+
49
58
  protected OracleRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
50
59
  super(runtime, metaClass);
51
60
  }
52
-
61
+
53
62
  public static RubyClass createOracleJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
54
63
  final RubyClass clazz = RubyJdbcConnection.getConnectionAdapters(runtime).
55
64
  defineClassUnder("OracleJdbcConnection", jdbcConnection, ORACLE_JDBCCONNECTION_ALLOCATOR);
@@ -63,6 +72,105 @@ public class OracleRubyJdbcConnection extends RubyJdbcConnection {
63
72
  }
64
73
  };
65
74
 
75
+ @JRubyMethod(name = "next_sequence_value", required = 1)
76
+ public IRubyObject next_sequence_value(final ThreadContext context,
77
+ final IRubyObject sequence) throws SQLException {
78
+ return withConnection(context, new Callable<IRubyObject>() {
79
+ public IRubyObject call(final Connection connection) throws SQLException {
80
+ Statement statement = null; ResultSet valSet = null;
81
+ try {
82
+ statement = connection.createStatement();
83
+ valSet = statement.executeQuery("SELECT "+ sequence +".NEXTVAL id FROM dual");
84
+ if ( ! valSet.next() ) return context.getRuntime().getNil();
85
+ return context.getRuntime().newFixnum( valSet.getLong(1) );
86
+ }
87
+ catch (final SQLException e) {
88
+ debugMessage(context, "failed to get " + sequence + ".NEXTVAL : " + e.getMessage());
89
+ throw e;
90
+ }
91
+ finally { close(valSet); close(statement); }
92
+ }
93
+ });
94
+ }
95
+
96
+ @Override // NOTE: Invalid column type:
97
+ // getLong not implemented for class oracle.jdbc.driver.T4CRowidAccessor
98
+ protected IRubyObject mapGeneratedKey(final Ruby runtime, final ResultSet genKeys)
99
+ throws SQLException {
100
+ // NOTE: it's likely a ROWID which we do not care about :
101
+ final String value = genKeys.getString(1); // "AAAsOjAAFAAABUlAAA"
102
+ if ( isPositiveInteger(value) ) {
103
+ return runtime.newFixnum( Long.parseLong(value) );
104
+ }
105
+ else {
106
+ return runtime.getNil();
107
+ }
108
+ }
109
+
110
+ private static boolean isPositiveInteger(final String value) {
111
+ for ( int i = 0; i < value.length(); i++ ) {
112
+ if ( ! Character.isDigit(value.charAt(i)) ) return false;
113
+ }
114
+ return true;
115
+ }
116
+
117
+ @Override // resultSet.wasNull() might be falsy for '' treated as null
118
+ protected IRubyObject stringToRuby(final ThreadContext context,
119
+ final Ruby runtime, final ResultSet resultSet, final int column)
120
+ throws SQLException {
121
+ final String value = resultSet.getString(column);
122
+ if ( value == null ) return runtime.getNil();
123
+ return RubyString.newUnicodeString(runtime, value);
124
+ }
125
+
126
+ @Override
127
+ protected IRubyObject readerToRuby(final ThreadContext context,
128
+ final Ruby runtime, final ResultSet resultSet, final int column)
129
+ throws SQLException, IOException {
130
+ final Reader reader = resultSet.getCharacterStream(column);
131
+ try {
132
+ if ( resultSet.wasNull() ) return RubyString.newEmptyString(runtime);
133
+
134
+ final int bufSize = streamBufferSize;
135
+ final StringBuilder string = new StringBuilder(bufSize);
136
+
137
+ final char[] buf = new char[ bufSize / 2 ];
138
+ for (int len = reader.read(buf); len != -1; len = reader.read(buf)) {
139
+ string.append(buf, 0, len);
140
+ }
141
+
142
+ return RubyString.newUnicodeString(runtime, string.toString());
143
+ }
144
+ finally { if ( reader != null ) reader.close(); }
145
+ }
146
+
147
+ @Override // booleans are emulated can not setNull(index, Types.BOOLEAN)
148
+ protected void setBooleanParameter(final ThreadContext context,
149
+ final Connection connection, final PreparedStatement statement,
150
+ final int index, final Object value,
151
+ final IRubyObject column, final int type) throws SQLException {
152
+ if ( value instanceof IRubyObject ) {
153
+ setBooleanParameter(context, connection, statement, index, (IRubyObject) value, column, type);
154
+ }
155
+ else {
156
+ if ( value == null ) statement.setNull(index, Types.TINYINT);
157
+ else {
158
+ statement.setBoolean(index, ((Boolean) value).booleanValue());
159
+ }
160
+ }
161
+ }
162
+
163
+ @Override // booleans are emulated can not setNull(index, Types.BOOLEAN)
164
+ protected void setBooleanParameter(final ThreadContext context,
165
+ final Connection connection, final PreparedStatement statement,
166
+ final int index, final IRubyObject value,
167
+ final IRubyObject column, final int type) throws SQLException {
168
+ if ( value.isNil() ) statement.setNull(index, Types.TINYINT);
169
+ else {
170
+ statement.setBoolean(index, value.isTrue());
171
+ }
172
+ }
173
+
66
174
  /**
67
175
  * Oracle needs this override to reconstruct NUMBER which is different
68
176
  * from NUMBER(x) or NUMBER(x,y).
@@ -81,10 +189,10 @@ public class OracleRubyJdbcConnection extends RubyJdbcConnection {
81
189
  final String type = resultSet.getString(TYPE_NAME);
82
190
  return formatTypeWithPrecisionAndScale(type, precision, scale);
83
191
  }
84
-
192
+
85
193
  @Override
86
- protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
87
- final String catalog, final String schemaPattern, final String tablePattern,
194
+ protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
195
+ final String catalog, final String schemaPattern, final String tablePattern,
88
196
  final ResultSet tablesSet) throws SQLException {
89
197
  final List<IRubyObject> tables = new ArrayList<IRubyObject>(32);
90
198
  while ( tablesSet.next() ) {
@@ -96,5 +204,5 @@ public class OracleRubyJdbcConnection extends RubyJdbcConnection {
96
204
  }
97
205
  return runtime.newArray(tables);
98
206
  }
99
-
207
+
100
208
  }
@@ -25,22 +25,34 @@
25
25
  ***** END LICENSE BLOCK *****/
26
26
  package arjdbc.postgresql;
27
27
 
28
+ import java.io.ByteArrayInputStream;
29
+ import java.io.InputStream;
28
30
  import java.sql.Array;
29
31
  import java.sql.Connection;
30
32
  import java.sql.DatabaseMetaData;
33
+ import java.sql.PreparedStatement;
31
34
  import java.sql.ResultSet;
32
35
  import java.sql.SQLException;
36
+ import java.sql.Statement;
37
+ import java.sql.Timestamp;
33
38
  import java.sql.Types;
34
39
  import java.util.UUID;
35
40
 
36
41
  import org.jruby.Ruby;
37
42
  import org.jruby.RubyArray;
43
+ import org.jruby.RubyBoolean;
38
44
  import org.jruby.RubyClass;
45
+ import org.jruby.RubyFloat;
46
+ import org.jruby.RubyIO;
39
47
  import org.jruby.RubyString;
48
+ import org.jruby.anno.JRubyMethod;
40
49
  import org.jruby.javasupport.JavaUtil;
41
50
  import org.jruby.runtime.ObjectAllocator;
51
+ import org.jruby.runtime.ThreadContext;
42
52
  import org.jruby.runtime.builtin.IRubyObject;
53
+ import org.jruby.util.ByteList;
43
54
 
55
+ import org.postgresql.PGStatement;
44
56
  import org.postgresql.util.PGInterval;
45
57
  import org.postgresql.util.PGobject;
46
58
 
@@ -49,7 +61,7 @@ import org.postgresql.util.PGobject;
49
61
  * @author enebo
50
62
  */
51
63
  public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
52
-
64
+
53
65
  protected PostgreSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
54
66
  super(runtime, metaClass);
55
67
  }
@@ -61,13 +73,32 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
61
73
  getConnectionAdapters(runtime).setConstant("PostgresJdbcConnection", clazz); // backwards-compat
62
74
  return clazz;
63
75
  }
64
-
76
+
65
77
  private static ObjectAllocator POSTGRESQL_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
66
78
  public IRubyObject allocate(Ruby runtime, RubyClass klass) {
67
79
  return new PostgreSQLRubyJdbcConnection(runtime, klass);
68
80
  }
69
81
  };
70
-
82
+
83
+ // enables testing if the bug is fixed (please run our test-suite)
84
+ // using `rake test_postgresql JRUBY_OPTS="-J-Darjdbc.postgresql.generated.keys=true"`
85
+ protected static final boolean generatedKeys = Boolean.getBoolean("arjdbc.postgresql.generated.keys");
86
+
87
+ @Override
88
+ protected IRubyObject mapGeneratedKeys(
89
+ final Ruby runtime, final Connection connection,
90
+ final Statement statement, final Boolean singleResult)
91
+ throws SQLException {
92
+ if ( generatedKeys ) {
93
+ super.mapGeneratedKeys(runtime, connection, statement, singleResult);
94
+ }
95
+ // NOTE: PostgreSQL driver supports generated keys but does not work
96
+ // correctly for all cases e.g. for tables whene no keys are generated
97
+ // during an INSERT getGeneratedKeys return all inserted rows instead
98
+ // of an empty result set ... thus disabled until issue is resolved !
99
+ return null; // not supported
100
+ }
101
+
71
102
  @Override
72
103
  protected String caseConvertIdentifierForJdbc(final DatabaseMetaData metaData, final String value)
73
104
  throws SQLException {
@@ -79,19 +110,91 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
79
110
  }
80
111
  return value;
81
112
  }
82
-
113
+
114
+ @Override // due statement.setNull(index, Types.BLOB) not working :
115
+ // org.postgresql.util.PSQLException: ERROR: column "sample_binary" is of type bytea but expression is of type oid
116
+ protected void setBlobParameter(final ThreadContext context,
117
+ final Connection connection, final PreparedStatement statement,
118
+ final int index, final Object value,
119
+ final IRubyObject column, final int type) throws SQLException {
120
+ if ( value instanceof IRubyObject ) {
121
+ setBlobParameter(context, connection, statement, index, (IRubyObject) value, column, type);
122
+ }
123
+ else {
124
+ if ( value == null ) statement.setNull(index, Types.BINARY);
125
+ else {
126
+ statement.setBinaryStream(index, (InputStream) value);
127
+ }
128
+ }
129
+ }
130
+
131
+ @Override // due statement.setNull(index, Types.BLOB) not working :
132
+ // org.postgresql.util.PSQLException: ERROR: column "sample_binary" is of type bytea but expression is of type oid
133
+ protected void setBlobParameter(final ThreadContext context,
134
+ final Connection connection, final PreparedStatement statement,
135
+ final int index, final IRubyObject value,
136
+ final IRubyObject column, final int type) throws SQLException {
137
+ if ( value.isNil() ) {
138
+ statement.setNull(index, Types.BINARY);
139
+ }
140
+ else {
141
+ if ( value instanceof RubyIO ) { // IO/File
142
+ statement.setBinaryStream(index, ((RubyIO) value).getInStream());
143
+ }
144
+ else { // should be a RubyString
145
+ final ByteList blob = value.asString().getByteList();
146
+ statement.setBinaryStream(index,
147
+ new ByteArrayInputStream(blob.unsafeBytes(), blob.getBegin(), blob.getRealSize()),
148
+ blob.getRealSize() // length
149
+ );
150
+ }
151
+ }
152
+ }
153
+
154
+ @Override // to handle infinity timestamp values
155
+ protected void setTimestampParameter(final ThreadContext context,
156
+ final Connection connection, final PreparedStatement statement,
157
+ final int index, IRubyObject value,
158
+ final IRubyObject column, final int type) throws SQLException {
159
+
160
+ if ( value instanceof RubyFloat ) {
161
+ final double _value = ( (RubyFloat) value ).getValue();
162
+ if ( Double.isInfinite(_value) ) {
163
+ final Timestamp timestamp;
164
+ if ( _value < 0 ) {
165
+ timestamp = new Timestamp(PGStatement.DATE_NEGATIVE_INFINITY);
166
+ }
167
+ else {
168
+ timestamp = new Timestamp(PGStatement.DATE_POSITIVE_INFINITY);
169
+ }
170
+ statement.setTimestamp( index, timestamp );
171
+ return;
172
+ }
173
+ }
174
+
175
+ super.setTimestampParameter(context, connection, statement, index, value, column, type);
176
+ }
177
+
178
+ @Override
179
+ protected int jdbcTypeFor(final ThreadContext context, final Ruby runtime,
180
+ final IRubyObject column, final Object value) throws SQLException {
181
+ // NOTE: likely wrong but native adapters handles this thus we should
182
+ // too - used from #table_exists? `binds << [ nil, schema ] if schema`
183
+ if ( column == null || column.isNil() ) return Types.VARCHAR; // assume type == :string
184
+ return super.jdbcTypeFor(context, runtime, column, value);
185
+ }
186
+
83
187
  /**
84
188
  * Override jdbcToRuby type conversions to handle infinite timestamps.
85
189
  * Handing timestamp off to ruby as string so adapter can perform type
86
190
  * conversion to timestamp
87
191
  */
88
192
  @Override
89
- protected IRubyObject jdbcToRuby(final Ruby runtime,
193
+ protected IRubyObject jdbcToRuby(
194
+ final ThreadContext context, final Ruby runtime,
90
195
  final int column, final int type, final ResultSet resultSet)
91
196
  throws SQLException {
92
197
  switch ( type ) {
93
- case Types.TIMESTAMP:
94
- return stringToRuby(runtime, resultSet, column);
95
198
  case Types.BIT:
96
199
  // we do get BIT for 't' 'f' as well as BIT strings e.g. "0110" :
97
200
  final String bits = resultSet.getString(column);
@@ -99,22 +202,42 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
99
202
  if ( bits.length() > 1 ) {
100
203
  return RubyString.newUnicodeString(runtime, bits);
101
204
  }
102
- return booleanToRuby(runtime, resultSet, column);
205
+ return booleanToRuby(context, runtime, resultSet, column);
103
206
  //case Types.JAVA_OBJECT: case Types.OTHER:
104
207
  //return objectToRuby(runtime, resultSet, resultSet.getObject(column));
105
208
  }
106
- return super.jdbcToRuby(runtime, column, type, resultSet);
209
+ return super.jdbcToRuby(context, runtime, column, type, resultSet);
107
210
  }
108
-
211
+
109
212
  @Override
110
- protected IRubyObject arrayToRuby(
213
+ protected IRubyObject timestampToRuby(final ThreadContext context,
214
+ final Ruby runtime, final ResultSet resultSet, final int column)
215
+ throws SQLException {
216
+ // NOTE: using Timestamp we loose information such as BC :
217
+ // Timestamp: '0001-12-31 22:59:59.0' String: '0001-12-31 22:59:59 BC'
218
+ final String value = resultSet.getString(column);
219
+ if ( value == null ) {
220
+ if ( resultSet.wasNull() ) return runtime.getNil();
221
+ return runtime.newString(); // ""
222
+ }
223
+
224
+ final RubyString strValue = timestampToRubyString(runtime, value.toString());
225
+ if ( rawDateTime ) return strValue;
226
+
227
+ final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
228
+ if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
229
+ return adapter.callMethod(context, "_string_to_timestamp", strValue);
230
+ }
231
+
232
+ @Override
233
+ protected IRubyObject arrayToRuby(final ThreadContext context,
111
234
  final Ruby runtime, final ResultSet resultSet, final int column)
112
235
  throws SQLException {
113
236
  // NOTE: avoid `finally { array.free(); }` on PostgreSQL due :
114
- // java.sql.SQLFeatureNotSupportedException:
237
+ // java.sql.SQLFeatureNotSupportedException:
115
238
  // Method org.postgresql.jdbc4.Jdbc4Array.free() is not yet implemented.
116
239
  final Array value = resultSet.getArray(column);
117
-
240
+
118
241
  if ( value == null && resultSet.wasNull() ) return runtime.getNil();
119
242
 
120
243
  final RubyArray array = runtime.newArray();
@@ -122,54 +245,53 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
122
245
  final ResultSet arrayResult = value.getResultSet(); // 1: index, 2: value
123
246
  final int baseType = value.getBaseType();
124
247
  while ( arrayResult.next() ) {
125
- IRubyObject element = jdbcToRuby(runtime, 2, baseType, arrayResult);
126
- array.append(element);
248
+ array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
127
249
  }
128
250
  return array;
129
251
  }
130
-
252
+
131
253
  @Override
132
- protected IRubyObject objectToRuby(
254
+ protected IRubyObject objectToRuby(final ThreadContext context,
133
255
  final Ruby runtime, final ResultSet resultSet, final int column)
134
256
  throws SQLException {
135
257
  final Object object = resultSet.getObject(column);
136
-
258
+
137
259
  if ( object == null && resultSet.wasNull() ) return runtime.getNil();
138
-
260
+
139
261
  final Class<?> objectClass = object.getClass();
140
262
  if ( objectClass == UUID.class ) {
141
263
  return runtime.newString( object.toString() );
142
264
  }
143
-
265
+
144
266
  if ( objectClass == PGInterval.class ) {
145
267
  return runtime.newString( formatInterval(object) );
146
268
  }
147
-
269
+
148
270
  if ( object instanceof PGobject ) {
149
271
  // PG 9.2 JSON type will be returned here as well
150
272
  return runtime.newString( object.toString() );
151
273
  }
152
-
274
+
153
275
  return JavaUtil.convertJavaToRuby(runtime, object);
154
276
  }
155
-
277
+
156
278
  @Override
157
279
  protected TableName extractTableName(
158
- final Connection connection, String catalog, String schema,
280
+ final Connection connection, String catalog, String schema,
159
281
  final String tableName) throws IllegalArgumentException, SQLException {
160
282
  // The postgres JDBC driver will default to searching every schema if no
161
283
  // schema search path is given. Default to the 'public' schema instead:
162
284
  if ( schema == null ) schema = "public";
163
285
  return super.extractTableName(connection, catalog, schema, tableName);
164
286
  }
165
-
287
+
166
288
  // NOTE: do not use PG classes in the API so that loading is delayed !
167
289
  private String formatInterval(final Object object) {
168
290
  final PGInterval interval = (PGInterval) object;
169
- if ( useRawIntervalType() ) return interval.getValue();
170
-
291
+ if ( rawIntervalType ) return interval.getValue();
292
+
171
293
  final StringBuilder str = new StringBuilder(32);
172
-
294
+
173
295
  final int years = interval.getYears();
174
296
  if ( years != 0 ) str.append(years).append(" years ");
175
297
  final int months = interval.getMonths();
@@ -190,10 +312,10 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
190
312
  else {
191
313
  if ( str.length() > 1 ) str.deleteCharAt( str.length() - 1 ); // " " at the end
192
314
  }
193
-
315
+
194
316
  return str.toString();
195
317
  }
196
-
318
+
197
319
  // whether to use "raw" interval values off by default - due native adapter compatibilty :
198
320
  // RAW values :
199
321
  // - 2 years 0 mons 0 days 0 hours 3 mins 0.00 secs
@@ -201,14 +323,22 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
201
323
  // Rails style :
202
324
  // - 2 years 00:03:00
203
325
  // - -1 years -2 days
204
- private static boolean rawIntervalType = Boolean.getBoolean("arjdbc.postgresql.iterval.raw");
326
+ protected static boolean rawIntervalType = Boolean.getBoolean("arjdbc.postgresql.iterval.raw");
205
327
 
206
- public static boolean useRawIntervalType() {
207
- return rawIntervalType;
328
+ @JRubyMethod(name = "raw_interval_type?")
329
+ public static IRubyObject useRawIntervalType(final ThreadContext context, final IRubyObject self) {
330
+ return context.getRuntime().newBoolean(rawIntervalType);
208
331
  }
209
332
 
210
- public static void setRawIntervalType(boolean rawInterval) {
211
- PostgreSQLRubyJdbcConnection.rawIntervalType = rawInterval;
333
+ @JRubyMethod(name = "raw_interval_type=")
334
+ public static IRubyObject setRawIntervalType(final IRubyObject self, final IRubyObject value) {
335
+ if ( value instanceof RubyBoolean ) {
336
+ rawIntervalType = ((RubyBoolean) value).isTrue();
337
+ }
338
+ else {
339
+ rawIntervalType = value.isNil();
340
+ }
341
+ return value;
212
342
  }
213
-
343
+
214
344
  }