activerecord-jdbc-alt-adapter 72.1.0-java → 80.1.0.beta1-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 (61) 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 +19 -38
  13. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  14. data/lib/arjdbc/mssql/adapter.rb +12 -11
  15. data/lib/arjdbc/mssql/column.rb +2 -2
  16. data/lib/arjdbc/mssql/database_statements.rb +31 -29
  17. data/lib/arjdbc/mssql/explain_support.rb +6 -1
  18. data/lib/arjdbc/mssql/schema_creation.rb +1 -1
  19. data/lib/arjdbc/mssql/schema_statements.rb +4 -0
  20. data/lib/arjdbc/mssql/type_metadata.rb +36 -0
  21. data/lib/arjdbc/mssql/types/binary_types.rb +5 -4
  22. data/lib/arjdbc/mssql/types/deprecated_types.rb +15 -12
  23. data/lib/arjdbc/mssql/types/string_types.rb +10 -8
  24. data/lib/arjdbc/mssql.rb +1 -1
  25. data/lib/arjdbc/postgresql/adapter.rb +4 -5
  26. data/lib/arjdbc/postgresql/database_statements.rb +45 -0
  27. data/lib/arjdbc/postgresql/schema_statements.rb +24 -0
  28. data/lib/arjdbc/sqlite3/adapter.rb +51 -7
  29. data/lib/arjdbc/sqlite3/database_statements.rb +26 -0
  30. data/lib/arjdbc/tasks/mssql_database_tasks.rb +1 -21
  31. data/lib/arjdbc/version.rb +1 -1
  32. data/pom.xml +3 -3
  33. data/rakelib/02-test.rake +1 -1
  34. data/rakelib/db.rake +5 -3
  35. data/rakelib/rails.rake +0 -1
  36. data/src/java/arjdbc/ArJdbcModule.java +13 -10
  37. data/src/java/arjdbc/db2/DB2Module.java +4 -4
  38. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +8 -9
  39. data/src/java/arjdbc/h2/H2Module.java +2 -1
  40. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +8 -7
  41. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +5 -6
  42. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +4 -3
  43. data/src/java/arjdbc/jdbc/DriverWrapper.java +13 -8
  44. data/src/java/arjdbc/jdbc/JdbcResult.java +10 -7
  45. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +171 -155
  46. data/src/java/arjdbc/mssql/MSSQLModule.java +3 -3
  47. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +13 -9
  48. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  49. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +25 -23
  50. data/src/java/arjdbc/oracle/OracleModule.java +2 -3
  51. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +22 -18
  52. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +2 -3
  53. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +24 -12
  54. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +16 -12
  55. data/src/java/arjdbc/sqlite3/SQLite3Module.java +2 -3
  56. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +78 -29
  57. data/src/java/arjdbc/util/DateTimeUtils.java +7 -5
  58. data/src/java/arjdbc/util/ObjectSupport.java +2 -2
  59. data/src/java/arjdbc/util/StringHelper.java +1 -1
  60. metadata +8 -6
  61. 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.1.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.1.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,21 +10,17 @@ 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)
13
+ sql = preprocess_query(sql)
14
14
 
15
- if preventing_writes?
16
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
17
- end
18
-
19
- mark_transaction_written_if_write(sql)
20
-
21
- binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
15
+ type_casted_binds = type_casted_binds(binds)
22
16
 
23
17
  with_raw_connection do |conn|
24
18
  if without_prepared_statement?(binds)
25
19
  log(sql, name) { conn.execute_insert_pk(sql, pk) }
26
20
  else
27
- log(sql, name, binds) do
21
+ log(sql, name, binds, type_casted_binds) do
22
+ # TODO: the ideas is to pass type casted binds but dozens of tests fail
23
+ # conn.execute_insert_pk(sql, type_casted_binds, pk)
28
24
  conn.execute_insert_pk(sql, binds, pk)
29
25
  end
30
26
  end
@@ -34,15 +30,7 @@ module ArJdbc
34
30
  # It appears that at this point (AR 5.0) "prepare" should only ever be true
35
31
  # if prepared statements are enabled
36
32
  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
- if preventing_writes? && write_query?(sql)
40
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
41
- end
42
-
43
- mark_transaction_written_if_write(sql)
44
-
45
- binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
33
+ sql = preprocess_query(sql)
46
34
 
47
35
  # puts "[1]internal----->sql: #{sql}, binds: #{binds}"
48
36
  type_casted_binds = type_casted_binds(binds)
@@ -55,28 +43,28 @@ module ArJdbc
55
43
  log(sql, name, type_casted_binds, async: async) do
56
44
  # this is different from normal AR that always caches
57
45
  cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
58
- conn.execute_prepared_query(sql, type_casted_binds, cached_statement)
46
+ conn.execute_prepared_query(sql, binds, cached_statement)
59
47
  end
60
48
  end
61
49
  end
62
50
  end
63
51
 
64
52
  def exec_update(sql, name = 'SQL', binds = NO_BINDS)
65
- sql = transform_query(sql)
66
-
67
- if preventing_writes?
68
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
69
- end
70
-
71
- mark_transaction_written_if_write(sql)
53
+ sql = preprocess_query(sql)
72
54
 
73
55
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
74
56
 
57
+ type_casted_binds = type_casted_binds(binds)
58
+
75
59
  with_raw_connection do |conn|
76
60
  if without_prepared_statement?(binds)
77
61
  log(sql, name) { conn.execute_update(sql) }
78
62
  else
79
- log(sql, name, binds) { conn.execute_prepared_update(sql, binds) }
63
+ log(sql, name, binds, type_casted_binds) do
64
+ # TODO: the ideas is to pass type casted binds but some tests fail
65
+ # conn.execute_prepared_update(sql, type_casted_binds)
66
+ conn.execute_prepared_update(sql, binds)
67
+ end
80
68
  end
81
69
  end
82
70
  end
@@ -90,22 +78,15 @@ module ArJdbc
90
78
 
91
79
  private
92
80
 
81
+ def without_prepared_statement?(binds)
82
+ !prepared_statements || binds.empty?
83
+ end
84
+
93
85
  def convert_legacy_binds_to_attributes(binds)
94
86
  binds.map do |column, value|
95
87
  ActiveRecord::Relation::QueryAttribute.new(nil, type_cast(value, column), ActiveModel::Type::Value.new)
96
88
  end
97
89
  end
98
-
99
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
100
- log(sql, name, async: async) do
101
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
102
- result = conn.execute(sql)
103
- verified!
104
- result
105
- end
106
- end
107
- end
108
-
109
90
  end
110
91
  end
111
92
  end
Binary file
@@ -29,6 +29,7 @@ require 'arjdbc/mssql/transaction'
29
29
  require 'arjdbc/mssql/errors'
30
30
  require 'arjdbc/mssql/schema_creation'
31
31
  require 'arjdbc/mssql/database_limits'
32
+ require "arjdbc/mssql/type_metadata"
32
33
 
33
34
  require "arjdbc/mssql/adapter_hash_config"
34
35
 
@@ -479,31 +480,31 @@ module ActiveRecord
479
480
  def translate_exception(exception, message:, sql:, binds:)
480
481
  case message
481
482
  when /no connection available/i
482
- ConnectionNotEstablished.new(exception)
483
+ ConnectionNotEstablished.new(message, connection_pool: @pool)
483
484
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
484
- RecordNotUnique.new(message, sql: sql, binds: binds)
485
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
485
486
  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)
487
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
487
488
  when /Lock request time out period exceeded/i
488
489
  LockTimeout.new(message, sql: sql, binds: binds)
489
490
  when /The .* statement conflicted with the FOREIGN KEY constraint/
490
- InvalidForeignKey.new(message, sql: sql, binds: binds)
491
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
491
492
  when /Could not drop object .* because it is referenced by a FOREIGN KEY constraint/
492
- StatementInvalid.new(message, sql: sql, binds: binds)
493
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
493
494
  when /The .* statement conflicted with the REFERENCE constraint/
494
- InvalidForeignKey.new(message, sql: sql, binds: binds)
495
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
495
496
  when /(String or binary data would be truncated)/i
496
- ValueTooLong.new(message, sql: sql, binds: binds)
497
+ ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
497
498
  when /Cannot insert the value NULL into column .* does not allow nulls/
498
- NotNullViolation.new(message, sql: sql, binds: binds)
499
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
499
500
  when /Arithmetic overflow error converting expression/
500
501
  RangeError.new(message, sql: sql, binds: binds)
501
502
  when /Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation/
502
- StatementInvalid.new(message, sql: sql, binds: binds)
503
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
503
504
  when /Incorrect syntax near the keyword .*/
504
- StatementInvalid.new(message, sql: sql, binds: binds)
505
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
505
506
  when /Could not find stored procedure .*/
506
- StatementInvalid.new(message, sql: sql, binds: binds)
507
+ StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
507
508
  else
508
509
  super
509
510
  end
@@ -6,12 +6,12 @@ module ActiveRecord
6
6
  class MSSQLColumn < Column
7
7
  attr_reader :table_name
8
8
 
9
- def initialize(name, raw_default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
9
+ def initialize(name, cast_type, raw_default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
10
10
  @table_name = table_name
11
11
 
12
12
  default_val, default_fun = extract_default(raw_default)
13
13
 
14
- super(name, default_val, sql_type_metadata, null, default_fun, collation: collation, comment: comment)
14
+ super(name, cast_type, default_val, sql_type_metadata, null, default_fun, collation: collation, comment: comment)
15
15
  end
16
16
 
17
17
  def extract_default(value)
@@ -92,12 +92,8 @@ module ActiveRecord
92
92
  end
93
93
  end
94
94
 
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)
95
+ def internal_exec_query(sql, name = 'SQL', binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true)
96
+ sql = preprocess_query(sql)
101
97
 
102
98
  # binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
103
99
 
@@ -105,7 +101,7 @@ module ActiveRecord
105
101
  type_casted_binds = type_casted_binds(binds)
106
102
  # puts "[2]internal----->sql: #{type_casted_binds.size}, binds: #{type_casted_binds}"
107
103
 
108
- if without_prepared_statement?(binds)
104
+ if binds.nil? || binds.empty?
109
105
  log(sql, name) do
110
106
  with_raw_connection do |conn|
111
107
  result = conditional_indentity_insert(sql) do
@@ -132,14 +128,12 @@ module ActiveRecord
132
128
  end
133
129
 
134
130
  def exec_update(sql, name = nil, binds = [])
135
- sql = transform_query(sql)
131
+ sql = preprocess_query(sql)
136
132
 
137
- check_if_write_query(sql)
138
-
139
- mark_transaction_written_if_write(sql)
133
+ type_casted_binds = type_casted_binds(binds)
140
134
 
141
135
  # puts "exec_update----->sql: #{sql}, binds: #{binds}"
142
- if without_prepared_statement?(binds)
136
+ if binds.nil? || binds.empty?
143
137
  log(sql, name) do
144
138
  with_raw_connection do |conn|
145
139
  result = conn.execute_update(sql)
@@ -148,9 +142,9 @@ module ActiveRecord
148
142
  end
149
143
  end
150
144
  else
151
- log(sql, name, binds) do
145
+ log(sql, name, binds, type_casted_binds) do
152
146
  with_raw_connection do |conn|
153
- result = conn.execute_prepared_update(sql, binds)
147
+ result = conn.execute_prepared_update(sql, type_casted_binds)
154
148
  verified!
155
149
  result
156
150
  end
@@ -160,14 +154,12 @@ module ActiveRecord
160
154
  alias :exec_delete :exec_update
161
155
 
162
156
  def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
163
- sql = transform_query(sql)
157
+ sql = preprocess_query(sql)
164
158
 
165
- check_if_write_query(sql)
166
-
167
- mark_transaction_written_if_write(sql)
159
+ type_casted_binds = type_casted_binds(binds)
168
160
 
169
161
  # puts "exec_insert----->sql: #{sql}, binds: #{binds}"
170
- if without_prepared_statement?(binds)
162
+ if binds.nil? || binds.empty?
171
163
  log(sql, name) do
172
164
  with_raw_connection do |conn|
173
165
  result = conditional_indentity_insert(sql) do
@@ -178,12 +170,14 @@ module ActiveRecord
178
170
  end
179
171
  end
180
172
  else
181
- log(sql, name, binds) do
173
+ log(sql, name, binds, type_casted_binds) do
182
174
  with_raw_connection do |conn|
183
175
  result = conditional_indentity_insert(sql) do
184
176
  # DEPRECATION WARNING: to_time will always preserve the timezone offset of the receiver in Rails 8.0.
185
177
  # To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`.
186
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)
187
181
  conn.execute_insert_pk(sql, binds, pk)
188
182
  end
189
183
  verified!
@@ -195,15 +189,23 @@ module ActiveRecord
195
189
 
196
190
  private
197
191
 
198
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
199
- # puts "raw_execute----->sql: #{sql}"
200
- log(sql, name, async: async) do
201
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
202
- result = conditional_indentity_insert(sql) { conn.execute(sql) }
203
- verified!
204
- result
205
- end
206
- 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
207
209
  end
208
210
 
209
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
 
@@ -55,7 +55,7 @@ module ActiveRecord
55
55
  end
56
56
 
57
57
  def add_column_options!(sql, options)
58
- sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
58
+ sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options)
59
59
 
60
60
  sql << ' NOT NULL' if options[:null] == false
61
61
 
@@ -419,6 +419,10 @@ module ActiveRecord
419
419
  field
420
420
  end
421
421
 
422
+ def fetch_type_metadata(sql_type)
423
+ MSSQL::TypeMetadata.new(super(sql_type))
424
+ end
425
+
422
426
  def create_db_edition_options(options = {})
423
427
  edition_config = options.select { |k, _v| k.match?('azure') }
424
428
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MSSQL
6
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
7
+ undef to_yaml if method_defined?(:to_yaml)
8
+
9
+ include Deduplicable
10
+
11
+ def initialize(type_metadata)
12
+ super(type_metadata)
13
+ end
14
+
15
+ def ==(other)
16
+ other.is_a?(TypeMetadata) &&
17
+ __getobj__ == other.__getobj__
18
+ end
19
+
20
+ alias eql? ==
21
+
22
+ def hash
23
+ TypeMetadata.hash ^
24
+ __getobj__.hash
25
+ end
26
+
27
+ private
28
+
29
+ def deduplicated
30
+ __setobj__(__getobj__.deduplicate)
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -20,12 +20,13 @@ module ActiveRecord
20
20
 
21
21
  # This is the Rails binary type
22
22
  class VarbinaryMax < ActiveRecord::Type::Binary
23
- def type
24
- :binary
23
+ def initialize(**args)
24
+ super
25
+ @limit = 2_147_483_647
25
26
  end
26
27
 
27
- def limit
28
- @limit ||= 2_147_483_647
28
+ def type
29
+ :binary
29
30
  end
30
31
  end
31
32
 
@@ -7,32 +7,35 @@ module ActiveRecord
7
7
  module Type
8
8
 
9
9
  class Text < ActiveRecord::Type::String
10
- def type
11
- :text_basic
10
+ def initialize(**args)
11
+ super
12
+ @limit = 2_147_483_647
12
13
  end
13
14
 
14
- def limit
15
- @limit ||= 2_147_483_647
15
+ def type
16
+ :text_basic
16
17
  end
17
18
  end
18
19
 
19
20
  class Ntext < ActiveRecord::Type::String
20
- def type
21
- :ntext
21
+ def initialize(**args)
22
+ super
23
+ @limit = 2_147_483_647
22
24
  end
23
25
 
24
- def limit
25
- @limit ||= 2_147_483_647
26
+ def type
27
+ :ntext
26
28
  end
27
29
  end
28
30
 
29
31
  class Image < ActiveRecord::Type::Binary
30
- def type
31
- :image
32
+ def initialize(**args)
33
+ super
34
+ @limit = 2_147_483_647
32
35
  end
33
36
 
34
- def limit
35
- @limit ||= 2_147_483_647
37
+ def type
38
+ :image
36
39
  end
37
40
  end
38
41
 
@@ -19,12 +19,13 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  class VarcharMax < ActiveRecord::Type::String
22
- def type
23
- :varchar_max
22
+ def initialize(**args)
23
+ super
24
+ @limit = 2_147_483_647
24
25
  end
25
26
 
26
- def limit
27
- @limit ||= 2_147_483_647
27
+ def type
28
+ :varchar_max
28
29
  end
29
30
  end
30
31
 
@@ -43,12 +44,13 @@ module ActiveRecord
43
44
 
44
45
  # This is Rails logical text type
45
46
  class NvarcharMax < ActiveRecord::Type::String
46
- def type
47
- :text
47
+ def initialize(**args)
48
+ super
49
+ @limit = 2_147_483_647
48
50
  end
49
51
 
50
- def limit
51
- @limit ||= 2_147_483_647
52
+ def type
53
+ :text
52
54
  end
53
55
  end
54
56
 
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, 1] # warns on AR >= 4.2