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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. data/.gitignore +12 -11
  2. data/.travis.yml +36 -7
  3. data/Appraisals +3 -3
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +13 -6
  6. data/History.txt +64 -0
  7. data/README.md +8 -1
  8. data/Rakefile +3 -1
  9. data/gemfiles/rails23.gemfile +1 -1
  10. data/gemfiles/rails23.gemfile.lock +6 -5
  11. data/gemfiles/rails30.gemfile +1 -1
  12. data/gemfiles/rails30.gemfile.lock +7 -6
  13. data/gemfiles/rails31.gemfile +1 -1
  14. data/gemfiles/rails31.gemfile.lock +6 -5
  15. data/gemfiles/rails32.gemfile +1 -1
  16. data/gemfiles/rails32.gemfile.lock +6 -5
  17. data/gemfiles/rails40.gemfile +2 -4
  18. data/gemfiles/rails40.gemfile.lock +37 -51
  19. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  20. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -1
  21. data/lib/arel/visitors/db2.rb +5 -1
  22. data/lib/arel/visitors/hsqldb.rb +1 -0
  23. data/lib/arel/visitors/sql_server.rb +55 -13
  24. data/lib/arjdbc/db2/adapter.rb +197 -227
  25. data/lib/arjdbc/db2/as400.rb +124 -0
  26. data/lib/arjdbc/db2/connection_methods.rb +20 -1
  27. data/lib/arjdbc/derby/adapter.rb +17 -85
  28. data/lib/arjdbc/derby/connection_methods.rb +2 -1
  29. data/lib/arjdbc/discover.rb +55 -47
  30. data/lib/arjdbc/h2/adapter.rb +52 -18
  31. data/lib/arjdbc/h2/connection_methods.rb +10 -2
  32. data/lib/arjdbc/hsqldb/adapter.rb +33 -9
  33. data/lib/arjdbc/hsqldb/connection_methods.rb +10 -2
  34. data/lib/arjdbc/informix.rb +2 -1
  35. data/lib/arjdbc/jdbc.rb +5 -1
  36. data/lib/arjdbc/jdbc/adapter.rb +167 -89
  37. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  38. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  39. data/lib/arjdbc/jdbc/base_ext.rb +25 -3
  40. data/lib/arjdbc/jdbc/callbacks.rb +9 -8
  41. data/lib/arjdbc/jdbc/column.rb +8 -20
  42. data/lib/arjdbc/jdbc/connection.rb +69 -80
  43. data/lib/arjdbc/jdbc/extension.rb +6 -8
  44. data/lib/arjdbc/jdbc/jdbc.rake +3 -141
  45. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -10
  46. data/lib/arjdbc/mssql/adapter.rb +108 -34
  47. data/lib/arjdbc/mssql/connection_methods.rb +3 -1
  48. data/lib/arjdbc/mssql/limit_helpers.rb +3 -2
  49. data/lib/arjdbc/mssql/lock_helpers.rb +5 -1
  50. data/lib/arjdbc/mysql/adapter.rb +127 -70
  51. data/lib/arjdbc/mysql/connection_methods.rb +5 -2
  52. data/lib/arjdbc/oracle/adapter.rb +124 -94
  53. data/lib/arjdbc/oracle/connection_methods.rb +2 -1
  54. data/lib/arjdbc/postgresql/adapter.rb +99 -67
  55. data/lib/arjdbc/postgresql/column_cast.rb +3 -5
  56. data/lib/arjdbc/postgresql/connection_methods.rb +6 -6
  57. data/lib/arjdbc/railtie.rb +3 -1
  58. data/lib/arjdbc/sqlite3/adapter.rb +60 -43
  59. data/lib/arjdbc/sqlite3/connection_methods.rb +9 -9
  60. data/lib/arjdbc/sybase.rb +1 -1
  61. data/lib/arjdbc/tasks.rb +13 -0
  62. data/lib/arjdbc/tasks/database_tasks.rb +50 -0
  63. data/lib/arjdbc/tasks/databases.rake +89 -0
  64. data/lib/arjdbc/tasks/databases3.rake +203 -0
  65. data/lib/arjdbc/tasks/databases4.rake +39 -0
  66. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  67. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  68. data/lib/arjdbc/tasks/h2_database_tasks.rb +29 -0
  69. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  70. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +122 -0
  71. data/lib/arjdbc/tasks/mssql_database_tasks.rb +36 -0
  72. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  73. data/lib/arjdbc/tasks/oracle_database_tasks.rb +62 -0
  74. data/lib/arjdbc/version.rb +1 -1
  75. data/pom.xml +11 -12
  76. data/rails_generators/jdbc_generator.rb +1 -1
  77. data/rails_generators/templates/config/initializers/jdbc.rb +8 -5
  78. data/rails_generators/templates/lib/tasks/jdbc.rake +7 -4
  79. data/rakelib/02-test.rake +42 -15
  80. data/rakelib/compile.rake +29 -2
  81. data/rakelib/db.rake +2 -1
  82. data/rakelib/rails.rake +23 -6
  83. data/src/java/arjdbc/ArJdbcModule.java +175 -0
  84. data/src/java/arjdbc/db2/DB2Module.java +2 -1
  85. data/src/java/arjdbc/derby/DerbyModule.java +5 -24
  86. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +3 -2
  87. data/src/java/arjdbc/jdbc/AdapterJavaService.java +3 -46
  88. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1001 -259
  89. data/src/java/arjdbc/mssql/MSSQLModule.java +2 -1
  90. data/src/java/arjdbc/mysql/MySQLModule.java +4 -3
  91. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +12 -7
  92. data/src/java/arjdbc/oracle/OracleModule.java +2 -1
  93. data/src/java/arjdbc/sqlite3/SQLite3Module.java +2 -1
  94. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +12 -0
  95. data/test/db/db2.rb +14 -7
  96. data/test/db/db2/rake_test.rb +82 -0
  97. data/test/db/db2/rake_test_data.sql +35 -0
  98. data/test/db/db2/simple_test.rb +20 -0
  99. data/test/db/db2/unit_test.rb +3 -1
  100. data/test/db/derby.rb +7 -5
  101. data/test/db/derby/rake_test.rb +96 -0
  102. data/test/db/derby/simple_test.rb +10 -2
  103. data/test/db/h2.rb +6 -8
  104. data/test/db/h2/identity_column_test.rb +35 -0
  105. data/test/db/h2/offset_test.rb +49 -0
  106. data/test/db/h2/rake_test.rb +98 -0
  107. data/test/db/h2/schema_dump_test.rb +5 -1
  108. data/test/db/hsqldb.rb +6 -10
  109. data/test/db/hsqldb/rake_test.rb +101 -0
  110. data/test/db/hsqldb/schema_dump_test.rb +5 -1
  111. data/test/db/hsqldb/simple_test.rb +8 -0
  112. data/test/db/jndi_config.rb +1 -3
  113. data/test/db/jndi_pooled_config.rb +1 -3
  114. data/test/db/mssql/limit_offset_test.rb +23 -14
  115. data/test/db/mssql/rake_test.rb +143 -0
  116. data/test/db/mysql/_rails_test_mysql.32.out +1069 -1252
  117. data/test/db/mysql/nonstandard_primary_key_test.rb +21 -24
  118. data/test/db/mysql/rake_test.rb +97 -0
  119. data/test/db/mysql/schema_dump_test.rb +11 -11
  120. data/test/db/mysql/simple_test.rb +52 -3
  121. data/test/db/mysql/statement_escaping_test.rb +46 -0
  122. data/test/db/oracle/rake_test.rb +100 -0
  123. data/test/db/oracle/simple_test.rb +48 -0
  124. data/test/db/postgres/_rails_test_postgres.32.out +998 -1370
  125. data/test/db/postgres/active_schema_unit_test.rb +68 -0
  126. data/test/db/postgres/connection_test.rb +10 -2
  127. data/test/db/postgres/data_types_test.rb +2 -2
  128. data/test/db/postgres/ltree_test.rb +6 -5
  129. data/test/db/postgres/native_types_test.rb +1 -5
  130. data/test/db/postgres/rake_test.rb +117 -0
  131. data/test/db/postgres/schema_dump_test.rb +9 -2
  132. data/test/db/postgres/schema_test.rb +4 -2
  133. data/test/db/postgres/simple_test.rb +57 -16
  134. data/test/db/sqlite3.rb +3 -10
  135. data/test/db/sqlite3/_rails_test_sqlite3.32.out +1070 -1298
  136. data/test/db/sqlite3/rake_test.rb +71 -0
  137. data/test/db/sqlite3/simple_test.rb +9 -9
  138. data/test/has_many_through.rb +4 -1
  139. data/test/jdbc/db2.rb +14 -1
  140. data/test/jdbc_column_test.rb +23 -0
  141. data/test/{generic_jdbc_connection_test.rb → jdbc_connection_test.rb} +22 -17
  142. data/test/jndi_callbacks_test.rb +26 -28
  143. data/test/jndi_test.rb +7 -16
  144. data/test/models/data_types.rb +2 -1
  145. data/test/models/thing.rb +1 -0
  146. data/test/rails/mysql.rb +13 -0
  147. data/test/rails/sqlite3/version.rb +6 -0
  148. data/test/rails_stub.rb +31 -0
  149. data/test/rake_test_support.rb +298 -0
  150. data/test/serialize.rb +2 -4
  151. data/test/{helper.rb → shared_helper.rb} +0 -0
  152. data/test/simple.rb +167 -93
  153. data/test/test_helper.rb +52 -16
  154. metadata +388 -354
  155. data/lib/pg.rb +0 -26
  156. data/test/abstract_db_create.rb +0 -139
  157. data/test/activerecord/connection_adapters/type_conversion_test.rb +0 -36
  158. data/test/db/mssql/db_create_test.rb +0 -29
  159. data/test/db/mysql/db_create_test.rb +0 -33
  160. data/test/db/postgres/db_create_test.rb +0 -44
  161. data/test/db/postgres/db_drop_test.rb +0 -17
@@ -1,3 +1,4 @@
1
+ ArJdbc.load_java_part :H2
1
2
  require 'arjdbc/hsqldb/adapter'
2
3
 
3
4
  module ArJdbc
@@ -11,11 +12,11 @@ module ArJdbc
11
12
  def self.column_selector
12
13
  [ /\.h2\./i, lambda { |cfg, column| column.extend(::ArJdbc::H2::Column) } ]
13
14
  end
14
-
15
+
15
16
  module Column
16
-
17
+
17
18
  private
18
-
19
+
19
20
  def extract_limit(sql_type)
20
21
  limit = super
21
22
  case @sql_type = sql_type.downcase
@@ -34,14 +35,14 @@ module ArJdbc
34
35
  when /blob|image|oid/i then @sql_type = 'blob'; limit = nil
35
36
  when /clob|text/i then @sql_type = 'clob'; limit = nil
36
37
  # NOTE: use lower-case due SchemaDumper not handling it's decimal/integer
37
- # optimization case-insensitively due : column.type == :integer &&
38
+ # optimization case-insensitively due : column.type == :integer &&
38
39
  # [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
39
40
  when /^decimal\(65535,32767\)/i
40
41
  @sql_type = 'decimal'; nil
41
42
  end
42
43
  limit
43
44
  end
44
-
45
+
45
46
  def simplified_type(field_type)
46
47
  case field_type
47
48
  when /^bit|bool/i then :boolean
@@ -54,7 +55,7 @@ module ArJdbc
54
55
  super
55
56
  end
56
57
  end
57
-
58
+
58
59
  # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
59
60
  def default_value(value)
60
61
  # H2 auto-generated key default value
@@ -63,11 +64,11 @@ module ArJdbc
63
64
  return $1 if value =~ /^'(.*)'$/
64
65
  value
65
66
  end
66
-
67
+
67
68
  end
68
-
69
+
69
70
  ADAPTER_NAME = 'H2' # :nodoc:
70
-
71
+
71
72
  def adapter_name # :nodoc:
72
73
  ADAPTER_NAME
73
74
  end
@@ -79,14 +80,14 @@ module ArJdbc
79
80
  'jdbch2' => ::Arel::Visitors::HSQLDB,
80
81
  })
81
82
  end
82
-
83
+
83
84
  # #deprecated
84
85
  def h2_adapter # :nodoc:
85
86
  true
86
87
  end
87
88
 
88
89
  NATIVE_DATABASE_TYPES = {
89
- :primary_key => "integer GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY",
90
+ :primary_key => "bigint identity",
90
91
  :boolean => { :name => "boolean" },
91
92
  :tinyint => { :name => "tinyint", :limit => 1 },
92
93
  :smallint => { :name => "smallint", :limit => 2 },
@@ -115,11 +116,11 @@ module ArJdbc
115
116
  def native_database_types
116
117
  NATIVE_DATABASE_TYPES.dup
117
118
  end
118
-
119
+
119
120
  def modify_types(types)
120
121
  types
121
122
  end
122
-
123
+
123
124
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
124
125
  case type.to_sym
125
126
  when :integer
@@ -146,6 +147,10 @@ module ArJdbc
146
147
  super
147
148
  end
148
149
  end
150
+
151
+ def empty_insert_statement_value
152
+ "VALUES ()"
153
+ end
149
154
 
150
155
  def tables
151
156
  @connection.tables(nil, h2_schema)
@@ -164,7 +169,7 @@ module ArJdbc
164
169
  def current_schema
165
170
  execute('CALL SCHEMA()')[0].values[0]
166
171
  end
167
-
172
+
168
173
  def quote(value, column = nil) # :nodoc:
169
174
  case value
170
175
  when String
@@ -177,9 +182,9 @@ module ArJdbc
177
182
  super
178
183
  end
179
184
  end
180
-
185
+
181
186
  # EXPLAIN support :
182
-
187
+
183
188
  def supports_explain?; true; end
184
189
 
185
190
  def explain(arel, binds = [])
@@ -188,8 +193,37 @@ module ArJdbc
188
193
  raw_result[0].values.join("\n") # [ "SELECT \n ..." ].to_s
189
194
  end
190
195
 
191
- private
196
+ def structure_dump
197
+ execute('SCRIPT SIMPLE').map do |result|
198
+ # [ { 'script' => SQL }, { 'script' ... }, ... ]
199
+ case sql = result.first[1] # ['script']
200
+ when /CREATE USER IF NOT EXISTS SA/i then nil
201
+ else sql
202
+ end
203
+ end.compact.join("\n\n")
204
+ end
205
+
206
+ def structure_load(dump)
207
+ dump.each_line("\n\n") { |ddl| execute(ddl) }
208
+ end
209
+
210
+ def shutdown
211
+ execute 'SHUTDOWN COMPACT'
212
+ end
213
+
214
+ def recreate_database(name = nil, options = {}) # :nodoc:
215
+ drop_database(name)
216
+ create_database(name, options)
217
+ end
192
218
 
219
+ def create_database(name = nil, options = {}); end # :nodoc:
220
+
221
+ def drop_database(name = nil) # :nodoc:
222
+ execute('DROP ALL OBJECTS')
223
+ end
224
+
225
+ private
226
+
193
227
  def change_column_null(table_name, column_name, null, default = nil)
194
228
  if !null && !default.nil?
195
229
  execute("UPDATE #{table_name} SET #{column_name}=#{quote(default)} WHERE #{column_name} IS NULL")
@@ -204,6 +238,6 @@ module ArJdbc
204
238
  def h2_schema
205
239
  @config[:schema] || ''
206
240
  end
207
-
241
+
208
242
  end
209
243
  end
@@ -6,9 +6,17 @@ ArJdbc::ConnectionMethods.module_eval do
6
6
  rescue LoadError # assuming driver.jar is on the class-path
7
7
  end
8
8
 
9
- config[:url] ||= "jdbc:h2:#{config[:database]}"
9
+ config[:url] ||= begin
10
+ db = config[:database]
11
+ if db[0, 4] == 'mem:' || db[0, 5] == 'file:' || db[0, 5] == 'hsql:'
12
+ "jdbc:h2:#{db}"
13
+ else
14
+ "jdbc:h2:file:#{db}"
15
+ end
16
+ end
10
17
  config[:driver] ||= defined?(::Jdbc::H2.driver_name) ? ::Jdbc::H2.driver_name : 'org.h2.Driver'
11
- config[:adapter_spec] = ::ArJdbc::H2
18
+ config[:adapter_spec] ||= ::ArJdbc::H2
19
+
12
20
  embedded_driver(config)
13
21
  end
14
22
  alias_method :jdbch2_connection, :h2_connection
@@ -1,3 +1,4 @@
1
+ ArJdbc.load_java_part :HSQLDB
1
2
  require 'arjdbc/hsqldb/explain_support'
2
3
 
3
4
  module ArJdbc
@@ -188,6 +189,12 @@ module ArJdbc
188
189
  end
189
190
  end
190
191
 
192
+ def empty_insert_statement_value
193
+ # on HSQLDB only work with tables that have a default value for each
194
+ # and every column ... you'll need to avoid `Model.create!` on 4.0
195
+ 'DEFAULT VALUES'
196
+ end
197
+
191
198
  # filter out system tables (that otherwise end up in db/schema.rb)
192
199
  # JdbcConnection#tables
193
200
  # now takes an optional block filter so we can screen out
@@ -201,19 +208,36 @@ module ArJdbc
201
208
  def remove_index(table_name, options = {})
202
209
  execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
203
210
  end
211
+
212
+ def structure_dump
213
+ execute('SCRIPT').map do |result|
214
+ # [ { 'command' => SQL }, { 'command' ... }, ... ]
215
+ case sql = result.first[1] # ['command']
216
+ when /CREATE USER SA PASSWORD DIGEST .*?/i then nil
217
+ when /CREATE SCHEMA PUBLIC AUTHORIZATION DBA/i then nil
218
+ when /GRANT DBA TO SA/i then nil
219
+ else sql
220
+ end
221
+ end.compact.join("\n\n")
222
+ end
204
223
 
205
- def recreate_database(name, options = {})
206
- drop_database(name)
224
+ def structure_load(dump)
225
+ dump.each_line("\n\n") { |ddl| execute(ddl) }
207
226
  end
208
227
 
209
- # do nothing since database gets created upon connection. However
210
- # this method gets called by rails db rake tasks so now we're
211
- # avoiding method_missing error
212
- def create_database(name)
228
+ def shutdown
229
+ execute 'SHUTDOWN'
213
230
  end
214
-
215
- def drop_database(name)
216
- execute("DROP ALL OBJECTS")
231
+
232
+ def recreate_database(name = nil, options = {}) # :nodoc:
233
+ drop_database(name)
234
+ create_database(name, options)
235
+ end
236
+
237
+ def create_database(name = nil, options = {}); end # :nodoc:
238
+
239
+ def drop_database(name = nil) # :nodoc:
240
+ execute('DROP SCHEMA PUBLIC CASCADE')
217
241
  end
218
242
 
219
243
  end
@@ -6,10 +6,18 @@ ArJdbc::ConnectionMethods.module_eval do
6
6
  rescue LoadError # assuming driver.jar is on the class-path
7
7
  end
8
8
 
9
- config[:url] ||= "jdbc:hsqldb:#{config[:database]}"
9
+ config[:url] ||= begin
10
+ db = config[:database]
11
+ if db[0, 4] == 'mem:' || db[0, 5] == 'file:' || db[0, 5] == 'hsql:'
12
+ "jdbc:hsqldb:#{db}"
13
+ else
14
+ "jdbc:hsqldb:file:#{db}"
15
+ end
16
+ end
10
17
  config[:driver] ||= defined?(::Jdbc::HSQLDB.driver_name) ? ::Jdbc::HSQLDB.driver_name : 'org.hsqldb.jdbcDriver'
11
- config[:adapter_spec] = ::ArJdbc::HSQLDB
18
+ config[:adapter_spec] ||= ::ArJdbc::HSQLDB
12
19
  config[:connection_alive_sql] ||= 'CALL PI()' # does not like 'SELECT 1'
20
+
13
21
  embedded_driver(config)
14
22
  end
15
23
  alias_method :jdbchsqldb_connection, :hsqldb_connection
@@ -1,3 +1,4 @@
1
1
  require 'arjdbc'
2
- require 'arjdbc/informix/connection_methods'
2
+ ArJdbc.load_java_part :Informix
3
3
  require 'arjdbc/informix/adapter'
4
+ require 'arjdbc/informix/connection_methods'
data/lib/arjdbc/jdbc.rb CHANGED
@@ -1,2 +1,6 @@
1
1
  require 'arjdbc/jdbc/adapter'
2
- module ArJdbc; self.discover_extensions; end
2
+ if Java::JavaLang::Boolean.getBoolean('arjdbc.extensions.discover')
3
+ module ArJdbc; self.discover_extensions; end
4
+ else
5
+ require 'arjdbc/discover'
6
+ end
@@ -16,31 +16,42 @@ module ActiveRecord
16
16
  module ConnectionAdapters
17
17
  class JdbcAdapter < AbstractAdapter
18
18
  extend ShadowCoreMethods
19
- include JdbcConnectionPoolCallbacks if JdbcConnectionPoolCallbacks.needed?
19
+
20
+ include JdbcConnectionPoolCallbacks
20
21
 
21
22
  attr_reader :config
22
-
23
- def initialize(connection, logger, config)
24
- @config = config
25
- spec = config[:adapter_spec] || adapter_spec(config)
26
- config[:adapter_spec] ||= spec
27
- unless connection
28
- connection_class = jdbc_connection_class spec
29
- connection = connection_class.new config
23
+
24
+ def initialize(connection, logger, config = nil) # (logger, config)
25
+ if config.nil? && logger.respond_to?(:key?) # only 2 arguments given
26
+ config, logger, connection = logger, connection, nil
30
27
  end
28
+
29
+ @config = config
30
+
31
+ @config[:adapter_spec] = adapter_spec(@config) unless @config.key?(:adapter_spec)
32
+ spec = @config[:adapter_spec]
33
+
34
+ connection ||= jdbc_connection_class(spec).new(@config)
35
+
31
36
  super(connection, logger)
32
- if spec && (config[:adapter_class].nil? || config[:adapter_class] == JdbcAdapter)
33
- extend spec
34
- end
35
- configure_arel2_visitors(config)
36
- connection.adapter = self
37
+
38
+ # kind of like `extend ArJdbc::MyDB if self.class == JdbcAdapter` :
39
+ klass = @config[:adapter_class]
40
+ extend spec if spec && ( ! klass || klass == JdbcAdapter)
41
+
42
+ connection.adapter = self # prepares native database types
43
+
44
+ # NOTE: should not be necessary for JNDI due reconnect! on checkout :
45
+ configure_connection if respond_to?(:configure_connection)
46
+
37
47
  JndiConnectionPoolCallbacks.prepare(self, connection)
48
+
49
+ @visitor = new_visitor(@config) # nil if no AREL (AR-2.3)
38
50
  end
39
-
51
+
40
52
  def jdbc_connection_class(spec)
41
53
  connection_class = spec.jdbc_connection_class if spec && spec.respond_to?(:jdbc_connection_class)
42
- connection_class = ::ActiveRecord::ConnectionAdapters::JdbcConnection unless connection_class
43
- connection_class
54
+ connection_class ? connection_class : ::ActiveRecord::ConnectionAdapters::JdbcConnection
44
55
  end
45
56
 
46
57
  def jdbc_column_class
@@ -67,18 +78,17 @@ module ActiveRecord
67
78
  # Locate specialized adapter specification if one exists based on config data
68
79
  def adapter_spec(config)
69
80
  dialect = (config[:dialect] || config[:driver]).to_s
70
- ::ArJdbc.constants.sort.each do |constant|
71
- constant = ::ArJdbc.const_get(constant) # e.g. ArJdbc::MySQL
72
-
81
+ ::ArJdbc.modules.each do |constant| # e.g. ArJdbc::MySQL
73
82
  if constant.respond_to?(:adapter_matcher)
74
83
  spec = constant.adapter_matcher(dialect, config)
75
84
  return spec if spec
76
85
  end
77
86
  end
78
87
 
79
- if config[:jndi] && ! config[:dialect]
88
+ if (config[:jndi] || config[:data_source]) && ! config[:dialect]
80
89
  begin
81
- data_source = Java::JavaxNaming::InitialContext.new.lookup(config[:jndi])
90
+ data_source = config[:data_source] ||
91
+ Java::JavaxNaming::InitialContext.new.lookup(config[:jndi])
82
92
  connection = data_source.getConnection
83
93
  config[:dialect] = connection.getMetaData.getDatabaseProductName
84
94
  rescue Java::JavaSql::SQLException => e
@@ -97,41 +107,99 @@ module ActiveRecord
97
107
  types
98
108
  end
99
109
 
100
- def adapter_name #:nodoc:
110
+ def adapter_name # :nodoc:
101
111
  'JDBC'
102
112
  end
103
113
 
114
+ def self.arel2_visitors(config)
115
+ { 'jdbc' => ::Arel::Visitors::ToSql }
116
+ end
117
+
118
+ # NOTE: called from {ConnectionPool#checkout} (up till AR-3.2)
104
119
  def self.visitor_for(pool)
105
120
  config = pool.spec.config
106
- adapter = config[:adapter]
107
- adapter_spec = config[:adapter_spec] || self
108
- if adapter =~ /^(jdbc|jndi)$/
109
- adapter_spec.arel2_visitors(config).values.first.new(pool)
110
- else
111
- adapter_spec.arel2_visitors(config)[adapter].new(pool)
121
+ adapter = config[:adapter] # e.g. "sqlite3" (based on {#adapter_name})
122
+ unless visitor = ::Arel::Visitors::VISITORS[ adapter ]
123
+ adapter_spec = config[:adapter_spec] || self # e.g. ArJdbc::SQLite3
124
+ if adapter =~ /^(jdbc|jndi)$/
125
+ visitor = adapter_spec.arel2_visitors(config).values.first
126
+ else
127
+ visitor = adapter_spec.arel2_visitors(config)[adapter]
128
+ end
112
129
  end
130
+ ( prepared_statements?(config) ? visitor : bind_substitution(visitor) ).new(pool)
113
131
  end
114
132
 
115
- def self.arel2_visitors(config)
116
- { 'jdbc' => ::Arel::Visitors::ToSql }
133
+ def self.configure_arel2_visitors(config)
134
+ visitors = ::Arel::Visitors::VISITORS
135
+ klass = config[:adapter_spec]
136
+ klass = self unless klass.respond_to?(:arel2_visitors)
137
+ visitor = nil
138
+ klass.arel2_visitors(config).each do |name, arel|
139
+ visitors[name] = ( visitor = arel )
140
+ end
141
+ if visitor && config[:adapter] =~ /^(jdbc|jndi)$/
142
+ visitors[ config[:adapter] ] = visitor
143
+ end
144
+ visitor
117
145
  end
118
146
 
119
- def configure_arel2_visitors(config)
120
- if defined?(::Arel::Visitors::VISITORS)
121
- visitors = ::Arel::Visitors::VISITORS
122
- visitor = nil
123
- adapter_spec = [config[:adapter_spec], self.class].detect {|a| a && a.respond_to?(:arel2_visitors) }
124
- adapter_spec.arel2_visitors(config).each do |k,v|
125
- visitor = v
126
- visitors[k] = v
147
+ def new_visitor(config = self.config)
148
+ visitor = ::Arel::Visitors::VISITORS[ adapter = config[:adapter] ]
149
+ unless visitor
150
+ visitor = self.class.configure_arel2_visitors(config)
151
+ unless visitor
152
+ raise "no visitor configured for adapter: #{adapter.inspect}"
127
153
  end
128
- if visitor && config[:adapter] =~ /^(jdbc|jndi)$/
129
- visitors[config[:adapter]] = visitor
154
+ end
155
+ ( prepared_statements? ? visitor : bind_substitution(visitor) ).new(self)
156
+ end
157
+ protected :new_visitor
158
+
159
+ unless defined? ::Arel::Visitors::VISITORS # NO-OP when no AREL (AR-2.3)
160
+ def self.configure_arel2_visitors(config); end
161
+ def new_visitor(config = self.config); end
162
+ end
163
+
164
+ @@bind_substitutions = nil
165
+
166
+ # @return a {#Arel::Visitors::BindVisitor} class for given visitor type
167
+ def self.bind_substitution(visitor)
168
+ # NOTE: similar convention as in AR (but no base substitution type) :
169
+ # class BindSubstitution < ::Arel::Visitors::ToSql
170
+ # include ::Arel::Visitors::BindVisitor
171
+ # end
172
+ return const_get(:BindSubstitution) if const_defined?(:BindSubstitution)
173
+
174
+ @@bind_substitutions ||= Java::JavaUtil::HashMap.new
175
+ unless bind_visitor = @@bind_substitutions.get(visitor)
176
+ @@bind_substitutions.synchronized do
177
+ unless @@bind_substitutions.get(visitor)
178
+ bind_visitor = Class.new(visitor) do
179
+ include ::Arel::Visitors::BindVisitor
180
+ end
181
+ @@bind_substitutions.put(visitor, bind_visitor)
182
+ end
130
183
  end
131
- @visitor = visitors[config[:adapter]].new(self)
184
+ bind_visitor = @@bind_substitutions.get(visitor)
132
185
  end
186
+ bind_visitor
133
187
  end
134
-
188
+
189
+ begin
190
+ require 'arel/visitors/bind_visitor'
191
+ rescue LoadError # AR-3.0
192
+ def self.bind_substitution(visitor); visitor; end
193
+ end
194
+
195
+ def bind_substitution(visitor); self.class.bind_substitution(visitor); end
196
+ private :bind_substitution
197
+
198
+ # @override default implementation (does nothing silently)
199
+ def structure_dump
200
+ raise NotImplementedError, "structure_dump not supported"
201
+ end
202
+
135
203
  def is_a?(klass) # :nodoc:
136
204
  # This is to fake out current_adapter? conditional logic in AR tests
137
205
  if Class === klass && klass.name =~ /#{adapter_name}Adapter$/i
@@ -145,11 +213,11 @@ module ActiveRecord
145
213
  true
146
214
  end
147
215
 
148
- def native_database_types #:nodoc:
216
+ def native_database_types # :nodoc:
149
217
  @connection.native_database_types
150
218
  end
151
219
 
152
- def database_name #:nodoc:
220
+ def database_name # :nodoc:
153
221
  @connection.database_name
154
222
  end
155
223
 
@@ -193,8 +261,7 @@ module ActiveRecord
193
261
  end
194
262
 
195
263
  def reconnect!
196
- @connection.reconnect!
197
- configure_connection if respond_to?(:configure_connection)
264
+ @connection.reconnect! # handles adapter.configure_connection
198
265
  @connection
199
266
  end
200
267
 
@@ -207,61 +274,71 @@ module ActiveRecord
207
274
  def jdbc_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) # :nodoc:
208
275
  insert_sql(sql, name, pk, id_value, sequence_name, binds)
209
276
  end
277
+ alias_chained_method :insert, :query_dirty, :jdbc_insert
210
278
 
211
279
  def jdbc_update(sql, name = nil, binds = []) # :nodoc:
212
280
  execute(sql, name, binds)
213
281
  end
282
+ alias_chained_method :update, :query_dirty, :jdbc_update
214
283
 
215
284
  def jdbc_select_all(sql, name = nil, binds = []) # :nodoc:
216
285
  select(sql, name, binds)
217
286
  end
218
-
219
- # Allow query caching to work even when we override alias_method_chain'd methods
220
287
  alias_chained_method :select_all, :query_cache, :jdbc_select_all
221
- alias_chained_method :update, :query_dirty, :jdbc_update
222
- alias_chained_method :insert, :query_dirty, :jdbc_insert
223
288
 
224
289
  end
225
-
226
- def jdbc_columns(table_name, name = nil)
290
+
291
+ def columns(table_name, name = nil)
227
292
  @connection.columns(table_name.to_s)
228
293
  end
229
- alias_chained_method :columns, :query_cache, :jdbc_columns
230
294
 
231
295
  # Executes +sql+ statement in the context of this connection using
232
296
  # +binds+ as the bind substitutes. +name+ is logged along with
233
297
  # the executed +sql+ statement.
234
298
  def exec_query(sql, name = 'SQL', binds = []) # :nodoc:
235
- log(sql, name || 'SQL') { @connection.execute_query(to_sql(sql, binds)) }
299
+ sql = to_sql(sql, binds)
300
+ if prepared_statements?
301
+ log(sql, name, binds) { @connection.execute_query(sql, binds) }
302
+ else
303
+ sql = suble_binds(sql, binds)
304
+ log(sql, name) { @connection.execute_query(sql) }
305
+ end
236
306
  end
237
307
 
238
308
  # Executes insert +sql+ statement in the context of this connection using
239
309
  # +binds+ as the bind substitutes. +name+ is the logged along with
240
310
  # the executed +sql+ statement.
241
311
  def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) # :nodoc:
242
- log(sql, name || 'SQL') { @connection.execute_insert(to_sql(sql, binds)) }
312
+ sql = suble_binds to_sql(sql, binds), binds
313
+ log(sql, name || 'SQL') { @connection.execute_insert(sql) }
243
314
  end
244
315
 
245
316
  # Executes delete +sql+ statement in the context of this connection using
246
317
  # +binds+ as the bind substitutes. +name+ is the logged along with
247
318
  # the executed +sql+ statement.
248
319
  def exec_delete(sql, name, binds) # :nodoc:
249
- log(sql, name || 'SQL') { @connection.execute_delete(to_sql(sql, binds)) }
320
+ sql = suble_binds to_sql(sql, binds), binds
321
+ log(sql, name || 'SQL') { @connection.execute_delete(sql) }
250
322
  end
251
323
 
252
324
  # Executes update +sql+ statement in the context of this connection using
253
325
  # +binds+ as the bind substitutes. +name+ is the logged along with
254
326
  # the executed +sql+ statement.
255
327
  def exec_update(sql, name, binds) # :nodoc:
256
- log(sql, name || 'SQL') { @connection.execute_update(to_sql(sql, binds)) }
328
+ sql = suble_binds to_sql(sql, binds), binds
329
+ log(sql, name || 'SQL') { @connection.execute_update(sql) }
257
330
  end
258
331
 
259
332
  # Similar to {#exec_query} except it returns "raw" results in an array
260
333
  # where each rows is a hash with keys as columns (just like Rails used to
261
334
  # do up until 3.0) instead of wrapping them in a {#ActiveRecord::Result}.
262
335
  def exec_query_raw(sql, name = 'SQL', binds = [], &block) # :nodoc:
263
- log(sql, name || 'SQL') do
264
- @connection.execute_query_raw(to_sql(sql, binds), &block)
336
+ sql = to_sql(sql, binds)
337
+ if prepared_statements?
338
+ log(sql, name, binds) { @connection.execute_query_raw(sql, binds, &block) }
339
+ else
340
+ sql = suble_binds(sql, binds)
341
+ log(sql, name) { @connection.execute_query_raw(sql, &block) }
265
342
  end
266
343
  end
267
344
 
@@ -289,7 +366,7 @@ module ActiveRecord
289
366
 
290
367
  # Executes the SQL statement in the context of this connection.
291
368
  def execute(sql, name = nil, binds = [])
292
- sql = to_sql(sql, binds)
369
+ sql = suble_binds to_sql(sql, binds), binds
293
370
  if name == :skip_logging
294
371
  _execute(sql, name)
295
372
  else
@@ -304,7 +381,7 @@ module ActiveRecord
304
381
 
305
382
  # Executes the SQL statement in the context of this connection.
306
383
  def execute(sql, name = nil, binds = [])
307
- sql = to_sql(sql, binds)
384
+ sql = suble_binds to_sql(sql, binds), binds
308
385
  if name == :skip_logging
309
386
  _execute(sql, name)
310
387
  else
@@ -386,37 +463,28 @@ module ActiveRecord
386
463
  if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
387
464
 
388
465
  #attr_reader :visitor unless method_defined?(:visitor) # not in 3.0
389
-
466
+
390
467
  # Converts an AREL AST to SQL.
391
468
  def to_sql(arel, binds = [])
392
- # NOTE: can not handle `visitor.accept(arel.ast)` right thus
393
- # convert AREL to a SQL string and simply substitute binds :
394
- sql = arel.respond_to?(:to_sql) ? arel.send(:to_sql) : arel
395
- return sql if binds.blank?
396
- sql.gsub('?') { quote(*binds.shift.reverse) }
469
+ # NOTE: can not handle `visitor.accept(arel.ast)` right
470
+ arel.respond_to?(:to_sql) ? arel.send(:to_sql) : arel
397
471
  end
398
472
 
399
473
  elsif ActiveRecord::VERSION::MAJOR >= 3 # AR >= 3.1 or 4.0
400
-
474
+
401
475
  # Converts an AREL AST to SQL.
402
476
  def to_sql(arel, binds = [])
403
477
  if arel.respond_to?(:ast)
404
478
  visitor.accept(arel.ast) { quote(*binds.shift.reverse) }
405
- else # for backwards compatibility :
406
- sql = arel.respond_to?(:to_sql) ? arel.send(:to_sql) : arel
407
- return sql if binds.blank?
408
- sql.gsub('?') { quote(*binds.shift.reverse) }
479
+ else
480
+ arel
409
481
  end
410
482
  end
411
483
 
412
484
  else # AR-2.3 no #to_sql method
413
485
 
414
- # Substitutes SQL bind parameters.
415
- def to_sql(sql, binds = [])
416
- sql = sql.send(:to_sql) if sql.respond_to?(:to_sql)
417
- return sql if binds.blank?
418
- copy = binds.dup
419
- sql.gsub('?') { quote(*copy.shift.reverse) }
486
+ def to_sql(sql, binds = nil)
487
+ sql
420
488
  end
421
489
 
422
490
  end
@@ -467,18 +535,28 @@ module ActiveRecord
467
535
 
468
536
  private
469
537
 
470
- # #deprecated no longer used
471
- def substitute_binds(sql, binds = [])
472
- sql = extract_sql(sql)
473
- if binds.empty?
474
- sql
475
- else
476
- copy = binds.dup
477
- sql.gsub('?') { quote(*copy.shift.reverse) }
478
- end
538
+ def prepared_statements?
539
+ self.class.prepared_statements?(config)
540
+ end
541
+
542
+ def self.prepared_statements?(config)
543
+ config.key?(:prepared_statements) ?
544
+ type_cast_config_to_boolean(config.fetch(:prepared_statements)) :
545
+ false # NOTE: off by default for now
546
+ end
547
+
548
+ def suble_binds(sql, binds)
549
+ return sql if binds.nil? || binds.empty?
550
+ copy = binds.dup
551
+ sql.gsub('?') { quote(*copy.shift.reverse) }
552
+ end
553
+
554
+ # @deprecated replaced with {#suble_binds}
555
+ def substitute_binds(sql, binds)
556
+ suble_binds(extract_sql(sql), binds)
479
557
  end
480
558
 
481
- # #deprecated no longer used
559
+ # @deprecated no longer used
482
560
  def extract_sql(obj)
483
561
  obj.respond_to?(:to_sql) ? obj.send(:to_sql) : obj
484
562
  end