activerecord-jdbc-adapter 1.2.5 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +5 -1
  3. data/Appraisals +5 -5
  4. data/Gemfile +9 -1
  5. data/Gemfile.lock +44 -10
  6. data/History.txt +126 -2
  7. data/README.md +246 -0
  8. data/Rakefile +34 -25
  9. data/activerecord-jdbc-adapter.gemspec +1 -1
  10. data/gemfiles/rails23.gemfile +5 -3
  11. data/gemfiles/rails23.gemfile.lock +26 -18
  12. data/gemfiles/rails30.gemfile +4 -2
  13. data/gemfiles/rails30.gemfile.lock +16 -8
  14. data/gemfiles/rails31.gemfile +4 -2
  15. data/gemfiles/rails31.gemfile.lock +16 -9
  16. data/gemfiles/rails32.gemfile +4 -2
  17. data/gemfiles/rails32.gemfile.lock +15 -8
  18. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  19. data/lib/arel/visitors/sql_server.rb +3 -0
  20. data/lib/arjdbc.rb +3 -5
  21. data/lib/arjdbc/db2.rb +1 -0
  22. data/lib/arjdbc/db2/adapter.rb +302 -196
  23. data/lib/arjdbc/db2/connection_methods.rb +18 -0
  24. data/lib/arjdbc/derby/active_record_patch.rb +12 -0
  25. data/lib/arjdbc/derby/adapter.rb +180 -158
  26. data/lib/arjdbc/derby/connection_methods.rb +5 -1
  27. data/lib/arjdbc/firebird/adapter.rb +27 -19
  28. data/lib/arjdbc/h2/adapter.rb +162 -7
  29. data/lib/arjdbc/h2/connection_methods.rb +5 -1
  30. data/lib/arjdbc/hsqldb.rb +1 -1
  31. data/lib/arjdbc/hsqldb/adapter.rb +96 -61
  32. data/lib/arjdbc/hsqldb/connection_methods.rb +5 -1
  33. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  34. data/lib/arjdbc/informix/adapter.rb +56 -55
  35. data/lib/arjdbc/jdbc/adapter.rb +173 -86
  36. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  37. data/lib/arjdbc/jdbc/column.rb +28 -23
  38. data/lib/arjdbc/jdbc/connection.rb +10 -6
  39. data/lib/arjdbc/jdbc/driver.rb +13 -5
  40. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +21 -0
  41. data/lib/arjdbc/mssql.rb +1 -1
  42. data/lib/arjdbc/mssql/adapter.rb +51 -53
  43. data/lib/arjdbc/mssql/connection_methods.rb +8 -1
  44. data/lib/arjdbc/mysql.rb +1 -1
  45. data/lib/arjdbc/mysql/adapter.rb +186 -150
  46. data/lib/arjdbc/mysql/connection_methods.rb +9 -9
  47. data/lib/arjdbc/mysql/explain_support.rb +85 -0
  48. data/lib/arjdbc/oracle.rb +1 -1
  49. data/lib/arjdbc/oracle/adapter.rb +232 -125
  50. data/lib/arjdbc/oracle/connection_methods.rb +2 -2
  51. data/lib/arjdbc/postgresql.rb +1 -1
  52. data/lib/arjdbc/postgresql/adapter.rb +134 -86
  53. data/lib/arjdbc/postgresql/connection_methods.rb +6 -4
  54. data/lib/arjdbc/postgresql/explain_support.rb +55 -0
  55. data/lib/arjdbc/sqlite3.rb +1 -1
  56. data/lib/arjdbc/sqlite3/adapter.rb +176 -108
  57. data/lib/arjdbc/sqlite3/connection_methods.rb +5 -5
  58. data/lib/arjdbc/sqlite3/explain_support.rb +32 -0
  59. data/lib/arjdbc/sybase/adapter.rb +7 -6
  60. data/lib/arjdbc/version.rb +1 -1
  61. data/pom.xml +1 -1
  62. data/rakelib/02-test.rake +9 -11
  63. data/rakelib/rails.rake +18 -10
  64. data/src/java/arjdbc/db2/DB2Module.java +70 -0
  65. data/src/java/arjdbc/derby/DerbyModule.java +24 -5
  66. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +66 -0
  67. data/src/java/arjdbc/jdbc/AdapterJavaService.java +14 -7
  68. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +111 -89
  69. data/src/java/arjdbc/mysql/MySQLModule.java +79 -70
  70. data/src/java/arjdbc/oracle/OracleModule.java +74 -0
  71. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +5 -10
  72. data/src/java/arjdbc/sqlite3/SQLite3Module.java +77 -0
  73. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +127 -0
  74. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +25 -111
  75. data/src/java/arjdbc/util/QuotingUtils.java +104 -0
  76. data/test/abstract_db_create.rb +6 -6
  77. data/test/activerecord/connection_adapters/type_conversion_test.rb +2 -2
  78. data/test/assets/flowers.jpg +0 -0
  79. data/test/binary.rb +67 -0
  80. data/test/db/db2.rb +30 -7
  81. data/test/db/jdbc.rb +4 -2
  82. data/test/db/oracle.rb +18 -27
  83. data/test/db2_binary_test.rb +6 -0
  84. data/test/db2_serialize_test.rb +6 -0
  85. data/test/db2_simple_test.rb +20 -25
  86. data/test/db2_test.rb +71 -0
  87. data/test/derby_binary_test.rb +6 -0
  88. data/test/derby_migration_test.rb +42 -35
  89. data/test/derby_reset_column_information_test.rb +1 -0
  90. data/test/derby_row_locking_test.rb +17 -0
  91. data/test/derby_schema_dump_test.rb +9 -0
  92. data/test/derby_serialize_test.rb +6 -0
  93. data/test/derby_simple_test.rb +59 -17
  94. data/test/generic_jdbc_connection_test.rb +112 -5
  95. data/test/h2_binary_test.rb +6 -0
  96. data/test/h2_change_column_test.rb +1 -1
  97. data/test/h2_schema_dump_test.rb +25 -0
  98. data/test/h2_serialize_test.rb +6 -0
  99. data/test/h2_simple_test.rb +23 -9
  100. data/test/has_many_through.rb +18 -4
  101. data/test/hsqldb_binary_test.rb +6 -0
  102. data/test/hsqldb_schema_dump_test.rb +15 -0
  103. data/test/hsqldb_serialize_test.rb +6 -0
  104. data/test/hsqldb_simple_test.rb +1 -0
  105. data/test/informix_simple_test.rb +1 -1
  106. data/test/jdbc/db2.rb +23 -0
  107. data/test/jdbc/oracle.rb +23 -0
  108. data/test/jdbc_common.rb +3 -110
  109. data/test/jndi_callbacks_test.rb +0 -2
  110. data/test/jndi_test.rb +2 -0
  111. data/test/models/binary.rb +18 -0
  112. data/test/models/custom_pk_name.rb +1 -0
  113. data/test/models/data_types.rb +11 -2
  114. data/test/models/entry.rb +1 -1
  115. data/test/models/string_id.rb +2 -2
  116. data/test/models/thing.rb +1 -1
  117. data/test/models/topic.rb +32 -0
  118. data/test/mssql_legacy_types_test.rb +1 -1
  119. data/test/mssql_limit_offset_test.rb +13 -3
  120. data/test/mssql_serialize_test.rb +6 -0
  121. data/test/mysql_binary_test.rb +6 -0
  122. data/test/mysql_schema_dump_test.rb +220 -0
  123. data/test/mysql_serialize_test.rb +6 -0
  124. data/test/mysql_simple_test.rb +22 -2
  125. data/test/mysql_test.rb +93 -0
  126. data/test/oracle_binary_test.rb +6 -0
  127. data/test/oracle_limit_test.rb +2 -1
  128. data/test/oracle_serialize_test.rb +6 -0
  129. data/test/oracle_simple_test.rb +61 -0
  130. data/test/oracle_specific_test.rb +77 -26
  131. data/test/postgres_binary_test.rb +6 -0
  132. data/test/postgres_native_type_mapping_test.rb +12 -11
  133. data/test/postgres_nonseq_pkey_test.rb +1 -0
  134. data/test/postgres_reserved_test.rb +1 -0
  135. data/test/postgres_reset_column_information_test.rb +1 -0
  136. data/test/postgres_row_locking_test.rb +21 -0
  137. data/test/postgres_schema_dump_test.rb +88 -0
  138. data/test/postgres_schema_search_path_test.rb +1 -0
  139. data/test/postgres_simple_test.rb +62 -89
  140. data/test/postgres_table_alias_length_test.rb +1 -0
  141. data/test/postgres_test.rb +31 -0
  142. data/test/postgres_type_conversion_test.rb +16 -16
  143. data/test/row_locking.rb +69 -64
  144. data/test/schema_dump.rb +168 -0
  145. data/test/serialize.rb +277 -0
  146. data/test/simple.rb +326 -122
  147. data/test/sqlite3_serialize_test.rb +6 -0
  148. data/test/sqlite3_simple_test.rb +51 -84
  149. data/test/sqlite3_type_conversion_test.rb +101 -0
  150. data/test/test_helper.rb +224 -0
  151. metadata +325 -366
  152. data/README.rdoc +0 -214
  153. data/test/db/logger.rb +0 -3
  154. data/test/derby_multibyte_test.rb +0 -11
  155. data/test/mysql_info_test.rb +0 -123
@@ -31,12 +31,13 @@ module ArJdbc
31
31
  !@limit.nil? && @limit == 0
32
32
  end
33
33
 
34
- def modify_types(tp) #:nodoc:
35
- tp[:primary_key] = "NUMERIC(22,0) IDENTITY PRIMARY KEY"
36
- tp[:integer][:limit] = nil
37
- tp[:boolean] = {:name => "bit"}
38
- tp[:binary] = {:name => "image"}
39
- tp
34
+ def modify_types(types) # :nodoc:
35
+ super(types)
36
+ types[:primary_key] = "NUMERIC(22,0) IDENTITY PRIMARY KEY"
37
+ types[:integer][:limit] = nil
38
+ types[:boolean] = {:name => "bit"}
39
+ types[:binary] = {:name => "image"}
40
+ types
40
41
  end
41
42
 
42
43
  def remove_index(table_name, options = {})
@@ -1,6 +1,6 @@
1
1
  module ArJdbc
2
2
  module Version
3
- VERSION = "1.2.5"
3
+ VERSION = "1.2.8"
4
4
  end
5
5
  end
6
6
  # Compatibility with older versions of ar-jdbc for other extensions out there
data/pom.xml CHANGED
@@ -35,7 +35,7 @@
35
35
  <dependency>
36
36
  <groupId>org.jruby</groupId>
37
37
  <artifactId>jruby-complete</artifactId>
38
- <version>1.5.3</version>
38
+ <version>1.6.8</version>
39
39
  </dependency>
40
40
  </dependencies>
41
41
 
@@ -1,4 +1,4 @@
1
- require File.expand_path '../../test/helper', __FILE__
1
+ require File.expand_path('../../test/helper', __FILE__)
2
2
  if defined?(JRUBY_VERSION)
3
3
  databases = [ :test_mysql, :test_sqlite3, :test_derby, :test_hsqldb, :test_h2 ]
4
4
  databases << :test_postgres if PostgresHelper.have_postgres?(false)
@@ -20,16 +20,16 @@ def set_compat_version(task)
20
20
  end
21
21
  end
22
22
 
23
- def all_appraisal_names
24
- @appraisal_names ||= begin names = []; Appraisal::File.each { |file| names << file.name }; names end
25
- end
26
-
27
23
  def declare_test_task_for(adapter, options = {})
28
24
  driver = options[:driver] || adapter
29
25
  prereqs = options[:prereqs] || []
30
26
  prereqs = [ prereqs ].flatten
31
27
  task "test_#{adapter}_pre" do
32
- puts "Specify AR version with 'rake appraisal:{version} test_#{adapter}' where version=(#{all_appraisal_names.join('|')})"
28
+ unless (ENV['BUNDLE_GEMFILE'] rescue '') =~ /gemfiles\/.*?\.gemfile/
29
+ appraisals = []; Appraisal::File.each { |file| appraisals << file.name }
30
+ puts "Specify AR version with `rake appraisal:{version} test_#{adapter}'" +
31
+ " where version=(#{appraisals.join('|')})"
32
+ end
33
33
  end
34
34
  prereqs << "test_#{adapter}_pre"
35
35
  test_task = lambda do |t|
@@ -43,12 +43,11 @@ def declare_test_task_for(adapter, options = {})
43
43
  t.libs = []
44
44
  set_compat_version(t)
45
45
  if defined?(JRUBY_VERSION)
46
- t.ruby_opts << "-rjdbc/#{driver}"
47
46
  t.libs << "lib" << "jdbc-#{driver}/lib"
48
47
  t.libs.push *FileList["activerecord-jdbc#{adapter}*/lib"]
49
48
  end
50
49
  t.libs << "test"
51
- t.verbose = true
50
+ t.verbose = true if $VERBOSE
52
51
  end
53
52
  Rake::TestTask.new("test_#{adapter}" => prereqs) { |t| test_task.call t }
54
53
  Rake::TestTask.new("test_jdbc_#{adapter}" => prereqs) { |t| test_task.call t }
@@ -64,12 +63,11 @@ declare_test_task_for :sqlite3
64
63
 
65
64
  Rake::TestTask.new(:test_jdbc) do |t|
66
65
  t.test_files = FileList['test/generic_jdbc_connection_test.rb']
67
- t.libs << 'test' << 'jdbc-mysql/lib'
66
+ t.libs << 'test' << 'jdbc-mysql/lib' << 'jdbc-derby/lib'
68
67
  set_compat_version(t)
69
68
  end
70
69
 
71
- Rake::TestTask.new(:test_jndi) do |t|
72
- Rake::Task['tomcat-jndi:check'].invoke
70
+ Rake::TestTask.new(:test_jndi => 'tomcat-jndi:check') do |t|
73
71
  t.test_files = FileList['test/jndi_test.rb']
74
72
  t.libs << 'test' << 'jdbc-derby/lib'
75
73
  set_compat_version(t)
@@ -27,15 +27,23 @@ namespace :rails do
27
27
  end
28
28
 
29
29
  task :test => :jar do
30
- driver = ENV['DRIVER']
31
- raise "need a DRIVER" unless driver
32
- activerecord = ENV['RAILS']
33
- raise "need location of RAILS source code" unless activerecord
34
- activerecord = File.join(activerecord, 'activerecord') unless activerecord =~ /activerecord$/
35
- ar_jdbc = File.expand_path(File.dirname(__FILE__) + '/..')
36
- rubylib = "#{ar_jdbc}/lib:#{ar_jdbc}/jdbc-#{_driver(driver)}/lib:#{ar_jdbc}/activerecord-jdbc#{_adapter(driver)}-adapter/lib"
37
- Dir.chdir(activerecord) do
38
- rake "RUBYLIB=#{rubylib}", "#{_target(driver)}"
39
- end
30
+ raise "need a DRIVER" unless driver = ENV['DRIVER']
31
+ raise "need location of RAILS source code" unless rails_dir = ENV['RAILS']
32
+ rails_dir = File.join(rails_dir, '..') if rails_dir =~ /activerecord$/
33
+ activerecord_dir = File.join(rails_dir, 'activerecord') # rails/activerecord
34
+
35
+ ar_jdbc_dir = File.expand_path('..', File.dirname(__FILE__))
36
+
37
+ rubylib = [
38
+ "#{ar_jdbc_dir}/lib",
39
+ "#{ar_jdbc_dir}/jdbc-#{_driver(driver)}/lib",
40
+ "#{ar_jdbc_dir}/activerecord-jdbc#{_adapter(driver)}-adapter/lib"
41
+ ]
42
+ rubylib << File.expand_path('activesupport/lib', rails_dir)
43
+ rubylib << File.expand_path('activemodel/lib', rails_dir)
44
+ rubylib << File.expand_path(File.join(activerecord_dir, 'lib'))
45
+ #rubylib << File.expand_path('actionpack/lib', rails_dir)
46
+
47
+ Dir.chdir(activerecord_dir) { rake "RUBYLIB=#{rubylib.join(':')}", "#{_target(driver)}" }
40
48
  end
41
49
  end
@@ -0,0 +1,70 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright 2013 Karol Bucek.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+ package arjdbc.db2;
25
+
26
+ import static arjdbc.util.QuotingUtils.BYTES_0;
27
+ import static arjdbc.util.QuotingUtils.BYTES_1;
28
+ import static arjdbc.util.QuotingUtils.quoteSingleQuotesWithFallback;
29
+
30
+ import org.jruby.RubyModule;
31
+ import org.jruby.RubyString;
32
+ import org.jruby.anno.JRubyMethod;
33
+ import org.jruby.runtime.ThreadContext;
34
+ import org.jruby.runtime.builtin.IRubyObject;
35
+
36
+ /**
37
+ * ArJdbc::DB2
38
+ *
39
+ * @author kares
40
+ */
41
+ public class DB2Module {
42
+
43
+ public static void load(final RubyModule arJdbc) {
44
+ RubyModule db2 = arJdbc.defineModuleUnder("DB2");
45
+ db2.defineAnnotatedMethods( DB2Module.class );
46
+ }
47
+
48
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
49
+ public static IRubyObject quote_string(
50
+ final ThreadContext context,
51
+ final IRubyObject self,
52
+ final IRubyObject string) {
53
+ return quoteSingleQuotesWithFallback(context, string);
54
+ }
55
+
56
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
57
+ public static IRubyObject quoted_true(
58
+ final ThreadContext context,
59
+ final IRubyObject self) {
60
+ return RubyString.newString(context.getRuntime(), BYTES_1);
61
+ }
62
+
63
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
64
+ public static IRubyObject quoted_false(
65
+ final ThreadContext context,
66
+ final IRubyObject self) {
67
+ return RubyString.newString(context.getRuntime(), BYTES_0);
68
+ }
69
+
70
+ }
@@ -26,27 +26,32 @@ package arjdbc.derby;
26
26
 
27
27
  import java.sql.SQLException;
28
28
 
29
+ import static arjdbc.util.QuotingUtils.BYTES_0;
30
+ import static arjdbc.util.QuotingUtils.BYTES_1;
31
+
29
32
  import arjdbc.jdbc.RubyJdbcConnection;
30
33
 
31
34
  import org.jruby.Ruby;
32
35
  import org.jruby.RubyBoolean;
33
36
  import org.jruby.RubyModule;
34
37
  import org.jruby.RubyObjectAdapter;
35
- import org.jruby.RubyRange;
36
38
  import org.jruby.RubyString;
37
39
  import org.jruby.anno.JRubyMethod;
40
+ import org.jruby.javasupport.JavaEmbedUtils;
38
41
  import org.jruby.runtime.ThreadContext;
39
42
  import org.jruby.runtime.builtin.IRubyObject;
40
43
  import org.jruby.util.ByteList;
41
44
 
42
45
  public class DerbyModule {
46
+
43
47
  private static RubyObjectAdapter rubyApi;
44
- public static void load(RubyModule arJdbc, RubyObjectAdapter adapter) {
48
+
49
+ public static void load(final RubyModule arJdbc) {
45
50
  RubyModule derby = arJdbc.defineModuleUnder("Derby");
46
51
  derby.defineAnnotatedMethods(DerbyModule.class);
47
52
  RubyModule column = derby.defineModuleUnder("Column");
48
53
  column.defineAnnotatedMethods(Column.class);
49
- rubyApi = adapter;
54
+ rubyApi = JavaEmbedUtils.newObjectAdapter();
50
55
  }
51
56
 
52
57
  public static class Column {
@@ -105,7 +110,7 @@ public class DerbyModule {
105
110
  IRubyObject value = args[0];
106
111
  if (args.length > 1) {
107
112
  IRubyObject col = args[1];
108
- String type = rubyApi.callMethod(col, "type").toString();
113
+ String type = col.isNil() ? "" : rubyApi.callMethod(col, "type").toString();
109
114
  // intercept and change value, maybe, if the column type is :text or :string
110
115
  if (type.equals("text") || type.equals("string")) {
111
116
  value = make_ruby_string_for_text_column(context, recv, runtime, value);
@@ -208,7 +213,7 @@ public class DerbyModule {
208
213
 
209
214
  private static IRubyObject quote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
210
215
  ByteList input = string.getByteList();
211
- ByteList output = new ByteList(before.getBytes());
216
+ ByteList output = new ByteList(before.getBytes(), input.encoding);
212
217
  for(int i = input.begin; i< input.begin + input.realSize; i++) {
213
218
  switch(input.bytes[i]) {
214
219
  case '\'':
@@ -286,6 +291,20 @@ public class DerbyModule {
286
291
  }
287
292
  }
288
293
 
294
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
295
+ public static IRubyObject quoted_true(
296
+ final ThreadContext context,
297
+ final IRubyObject self) {
298
+ return RubyString.newString(context.getRuntime(), BYTES_1);
299
+ }
300
+
301
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
302
+ public static IRubyObject quoted_false(
303
+ final ThreadContext context,
304
+ final IRubyObject self) {
305
+ return RubyString.newString(context.getRuntime(), BYTES_0);
306
+ }
307
+
289
308
  @JRubyMethod(name = "select_all", rest = true)
290
309
  public static IRubyObject select_all(IRubyObject recv, IRubyObject[] args) {
291
310
  return rubyApi.callMethod(recv, "execute", args);
@@ -0,0 +1,66 @@
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.hsqldb;
26
+
27
+ import static arjdbc.util.QuotingUtils.BYTES_0;
28
+ import static arjdbc.util.QuotingUtils.BYTES_1;
29
+ import static arjdbc.util.QuotingUtils.quoteSingleQuotesWithFallback;
30
+
31
+ import org.jruby.RubyModule;
32
+ import org.jruby.RubyString;
33
+ import org.jruby.anno.JRubyMethod;
34
+ import org.jruby.runtime.ThreadContext;
35
+ import org.jruby.runtime.builtin.IRubyObject;
36
+
37
+ public class HSQLDBModule {
38
+
39
+ public static void load(final RubyModule arJdbc) {
40
+ RubyModule hsqldb = arJdbc.defineModuleUnder("HSQLDB");
41
+ hsqldb.defineAnnotatedMethods(HSQLDBModule.class);
42
+ }
43
+
44
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
45
+ public static IRubyObject quote_string(
46
+ final ThreadContext context,
47
+ final IRubyObject self,
48
+ final IRubyObject string) {
49
+ return quoteSingleQuotesWithFallback(context, string);
50
+ }
51
+
52
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
53
+ public static IRubyObject quoted_true(
54
+ final ThreadContext context,
55
+ final IRubyObject self) {
56
+ return RubyString.newString(context.getRuntime(), BYTES_1);
57
+ }
58
+
59
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
60
+ public static IRubyObject quoted_false(
61
+ final ThreadContext context,
62
+ final IRubyObject self) {
63
+ return RubyString.newString(context.getRuntime(), BYTES_0);
64
+ }
65
+
66
+ }
@@ -28,41 +28,48 @@ package arjdbc.jdbc;
28
28
 
29
29
  import java.io.IOException;
30
30
 
31
+ import arjdbc.db2.DB2Module;
31
32
  import arjdbc.db2.DB2RubyJdbcConnection;
32
33
  import arjdbc.derby.DerbyModule;
33
34
  import arjdbc.h2.H2RubyJdbcConnection;
35
+ import arjdbc.hsqldb.HSQLDBModule;
34
36
  import arjdbc.informix.InformixRubyJdbcConnection;
35
37
  import arjdbc.mssql.MssqlRubyJdbcConnection;
36
38
  import arjdbc.mysql.MySQLModule;
37
39
  import arjdbc.mysql.MySQLRubyJdbcConnection;
40
+ import arjdbc.oracle.OracleModule;
38
41
  import arjdbc.oracle.OracleRubyJdbcConnection;
39
42
  import arjdbc.postgresql.PostgresqlRubyJdbcConnection;
40
- import arjdbc.sqlite3.Sqlite3RubyJdbcConnection;
43
+ import arjdbc.sqlite3.SQLite3Module;
44
+ import arjdbc.sqlite3.SQLite3RubyJdbcConnection;
41
45
 
42
46
  import org.jruby.Ruby;
43
47
  import org.jruby.RubyClass;
44
48
  import org.jruby.RubyModule;
45
- import org.jruby.RubyObjectAdapter;
46
- import org.jruby.javasupport.JavaEmbedUtils;
47
49
  import org.jruby.runtime.load.BasicLibraryService;
48
50
 
49
51
  public class AdapterJavaService implements BasicLibraryService {
50
- private static RubyObjectAdapter rubyApi;
51
52
 
52
53
  public boolean basicLoad(final Ruby runtime) throws IOException {
54
+ // ActiveRecord::ConnectionAdapter-s :
53
55
  RubyClass jdbcConnection = RubyJdbcConnection.createJdbcConnectionClass(runtime);
54
56
  PostgresqlRubyJdbcConnection.createPostgresqlJdbcConnectionClass(runtime, jdbcConnection);
55
57
  MssqlRubyJdbcConnection.createMssqlJdbcConnectionClass(runtime, jdbcConnection);
56
58
  InformixRubyJdbcConnection.createInformixJdbcConnectionClass(runtime, jdbcConnection);
57
59
  OracleRubyJdbcConnection.createOracleJdbcConnectionClass(runtime, jdbcConnection);
58
- Sqlite3RubyJdbcConnection.createSqlite3JdbcConnectionClass(runtime, jdbcConnection);
60
+ SQLite3RubyJdbcConnection.createSQLite3JdbcConnectionClass(runtime, jdbcConnection);
59
61
  H2RubyJdbcConnection.createH2JdbcConnectionClass(runtime, jdbcConnection);
60
62
  MySQLRubyJdbcConnection.createMySQLJdbcConnectionClass(runtime, jdbcConnection);
61
63
  DB2RubyJdbcConnection.createDB2JdbcConnectionClass(runtime, jdbcConnection);
64
+ // ArJdbc :
62
65
  RubyModule arJdbc = runtime.getOrCreateModule("ArJdbc");
63
- rubyApi = JavaEmbedUtils.newObjectAdapter();
64
66
  MySQLModule.load(arJdbc);
65
- DerbyModule.load(arJdbc, rubyApi);
67
+ DerbyModule.load(arJdbc);
68
+ HSQLDBModule.load(arJdbc);
69
+ SQLite3Module.load(arJdbc);
70
+ OracleModule.load(arJdbc);
71
+ DB2Module.load(arJdbc);
66
72
  return true;
67
73
  }
74
+
68
75
  }
@@ -61,14 +61,14 @@ import org.jruby.RubySymbol;
61
61
  import org.jruby.RubyTime;
62
62
  import org.jruby.anno.JRubyMethod;
63
63
  import org.jruby.exceptions.RaiseException;
64
- import org.jruby.javasupport.Java;
65
64
  import org.jruby.javasupport.JavaEmbedUtils;
66
- import org.jruby.javasupport.JavaObject;
65
+ import org.jruby.javasupport.JavaUtil;
67
66
  import org.jruby.javasupport.util.RuntimeHelpers;
68
67
  import org.jruby.runtime.Arity;
69
68
  import org.jruby.runtime.Block;
70
69
  import org.jruby.runtime.ObjectAllocator;
71
70
  import org.jruby.runtime.ThreadContext;
71
+ import org.jruby.runtime.backtrace.RubyStackTraceElement;
72
72
  import org.jruby.runtime.builtin.IRubyObject;
73
73
  import org.jruby.util.ByteList;
74
74
 
@@ -85,9 +85,15 @@ public class RubyJdbcConnection extends RubyObject {
85
85
  super(runtime, metaClass);
86
86
  }
87
87
 
88
- public static RubyClass createJdbcConnectionClass(Ruby runtime) {
89
- RubyClass jdbcConnection = getConnectionAdapters(runtime).defineClassUnder("JdbcConnection",
90
- runtime.getObject(), JDBCCONNECTION_ALLOCATOR);
88
+ private static ObjectAllocator JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
89
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
90
+ return new RubyJdbcConnection(runtime, klass);
91
+ }
92
+ };
93
+
94
+ public static RubyClass createJdbcConnectionClass(final Ruby runtime) {
95
+ RubyClass jdbcConnection = getConnectionAdapters(runtime).
96
+ defineClassUnder("JdbcConnection", runtime.getObject(), JDBCCONNECTION_ALLOCATOR);
91
97
  jdbcConnection.defineAnnotatedMethods(RubyJdbcConnection.class);
92
98
 
93
99
  rubyApi = JavaEmbedUtils.newObjectAdapter();
@@ -95,31 +101,14 @@ public class RubyJdbcConnection extends RubyObject {
95
101
  return jdbcConnection;
96
102
  }
97
103
 
98
- private static ObjectAllocator JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
99
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
100
- return new RubyJdbcConnection(runtime, klass);
101
- }
102
- };
103
-
104
104
  protected static RubyModule getConnectionAdapters(Ruby runtime) {
105
105
  return (RubyModule) runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters");
106
106
  }
107
-
107
+
108
108
  protected String[] getTableTypes() {
109
109
  return TABLE_TYPES;
110
110
  }
111
111
 
112
- @JRubyMethod(name = "begin")
113
- public IRubyObject begin(ThreadContext context) throws SQLException {
114
- final Ruby runtime = context.getRuntime();
115
- return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
116
- public Object call(Connection c) throws SQLException {
117
- getConnection(true).setAutoCommit(false);
118
- return runtime.getNil();
119
- }
120
- });
121
- }
122
-
123
112
  @JRubyMethod(name = {"columns", "columns_internal"}, required = 1, optional = 2)
124
113
  public IRubyObject columns_internal(final ThreadContext context, final IRubyObject[] args)
125
114
  throws SQLException, IOException {
@@ -149,6 +138,17 @@ public class RubyJdbcConnection extends RubyObject {
149
138
  });
150
139
  }
151
140
 
141
+ @JRubyMethod(name = "begin")
142
+ public IRubyObject begin(ThreadContext context) throws SQLException {
143
+ final Ruby runtime = context.getRuntime();
144
+ return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
145
+ public Object call(Connection c) throws SQLException {
146
+ getConnection(true).setAutoCommit(false);
147
+ return runtime.getNil();
148
+ }
149
+ });
150
+ }
151
+
152
152
  @JRubyMethod(name = "commit")
153
153
  public IRubyObject commit(ThreadContext context) throws SQLException {
154
154
  Connection connection = getConnection(true);
@@ -164,14 +164,39 @@ public class RubyJdbcConnection extends RubyObject {
164
164
  return context.getRuntime().getNil();
165
165
  }
166
166
 
167
- @JRubyMethod(name = "connection", frame = false)
167
+ @JRubyMethod(name = "connection")
168
168
  public IRubyObject connection() {
169
- if (getConnection() == null) reconnect();
170
-
169
+ if ( getConnection(false) == null ) {
170
+ synchronized (this) {
171
+ if ( getConnection(false) == null ) {
172
+ reconnect();
173
+ }
174
+ }
175
+ }
171
176
  return getInstanceVariable("@connection");
172
177
  }
173
178
 
174
- @JRubyMethod(name = "database_name", frame = false)
179
+ @JRubyMethod(name = "disconnect!")
180
+ public IRubyObject disconnect(final ThreadContext context) {
181
+ // TODO: only here to try resolving multi-thread issues :
182
+ // https://github.com/jruby/activerecord-jdbc-adapter/issues/197
183
+ // https://github.com/jruby/activerecord-jdbc-adapter/issues/198
184
+ if ( Boolean.getBoolean("arjdbc.disconnect.debug") ) {
185
+ final Ruby runtime = context.getRuntime();
186
+ List backtrace = (List) context.createCallerBacktrace(runtime, 0);
187
+ runtime.getOut().println(this + " connection.disconnect! occured: ");
188
+ for ( Object element : backtrace ) runtime.getOut().println(element);
189
+ runtime.getOut().flush();
190
+ }
191
+ return setConnection(null);
192
+ }
193
+
194
+ @JRubyMethod(name = "reconnect!")
195
+ public IRubyObject reconnect() {
196
+ return setConnection( getConnectionFactory().newConnection() );
197
+ }
198
+
199
+ @JRubyMethod(name = "database_name")
175
200
  public IRubyObject database_name(ThreadContext context) throws SQLException {
176
201
  Connection connection = getConnection(true);
177
202
  String name = connection.getCatalog();
@@ -185,12 +210,7 @@ public class RubyJdbcConnection extends RubyObject {
185
210
  return context.getRuntime().newString(name);
186
211
  }
187
212
 
188
- @JRubyMethod(name = "disconnect!", frame = false)
189
- public IRubyObject disconnect() {
190
- return setConnection(null);
191
- }
192
-
193
- @JRubyMethod
213
+ @JRubyMethod(name = "execute", required = 1)
194
214
  public IRubyObject execute(final ThreadContext context, final IRubyObject sql) {
195
215
  return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
196
216
  public Object call(Connection c) throws SQLException {
@@ -481,11 +501,6 @@ public class RubyJdbcConnection extends RubyObject {
481
501
  });
482
502
  }
483
503
 
484
- @JRubyMethod(name = "reconnect!")
485
- public IRubyObject reconnect() {
486
- return setConnection(getConnectionFactory().newConnection());
487
- }
488
-
489
504
 
490
505
  @JRubyMethod(name = "rollback")
491
506
  public IRubyObject rollback(ThreadContext context) throws SQLException {
@@ -698,19 +713,6 @@ public class RubyJdbcConnection extends RubyObject {
698
713
  return runtime.newFloat(doubleValue);
699
714
  }
700
715
 
701
- protected Connection getConnection() {
702
- return getConnection(false);
703
- }
704
-
705
- protected Connection getConnection(boolean error) {
706
- Connection conn = (Connection) dataGetStruct();
707
- if(error && conn == null) {
708
- RubyClass err = getRuntime().getModule("ActiveRecord").getClass("ConnectionNotEstablished");
709
- throw new RaiseException(getRuntime(), err, "no connection available", false);
710
- }
711
- return conn;
712
- }
713
-
714
716
  protected IRubyObject getAdapter(ThreadContext context) {
715
717
  return callMethod(context, "adapter");
716
718
  }
@@ -721,17 +723,21 @@ public class RubyJdbcConnection extends RubyObject {
721
723
 
722
724
  protected JdbcConnectionFactory getConnectionFactory() throws RaiseException {
723
725
  IRubyObject connection_factory = getInstanceVariable("@connection_factory");
724
- JdbcConnectionFactory factory = null;
726
+ if (connection_factory == null) {
727
+ throw getRuntime().newRuntimeError("@connection_factory not set");
728
+ }
729
+ JdbcConnectionFactory connectionFactory;
725
730
  try {
726
- factory = (JdbcConnectionFactory) JavaEmbedUtils.rubyToJava(
727
- connection_factory.getRuntime(), connection_factory, JdbcConnectionFactory.class);
728
- } catch (Exception e) {
729
- factory = null;
731
+ connectionFactory = (JdbcConnectionFactory)
732
+ connection_factory.toJava(JdbcConnectionFactory.class);
730
733
  }
731
- if (factory == null) {
734
+ catch (Exception e) { // TODO debug this !
735
+ connectionFactory = null;
736
+ }
737
+ if (connectionFactory == null) {
732
738
  throw getRuntime().newRuntimeError("@connection_factory not set properly");
733
739
  }
734
- return factory;
740
+ return connectionFactory;
735
741
  }
736
742
 
737
743
  private static String[] getTypes(IRubyObject typeArg) {
@@ -880,12 +886,26 @@ public class RubyJdbcConnection extends RubyObject {
880
886
  return RubyString.newUnicodeString(runtime, str.toString());
881
887
  }
882
888
 
883
- private IRubyObject setConnection(Connection c) {
884
- close(getConnection()); // Close previously open connection if there is one
889
+ protected final Connection getConnection() {
890
+ return getConnection(false);
891
+ }
885
892
 
886
- IRubyObject rubyconn = c != null ? wrappedConnection(c) : getRuntime().getNil();
887
- setInstanceVariable("@connection", rubyconn);
888
- dataWrapStruct(c);
893
+ protected Connection getConnection(boolean error) {
894
+ final Connection connection = (Connection) dataGetStruct();
895
+ if ( connection == null && error ) {
896
+ RubyClass err = getRuntime().getModule("ActiveRecord").getClass("ConnectionNotEstablished");
897
+ throw new RaiseException(getRuntime(), err, "no connection available", false);
898
+ }
899
+ return connection;
900
+ }
901
+
902
+ private synchronized RubyJdbcConnection setConnection(final Connection connection) {
903
+ close( getConnection(false) ); // close previously open connection if there is one
904
+
905
+ final IRubyObject rubyConnectionObject =
906
+ connection != null ? wrappedConnection(connection) : getRuntime().getNil();
907
+ setInstanceVariable( "@connection", rubyConnectionObject );
908
+ dataWrapStruct(connection);
889
909
  return this;
890
910
  }
891
911
 
@@ -1061,18 +1081,21 @@ public class RubyJdbcConnection extends RubyObject {
1061
1081
  * Create a string which represents a sql type usable by Rails from the resultSet column
1062
1082
  * metadata object.
1063
1083
  */
1064
- protected String typeFromResultSet(ResultSet resultSet) throws SQLException {
1065
- int precision = intFromResultSet(resultSet, COLUMN_SIZE);
1066
- int scale = intFromResultSet(resultSet, DECIMAL_DIGITS);
1067
-
1068
- String type = resultSet.getString(TYPE_NAME);
1069
- if (precision > 0) {
1070
- type += "(" + precision;
1071
- if(scale > 0) type += "," + scale;
1072
- type += ")";
1073
- }
1084
+ protected String typeFromResultSet(final ResultSet resultSet) throws SQLException {
1085
+ final int precision = intFromResultSet(resultSet, COLUMN_SIZE);
1086
+ final int scale = intFromResultSet(resultSet, DECIMAL_DIGITS);
1074
1087
 
1075
- return type;
1088
+ final String type = resultSet.getString(TYPE_NAME);
1089
+ return formatTypeWithPrecisionAndScale(type, precision, scale);
1090
+ }
1091
+
1092
+ protected static String formatTypeWithPrecisionAndScale(final String type, final int precision, final int scale) {
1093
+ if ( precision <= 0 ) return type;
1094
+
1095
+ final StringBuilder typeStr = new StringBuilder().append(type);
1096
+ typeStr.append('(').append(precision); // type += "(" + precision;
1097
+ if ( scale > 0 ) typeStr.append(',').append(scale); // type += "," + scale;
1098
+ return typeStr.append(')').toString(); // type += ")";
1076
1099
  }
1077
1100
 
1078
1101
  private IRubyObject defaultValueFromResultSet(Ruby runtime, ResultSet resultSet)
@@ -1134,21 +1157,20 @@ public class RubyJdbcConnection extends RubyObject {
1134
1157
 
1135
1158
  protected IRubyObject unmarshalResults(ThreadContext context, DatabaseMetaData metadata,
1136
1159
  Statement stmt, boolean downCase) throws SQLException {
1137
- Ruby runtime = context.getRuntime();
1138
- List<IRubyObject> sets = new ArrayList<IRubyObject>();
1139
-
1140
- while (true) {
1141
- sets.add(unmarshalResult(context, metadata, stmt.getResultSet(), downCase));
1142
- if (!stmt.getMoreResults()) {
1143
- break;
1144
- }
1160
+
1161
+ IRubyObject result = unmarshalResult(context, metadata, stmt.getResultSet(), downCase);
1162
+
1163
+ if ( ! stmt.getMoreResults() ) return result;
1164
+
1165
+ final List<IRubyObject> results = new ArrayList<IRubyObject>();
1166
+ results.add(result);
1167
+ do {
1168
+ result = unmarshalResult(context, metadata, stmt.getResultSet(), downCase);
1169
+ results.add(result);
1145
1170
  }
1171
+ while ( stmt.getMoreResults() );
1146
1172
 
1147
- if (sets.size() > 1) {
1148
- return runtime.newArray(sets);
1149
- } else {
1150
- return sets.get(0);
1151
- }
1173
+ return context.getRuntime().newArray(results);
1152
1174
  }
1153
1175
 
1154
1176
  /**
@@ -1225,8 +1247,8 @@ public class RubyJdbcConnection extends RubyObject {
1225
1247
  return (RuntimeException) arError;
1226
1248
  }
1227
1249
 
1228
- private IRubyObject wrappedConnection(Connection c) {
1229
- return Java.java_to_ruby(this, JavaObject.wrap(getRuntime(), c), Block.NULL_BLOCK);
1250
+ private IRubyObject wrappedConnection(final Connection connection) {
1251
+ return JavaUtil.convertJavaToRuby( getRuntime(), connection );
1230
1252
  }
1231
1253
 
1232
1254
  /**