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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +23 -16
- data/.github/workflows/ruby.yml +123 -18
- data/.mise.toml +3 -0
- data/Gemfile +43 -48
- data/README.md +3 -2
- data/RUNNING_TESTS.md +9 -1
- data/Rakefile +63 -15
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/activerecord-jdbc-alt-adapter.gemspec +1 -1
- data/lib/arjdbc/abstract/core.rb +0 -17
- data/lib/arjdbc/abstract/database_statements.rb +23 -19
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/mssql/adapter.rb +11 -11
- data/lib/arjdbc/mssql/database_statements.rb +32 -29
- data/lib/arjdbc/mssql/explain_support.rb +6 -1
- data/lib/arjdbc/mssql/quoting.rb +9 -0
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +4 -1
- data/lib/arjdbc/postgresql/database_statements.rb +45 -0
- data/lib/arjdbc/postgresql/schema_statements.rb +17 -0
- data/lib/arjdbc/sqlite3/adapter.rb +51 -7
- data/lib/arjdbc/sqlite3/database_statements.rb +26 -0
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +3 -3
- data/rakelib/02-test.rake +1 -1
- data/rakelib/db.rake +5 -3
- data/rakelib/rails.rake +0 -1
- data/src/java/arjdbc/ArJdbcModule.java +13 -10
- data/src/java/arjdbc/db2/DB2Module.java +4 -4
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +8 -9
- data/src/java/arjdbc/h2/H2Module.java +2 -1
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +8 -7
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +5 -6
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +4 -3
- data/src/java/arjdbc/jdbc/DriverWrapper.java +13 -8
- data/src/java/arjdbc/jdbc/JdbcResult.java +10 -7
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +176 -155
- data/src/java/arjdbc/mssql/MSSQLModule.java +3 -3
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +15 -9
- data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +25 -23
- data/src/java/arjdbc/oracle/OracleModule.java +2 -3
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +22 -18
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +2 -3
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +24 -12
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +25 -12
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +2 -3
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +78 -29
- data/src/java/arjdbc/util/DateTimeUtils.java +7 -5
- data/src/java/arjdbc/util/ObjectSupport.java +2 -2
- data/src/java/arjdbc/util/StringHelper.java +1 -1
- metadata +7 -6
- 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
|
-
|
|
111
|
-
|
|
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 = '
|
|
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.
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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", "~>
|
|
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", "~>
|
|
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'
|
data/lib/arjdbc/abstract/core.rb
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
data/lib/arjdbc/mssql/adapter.rb
CHANGED
|
@@ -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(
|
|
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 =
|
|
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
|
|
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 =
|
|
131
|
+
sql = preprocess_query(sql)
|
|
135
132
|
|
|
136
|
-
|
|
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
|
|
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,
|
|
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 =
|
|
157
|
+
sql = preprocess_query(sql)
|
|
163
158
|
|
|
164
|
-
|
|
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
|
|
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
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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 =
|
|
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
|
|
data/lib/arjdbc/mssql/quoting.rb
CHANGED
data/lib/arjdbc/mssql.rb
CHANGED
|
@@ -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)
|