activerecord-jdbc-alt-adapter 72.0.0.rc3-java → 80.0.0.rc1-java

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +23 -16
  3. data/.github/workflows/ruby.yml +123 -18
  4. data/.mise.toml +3 -0
  5. data/Gemfile +43 -48
  6. data/README.md +3 -2
  7. data/RUNNING_TESTS.md +9 -1
  8. data/Rakefile +63 -15
  9. data/activerecord-jdbc-adapter.gemspec +2 -2
  10. data/activerecord-jdbc-alt-adapter.gemspec +1 -1
  11. data/lib/arjdbc/abstract/core.rb +0 -17
  12. data/lib/arjdbc/abstract/database_statements.rb +23 -19
  13. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  14. data/lib/arjdbc/mssql/adapter.rb +11 -11
  15. data/lib/arjdbc/mssql/database_statements.rb +32 -29
  16. data/lib/arjdbc/mssql/explain_support.rb +6 -1
  17. data/lib/arjdbc/mssql/quoting.rb +9 -0
  18. data/lib/arjdbc/mssql.rb +1 -1
  19. data/lib/arjdbc/postgresql/adapter.rb +4 -1
  20. data/lib/arjdbc/postgresql/database_statements.rb +45 -0
  21. data/lib/arjdbc/postgresql/schema_statements.rb +17 -0
  22. data/lib/arjdbc/sqlite3/adapter.rb +51 -7
  23. data/lib/arjdbc/sqlite3/database_statements.rb +26 -0
  24. data/lib/arjdbc/version.rb +1 -1
  25. data/pom.xml +3 -3
  26. data/rakelib/02-test.rake +1 -1
  27. data/rakelib/db.rake +5 -3
  28. data/rakelib/rails.rake +0 -1
  29. data/src/java/arjdbc/ArJdbcModule.java +13 -10
  30. data/src/java/arjdbc/db2/DB2Module.java +4 -4
  31. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +8 -9
  32. data/src/java/arjdbc/h2/H2Module.java +2 -1
  33. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +8 -7
  34. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +5 -6
  35. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +4 -3
  36. data/src/java/arjdbc/jdbc/DriverWrapper.java +13 -8
  37. data/src/java/arjdbc/jdbc/JdbcResult.java +10 -7
  38. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +176 -155
  39. data/src/java/arjdbc/mssql/MSSQLModule.java +3 -3
  40. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +15 -9
  41. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  42. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +25 -23
  43. data/src/java/arjdbc/oracle/OracleModule.java +2 -3
  44. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +22 -18
  45. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +2 -3
  46. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +24 -12
  47. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +25 -12
  48. data/src/java/arjdbc/sqlite3/SQLite3Module.java +2 -3
  49. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +78 -29
  50. data/src/java/arjdbc/util/DateTimeUtils.java +7 -5
  51. data/src/java/arjdbc/util/ObjectSupport.java +2 -2
  52. data/src/java/arjdbc/util/StringHelper.java +1 -1
  53. metadata +7 -6
  54. data/.travis.yml +0 -128
data/Rakefile CHANGED
@@ -5,6 +5,21 @@
5
5
  #
6
6
  # Environment variables used by this Rakefile:
7
7
  #
8
+ # DBS
9
+ # Limits the command performed to only work for one of the database
10
+ # types listed in this env var. You can set to a combination of mysql,
11
+ # postgres, or sqlite, separated by commas. For example:
12
+ #
13
+ # mysql,postgres,sqlite
14
+ #
15
+ # You may use pg or postgres as aliases for postgresql
16
+ # You may use sqlite3 as an alias for sqlite
17
+ # You may use all to mean all three
18
+ #
19
+ # NOTE: if you ever want to add a new type of database to be released,
20
+ # just fix up this documentation, the error string in invalid_dbs,
21
+ # and add it to the ALL_DBS array. Everything else should just work!
22
+ #
8
23
  # INCLUDE_JAR_IN_GEM [default task - false, other taks - true]:
9
24
  # Note: This is something you should not normally have to set.
10
25
  # For local development we always will end up including the jar file
@@ -85,6 +100,12 @@ end
85
100
 
86
101
  desc "Releasing AR-JDBC gems (use NOOP=true to disable gem pushing)"
87
102
  task 'release:do' do
103
+ unless ENV["DBS"]
104
+ puts "you must explicitly provide a DBS env var when calling release:do. An empty one will not default to 'all' " \
105
+ "for this command\n\n"
106
+ invalid_dbs!
107
+ end
108
+
88
109
  Rake::Task['build'].invoke
89
110
  # Rake::Task['build:adapters'].invoke
90
111
 
@@ -107,8 +128,41 @@ task 'release:push' do
107
128
  sh "for gem in `ls pkg/*-#{current_version.call}-java.gem`; do gem push $gem; done"
108
129
  end
109
130
 
110
- ADAPTERS = %w[mysql postgresql sqlite3].map { |a| "activerecord-jdbc#{a}-adapter" }
111
- DRIVERS = %w[mysql postgres sqlite3].map { |a| "jdbc-#{a}" }
131
+ ALL_DBS = ["mysql", "postgresql", "sqlite3"] #NOTE: if we add a new database type to be released, just add it here!
132
+ DB_ALIASES = ALL_DBS.map {|db| [db, db]}.to_h.merge({
133
+ "pg" => "postgresql",
134
+ "postgres" => "postgresql",
135
+ "sqlite" => "sqlite3"
136
+ })
137
+
138
+ def invalid_dbs!
139
+ raise ArgumentError, "Invalid DBS env var\nThe DBS env var must be set to a combination of mysql, postgres, or " \
140
+ "sqlite, separated by commas. For example:\n\nmysql,postgres,sqlite\n\nYou may use pg or " \
141
+ "postgres as aliases for postgresql\nYou may use sqlite3 as an alias for sqlite\n" \
142
+ "You may use all as a shortcut to listing them all out"
143
+ end
144
+
145
+ def make_db_list
146
+ env_dbs = ENV["DBS"]
147
+ return ALL_DBS if !env_dbs || env_dbs == "all"
148
+ requested = env_dbs.split(",").map(&:strip).reject(&:empty?).map(&:downcase)
149
+ invalid_dbs! unless requested.size > 0 && requested == requested.uniq
150
+
151
+ canonical = requested.map do |name|
152
+ DB_ALIASES.fetch(name) { invalid_dbs! }
153
+ end
154
+
155
+ invalid_dbs! unless canonical == canonical.uniq
156
+
157
+ canonical
158
+ end
159
+
160
+ db_list = make_db_list
161
+ ADAPTERS = db_list.map { |db| "activerecord-jdbc#{db}-adapter" }
162
+
163
+ db_list.map! {|db| db == 'postgresql' ? 'postgres' : db } #naming convention for DRIVERS
164
+ DRIVERS = db_list.map { |a| "jdbc-#{a}" }
165
+
112
166
  TARGETS = ( ADAPTERS + DRIVERS )
113
167
 
114
168
  ADAPTERS.each do |target|
@@ -185,7 +239,7 @@ if defined? JRUBY_VERSION
185
239
  #directory classes = 'pkg/classes'; CLEAN << classes
186
240
 
187
241
  file jar_file => FileList[ 'src/java/**/*.java' ] do
188
- source = target = '1.8'; debug = true
242
+ source = target = '21'; debug = true
189
243
 
190
244
  get_driver_jars_local = lambda do |*args|
191
245
  driver_deps = args.empty? ? [ :Postgres, :MySQL ] : args
@@ -230,7 +284,7 @@ if defined? JRUBY_VERSION
230
284
  if match = requirement.match(/^jar\s+([\w\-\.]+):([\w\-]+),\s+?([\w\.\-]+)?/)
231
285
  matched_jar = Jars.send :to_jar, match[1], match[2], match[3], nil
232
286
  matched_jar = File.join( Jars.home, matched_jar )
233
- matched_jars << matched_jar if File.exists?( matched_jar )
287
+ matched_jars << matched_jar if File.exist?( matched_jar )
234
288
  end
235
289
  end
236
290
  matched_jars
@@ -262,18 +316,12 @@ if defined? JRUBY_VERSION
262
316
  end
263
317
 
264
318
  classpath = []
265
- [ 'java.class.path', 'sun.boot.class.path' ].map { |key| ENV_JAVA[key] }.each do |v|
266
- classpath += v.split(File::PATH_SEPARATOR).find_all { |jar| jar =~ /jruby/i } if v
267
- end
268
- # Using Java 9+. Let's try and infer jruby.jar location from rbconfig
269
- if classpath.empty?; require 'rbconfig'
270
- libdir = RbConfig::CONFIG['libdir']
271
- if libdir.start_with? 'classpath:'
272
- error "Cannot build activerecord-jdbc with jruby-complete"
273
- end
274
- classpath << File.join(libdir, 'jruby.jar')
319
+ require 'rbconfig'
320
+ libdir = RbConfig::CONFIG['libdir']
321
+ if libdir.start_with? 'classpath:'
322
+ error "Cannot build activerecord-jdbc with jruby-complete"
275
323
  end
276
-
324
+ classpath << File.join(libdir, 'jruby.jar')
277
325
  classpath += driver_jars
278
326
  classpath = classpath.compact.join(File::PATH_SEPARATOR)
279
327
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.homepage = 'https://github.com/jruby/activerecord-jdbc-adapter'
11
11
  gem.license = 'BSD-2-Clause'
12
12
  gem.summary = 'JDBC adapter for ActiveRecord, for use within JRuby on Rails.'
13
- gem.description = "" <<
13
+ gem.description = +"" <<
14
14
  "AR-JDBC is a database adapter for Rails' ActiveRecord component " <<
15
15
  "designed to be used with JRuby built upon Java's JDBC API for " <<
16
16
  "database access. Provides (ActiveRecord) built-in adapters: MySQL, " <<
@@ -41,7 +41,7 @@ Gem::Specification.new do |gem|
41
41
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
42
42
  gem.test_files = gem.files.grep(%r{^test/})
43
43
 
44
- gem.add_dependency "activerecord", "~> 7.2.2"
44
+ gem.add_dependency "activerecord", "~> 8.0"
45
45
 
46
46
  #gem.add_development_dependency 'test-unit', '2.5.4'
47
47
  #gem.add_development_dependency 'test-unit-context', '>= 0.3.0'
@@ -43,7 +43,7 @@ Gem::Specification.new do |gem|
43
43
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
44
44
  gem.test_files = gem.files.grep(%r{^test/})
45
45
 
46
- gem.add_dependency "activerecord", "~> 7.2.2"
46
+ gem.add_dependency "activerecord", "~> 8.0.0"
47
47
 
48
48
  #gem.add_development_dependency 'test-unit', '2.5.4'
49
49
  #gem.add_development_dependency 'test-unit-context', '>= 0.3.0'
@@ -56,23 +56,6 @@ module ArJdbc
56
56
  else super
57
57
  end
58
58
  end
59
-
60
- # this version of log() automatically fills type_casted_binds from binds if necessary
61
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false)
62
- if binds.any? && (type_casted_binds.nil? || type_casted_binds.empty?)
63
- type_casted_binds = lambda {
64
- # extract_raw_bind_values
65
- binds.map do |bind|
66
- if bind.respond_to?(:value_for_database)
67
- bind.value_for_database
68
- else
69
- bind
70
- end
71
- end
72
- }
73
- end
74
- super
75
- end
76
59
  end
77
60
  end
78
61
 
@@ -10,8 +10,6 @@ module ArJdbc
10
10
  NO_BINDS = [].freeze
11
11
 
12
12
  def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil, returning: nil)
13
- sql = transform_query(sql)
14
-
15
13
  if preventing_writes?
16
14
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
17
15
  end
@@ -20,11 +18,15 @@ module ArJdbc
20
18
 
21
19
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
22
20
 
21
+ type_casted_binds = type_casted_binds(binds)
22
+
23
23
  with_raw_connection do |conn|
24
24
  if without_prepared_statement?(binds)
25
25
  log(sql, name) { conn.execute_insert_pk(sql, pk) }
26
26
  else
27
- log(sql, name, binds) do
27
+ log(sql, name, binds, type_casted_binds) do
28
+ # TODO: the ideas is to pass type casted binds but dozens of tests fail
29
+ # conn.execute_insert_pk(sql, type_casted_binds, pk)
28
30
  conn.execute_insert_pk(sql, binds, pk)
29
31
  end
30
32
  end
@@ -34,8 +36,6 @@ module ArJdbc
34
36
  # It appears that at this point (AR 5.0) "prepare" should only ever be true
35
37
  # if prepared statements are enabled
36
38
  def internal_exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
37
- sql = transform_query(sql)
38
-
39
39
  if preventing_writes? && write_query?(sql)
40
40
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
41
41
  end
@@ -44,8 +44,9 @@ module ArJdbc
44
44
 
45
45
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
46
46
 
47
- # puts "internal----->sql: #{sql}, binds: #{binds}"
47
+ # puts "[1]internal----->sql: #{sql}, binds: #{binds}"
48
48
  type_casted_binds = type_casted_binds(binds)
49
+ # puts "[2]internal----->sql: #{type_casted_binds.size}, binds: #{type_casted_binds}"
49
50
 
50
51
  with_raw_connection do |conn|
51
52
  if without_prepared_statement?(binds)
@@ -54,15 +55,13 @@ module ArJdbc
54
55
  log(sql, name, type_casted_binds, async: async) do
55
56
  # this is different from normal AR that always caches
56
57
  cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
57
- conn.execute_prepared_query(sql, type_casted_binds, cached_statement)
58
+ conn.execute_prepared_query(sql, binds, cached_statement)
58
59
  end
59
60
  end
60
61
  end
61
62
  end
62
63
 
63
64
  def exec_update(sql, name = 'SQL', binds = NO_BINDS)
64
- sql = transform_query(sql)
65
-
66
65
  if preventing_writes?
67
66
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
68
67
  end
@@ -71,11 +70,17 @@ module ArJdbc
71
70
 
72
71
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
73
72
 
73
+ type_casted_binds = type_casted_binds(binds)
74
+
74
75
  with_raw_connection do |conn|
75
76
  if without_prepared_statement?(binds)
76
77
  log(sql, name) { conn.execute_update(sql) }
77
78
  else
78
- log(sql, name, binds) { conn.execute_prepared_update(sql, binds) }
79
+ log(sql, name, binds, type_casted_binds) do
80
+ # TODO: the ideas is to pass type casted binds but some tests fail
81
+ # conn.execute_prepared_update(sql, type_casted_binds)
82
+ conn.execute_prepared_update(sql, binds)
83
+ end
79
84
  end
80
85
  end
81
86
  end
@@ -89,22 +94,21 @@ module ArJdbc
89
94
 
90
95
  private
91
96
 
97
+ def without_prepared_statement?(binds)
98
+ !prepared_statements || binds.empty?
99
+ end
100
+
92
101
  def convert_legacy_binds_to_attributes(binds)
93
102
  binds.map do |column, value|
94
103
  ActiveRecord::Relation::QueryAttribute.new(nil, type_cast(value, column), ActiveModel::Type::Value.new)
95
104
  end
96
105
  end
97
106
 
98
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
99
- log(sql, name, async: async) do
100
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
101
- result = conn.execute(sql)
102
- verified!
103
- result
104
- end
105
- end
107
+ def preprocess_query(sql)
108
+ check_if_write_query(sql) if respond_to?(:check_if_write_query, true)
109
+ mark_transaction_written_if_write(sql) if respond_to?(:mark_transaction_written_if_write, true)
110
+ sql
106
111
  end
107
-
108
112
  end
109
113
  end
110
114
  end
Binary file
@@ -479,31 +479,31 @@ module ActiveRecord
479
479
  def translate_exception(exception, message:, sql:, binds:)
480
480
  case message
481
481
  when /no connection available/i
482
- ConnectionNotEstablished.new(exception)
482
+ ConnectionNotEstablished.new(message, connection_pool: @pool)
483
483
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
484
- RecordNotUnique.new(message, sql: sql, binds: binds)
484
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
485
485
  when /Violation of PRIMARY KEY constraint .* Cannot insert duplicate key in object .* The duplicate key value is/i
486
- RecordNotUnique.new(message, sql: sql, binds: binds)
486
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
487
487
  when /Lock request time out period exceeded/i
488
488
  LockTimeout.new(message, sql: sql, binds: binds)
489
489
  when /The .* statement conflicted with the FOREIGN KEY constraint/
490
- InvalidForeignKey.new(message, sql: sql, binds: binds)
490
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
491
491
  when /Could not drop object .* because it is referenced by a FOREIGN KEY constraint/
492
- StatementInvalid.new(message, sql: sql, binds: binds)
492
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
493
493
  when /The .* statement conflicted with the REFERENCE constraint/
494
- InvalidForeignKey.new(message, sql: sql, binds: binds)
494
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
495
495
  when /(String or binary data would be truncated)/i
496
- ValueTooLong.new(message, sql: sql, binds: binds)
496
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
497
497
  when /Cannot insert the value NULL into column .* does not allow nulls/
498
- NotNullViolation.new(message, sql: sql, binds: binds)
498
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
499
499
  when /Arithmetic overflow error converting expression/
500
500
  RangeError.new(message, sql: sql, binds: binds)
501
501
  when /Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation/
502
- StatementInvalid.new(message, sql: sql, binds: binds)
502
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
503
503
  when /Incorrect syntax near the keyword .*/
504
- StatementInvalid.new(message, sql: sql, binds: binds)
504
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
505
505
  when /Could not find stored procedure .*/
506
- StatementInvalid.new(message, sql: sql, binds: binds)
506
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
507
507
  else
508
508
  super
509
509
  end
@@ -93,18 +93,15 @@ module ActiveRecord
93
93
  end
94
94
 
95
95
  def internal_exec_query(sql, name = 'SQL', binds = [], prepare: false, async: false, allow_retry: false)
96
- sql = transform_query(sql)
97
-
98
- check_if_write_query(sql)
99
-
100
- mark_transaction_written_if_write(sql)
96
+ sql = preprocess_query(sql)
101
97
 
102
98
  # binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
103
99
 
104
- # puts "internal----->sql: #{sql}, binds: #{binds}"
100
+ # puts "[1]internal----->sql: #{sql}, binds: #{binds}"
105
101
  type_casted_binds = type_casted_binds(binds)
102
+ # puts "[2]internal----->sql: #{type_casted_binds.size}, binds: #{type_casted_binds}"
106
103
 
107
- if without_prepared_statement?(binds)
104
+ if binds.nil? || binds.empty?
108
105
  log(sql, name) do
109
106
  with_raw_connection do |conn|
110
107
  result = conditional_indentity_insert(sql) do
@@ -131,14 +128,12 @@ module ActiveRecord
131
128
  end
132
129
 
133
130
  def exec_update(sql, name = nil, binds = [])
134
- sql = transform_query(sql)
131
+ sql = preprocess_query(sql)
135
132
 
136
- check_if_write_query(sql)
137
-
138
- mark_transaction_written_if_write(sql)
133
+ type_casted_binds = type_casted_binds(binds)
139
134
 
140
135
  # puts "exec_update----->sql: #{sql}, binds: #{binds}"
141
- if without_prepared_statement?(binds)
136
+ if binds.nil? || binds.empty?
142
137
  log(sql, name) do
143
138
  with_raw_connection do |conn|
144
139
  result = conn.execute_update(sql)
@@ -147,9 +142,9 @@ module ActiveRecord
147
142
  end
148
143
  end
149
144
  else
150
- log(sql, name, binds) do
145
+ log(sql, name, binds, type_casted_binds) do
151
146
  with_raw_connection do |conn|
152
- result = conn.execute_prepared_update(sql, binds)
147
+ result = conn.execute_prepared_update(sql, type_casted_binds)
153
148
  verified!
154
149
  result
155
150
  end
@@ -159,14 +154,12 @@ module ActiveRecord
159
154
  alias :exec_delete :exec_update
160
155
 
161
156
  def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
162
- sql = transform_query(sql)
157
+ sql = preprocess_query(sql)
163
158
 
164
- check_if_write_query(sql)
165
-
166
- mark_transaction_written_if_write(sql)
159
+ type_casted_binds = type_casted_binds(binds)
167
160
 
168
161
  # puts "exec_insert----->sql: #{sql}, binds: #{binds}"
169
- if without_prepared_statement?(binds)
162
+ if binds.nil? || binds.empty?
170
163
  log(sql, name) do
171
164
  with_raw_connection do |conn|
172
165
  result = conditional_indentity_insert(sql) do
@@ -177,12 +170,14 @@ module ActiveRecord
177
170
  end
178
171
  end
179
172
  else
180
- log(sql, name, binds) do
173
+ log(sql, name, binds, type_casted_binds) do
181
174
  with_raw_connection do |conn|
182
175
  result = conditional_indentity_insert(sql) do
183
176
  # DEPRECATION WARNING: to_time will always preserve the timezone offset of the receiver in Rails 8.0.
184
177
  # To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`.
185
178
  # (called from block in execute_insert_pk
179
+ # TODO: the ideas is to pass type casted binds but causes strange test failures
180
+ # conn.execute_insert_pk(sql, type_casted_binds, pk)
186
181
  conn.execute_insert_pk(sql, binds, pk)
187
182
  end
188
183
  verified!
@@ -194,15 +189,23 @@ module ActiveRecord
194
189
 
195
190
  private
196
191
 
197
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
198
- # puts "raw_execute----->sql: #{sql}"
199
- log(sql, name, async: async) do
200
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
201
- result = conditional_indentity_insert(sql) { conn.execute(sql) }
202
- verified!
203
- result
204
- end
205
- end
192
+ def cast_result(raw_result)
193
+ return ActiveRecord::Result.empty if raw_result.nil?
194
+
195
+ # nothing to cast here. the java part returns an AR Result
196
+ raw_result
197
+ end
198
+
199
+
200
+ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
201
+ result = conditional_indentity_insert(sql) { raw_connection.execute(sql) }
202
+
203
+ count = 0
204
+ count = result.count if result.respond_to?(:count)
205
+
206
+ verified!
207
+ notification_payload[:row_count] = count
208
+ result
206
209
  end
207
210
 
208
211
  def sql_for_insert(sql, pk, binds, returning) # :nodoc:
@@ -50,7 +50,12 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  binds.each do |bind|
53
- value = quote(bind.value_for_database)
53
+ value = if bind.is_a?(::ActiveModel::Attribute)
54
+ quote(bind.value_for_database)
55
+ else
56
+ quote(bind)
57
+ end
58
+
54
59
  sql = sql.sub('?', value)
55
60
  end
56
61
 
@@ -60,6 +60,15 @@ module ActiveRecord
60
60
  end
61
61
  end
62
62
 
63
+ def type_cast(value) # :nodoc:
64
+ case value
65
+ when BigDecimal
66
+ value
67
+ else
68
+ super
69
+ end
70
+ end
71
+
63
72
  QUOTED_TRUE = '1'
64
73
  QUOTED_FALSE = '0'
65
74
 
data/lib/arjdbc/mssql.rb CHANGED
@@ -6,4 +6,4 @@ module ArJdbc
6
6
  MsSQL = MSSQL # compatibility with 1.2
7
7
  end
8
8
 
9
- ArJdbc.warn_unsupported_adapter "mssql", [7, 2] # warns on AR >= 4.2
9
+ ArJdbc.warn_unsupported_adapter "mssql", [8, 0] # warns on AR >= 4.2
@@ -219,6 +219,10 @@ module ArJdbc
219
219
  true
220
220
  end
221
221
 
222
+ def supports_native_partitioning?
223
+ database_version >= 100_000
224
+ end
225
+
222
226
  def supports_insert_returning?
223
227
  true
224
228
  end
@@ -953,7 +957,6 @@ module ActiveRecord::ConnectionAdapters
953
957
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
954
958
 
955
959
  def execute_and_clear(sql, name, binds, prepare: false, async: false)
956
- sql = transform_query(sql)
957
960
  check_if_write_query(sql)
958
961
 
959
962
  if !prepare || without_prepared_statement?(binds)
@@ -15,6 +15,51 @@ module ArJdbc
15
15
 
16
16
  "EXPLAIN (#{options.join(", ").upcase})"
17
17
  end
18
+
19
+ private
20
+
21
+ def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true)
22
+ sql = preprocess_query(sql)
23
+
24
+ # puts "[1]internal----->sql: #{sql}, binds: #{binds}"
25
+ type_casted_binds = type_casted_binds(binds)
26
+ # puts "[2]internal----->sql: #{type_casted_binds.size}, binds: #{type_casted_binds}"
27
+
28
+ with_raw_connection do |conn|
29
+ if without_prepared_statement?(binds)
30
+ log(sql, name, async: async) { conn.execute_query(sql) }
31
+ else
32
+ log(sql, name, type_casted_binds, async: async) do
33
+ # this is different from normal AR that always caches
34
+ cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
35
+ conn.execute_prepared_query(sql, type_casted_binds, cached_statement)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
42
+ result = raw_connection.execute(sql)
43
+
44
+ count = 0
45
+ count = result.count if result.respond_to?(:count)
46
+
47
+ verified!
48
+ notification_payload[:row_count] = count
49
+ result
50
+ end
51
+
52
+ def cast_result(raw_result)
53
+ return ActiveRecord::Result.empty if raw_result.nil?
54
+
55
+ fields = raw_result.fields
56
+
57
+ if fields.empty?
58
+ ActiveRecord::Result.empty
59
+ else
60
+ ActiveRecord::Result.new(fields, raw_result.values)
61
+ end
62
+ end
18
63
  end
19
64
  end
20
65
  end
@@ -6,6 +6,23 @@ module ArJdbc
6
6
  ForeignKeyDefinition = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
7
7
  Utils = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
8
8
 
9
+ def decode_string_array(value)
10
+ return value if value.is_a?(Array)
11
+ _arjdbc_array_parser.parse_pg_array(value)
12
+ end
13
+
14
+ private
15
+
16
+ def _arjdbc_array_parser
17
+ @_arjdbc_array_parser ||= begin
18
+ obj = Object.new
19
+ obj.extend(ActiveRecord::ConnectionAdapters::PostgreSQL::ArrayParser)
20
+ obj
21
+ end
22
+ end
23
+
24
+ public
25
+
9
26
  def foreign_keys(table_name)
10
27
  scope = quoted_scope(table_name)
11
28
  fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)