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

Sign up to get free protection for your applications and to get access to all the features.
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