Skip to content

Commit 0944ef9

Browse files
authored
Merge pull request #1167 from JesseChavez/rails_72_initial_fixes
Initial fixes to support Rails 7.2, mostly postgres fix
2 parents 784b5a8 + 1d1d7f2 commit 0944ef9

31 files changed

+426
-148
lines changed

activerecord-jdbc-adapter.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Gem::Specification.new do |gem|
4141
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
4242
gem.test_files = gem.files.grep(%r{^test/})
4343

44-
gem.add_dependency 'activerecord', '~> 7.1.3'
44+
gem.add_dependency "activerecord", "~> 7.2.2"
4545

4646
#gem.add_development_dependency 'test-unit', '2.5.4'
4747
#gem.add_development_dependency 'test-unit-context', '>= 0.3.0'

lib/arjdbc/abstract/database_statements.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def exec_update(sql, name = 'SQL', binds = NO_BINDS)
7979
alias :exec_delete :exec_update
8080

8181
# overridden to support legacy binds
82-
def select_all(arel, name = nil, binds = NO_BINDS, preparable: nil, async: false)
82+
def select_all(arel, name = nil, binds = NO_BINDS, preparable: nil, async: false, allow_retry: false)
8383
binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
8484
super
8585
end

lib/arjdbc/mysql.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
require 'arjdbc'
22
require 'arjdbc/mysql/adapter'
3-
require 'arjdbc/mysql/connection_methods'
3+
# require 'arjdbc/mysql/connection_methods'

lib/arjdbc/mysql/adapter.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
require 'arjdbc/abstract/statement_cache'
1212
require 'arjdbc/abstract/transaction_support'
1313

14+
require "arjdbc/mysql/adapter_hash_config"
15+
1416
require "arjdbc/abstract/relation_query_attribute_monkey_patch"
1517

1618
module ActiveRecord
@@ -34,6 +36,7 @@ class Mysql2Adapter < AbstractMysqlAdapter
3436
include ArJdbc::Abstract::TransactionSupport
3537

3638
include ArJdbc::MySQL
39+
include ArJdbc::MysqlConfig
3740

3841
class << self
3942
def jdbc_connection_class
@@ -68,14 +71,17 @@ def initialize(...)
6871

6972
@config[:flags] ||= 0
7073

74+
# assign arjdbc extra connection params
75+
conn_params = build_connection_config(@config.compact)
76+
7177
# JDBC mysql appears to use found rows by default: https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-connection.html
7278
# if @config[:flags].kind_of? Array
7379
# @config[:flags].push "FOUND_ROWS"
7480
# else
7581
# @config[:flags] |= ::Mysql2::Client::FOUND_ROWS
7682
# end
7783

78-
@connection_parameters ||= @config
84+
@connection_parameters = conn_params
7985
end
8086

8187
def self.database_exists?(config)
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# frozen_string_literal: true
2+
3+
module ArJdbc
4+
module MysqlConfig
5+
def build_connection_config(config)
6+
config = config.deep_dup
7+
8+
load_jdbc_driver
9+
10+
config[:driver] ||= database_driver_name
11+
12+
host = (config[:host] ||= "localhost")
13+
port = (config[:port] ||= 3306)
14+
15+
# jdbc:mysql://[host][,failoverhost...][:port]/[database]
16+
# - alternate fail-over syntax: [host:port],[host:port]/[database]
17+
config[:url] ||= "jdbc:mysql://#{host}:#{port}/#{config[:database]}"
18+
19+
config[:properties] = build_properties(config)
20+
21+
config
22+
end
23+
24+
private
25+
26+
def load_jdbc_driver
27+
require "jdbc/mysql"
28+
29+
::Jdbc::MySQL.load_driver(:require) if defined?(::Jdbc::MySQL.load_driver)
30+
rescue LoadError
31+
# assuming driver.jar is on the class-path
32+
end
33+
34+
def database_driver_name
35+
return ::Jdbc::MySQL.driver_name if defined?(::Jdbc::MySQL.driver_name)
36+
37+
"com.mysql.jdbc.Driver"
38+
end
39+
40+
def build_properties(config)
41+
properties = config[:properties] || {}
42+
43+
properties["zeroDateTimeBehavior"] ||= "CONVERT_TO_NULL"
44+
45+
properties["jdbcCompliantTruncation"] ||= false
46+
47+
charset_name = convert_mysql_encoding(config)
48+
49+
# do not set characterEncoding
50+
if charset_name.eql?(false)
51+
properties["character_set_server"] = config[:encoding] || "utf8"
52+
else
53+
properties["characterEncoding"] = charset_name
54+
end
55+
56+
# driver also executes: "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8")
57+
# thus no need to do it on configure_connection :
58+
config[:encoding] = nil if config.key?(:encoding)
59+
60+
properties["connectionCollation"] ||= config[:collation] if config[:collation]
61+
62+
properties["autoReconnect"] ||= reconnect.to_s unless config[:reconnect].nil?
63+
64+
properties["noDatetimeStringSync"] = true unless properties.key?("noDatetimeStringSync")
65+
66+
sslcert = config[:sslcert]
67+
sslca = config[:sslca]
68+
69+
if config[:sslkey] || sslcert
70+
properties["useSSL"] ||= true
71+
properties["requireSSL"] ||= true
72+
properties["clientCertificateKeyStoreUrl"] ||= java.io.File.new(sslcert).to_url.to_s if sslcert
73+
74+
if sslca
75+
properties["trustCertificateKeyStoreUrl"] ||= java.io.File.new(sslca).to_url.to_s
76+
else
77+
properties["verifyServerCertificate"] ||= false
78+
end
79+
else
80+
# According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection
81+
# must be established by default if explicit option isn't set :
82+
properties["useSSL"] ||= false
83+
end
84+
85+
# disables the effect of 'useTimezone'
86+
properties["useLegacyDatetimeCode"] = false
87+
88+
properties
89+
end
90+
91+
# See https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-charsets.html
92+
# to charset-name (characterEncoding=...)
93+
def convert_mysql_encoding(config)
94+
# NOTE: this is "better" than passing what users are used to set on MRI
95+
# e.g. 'utf8mb4' will fail cause the driver will check for a Java charset
96+
# ... it's smart enough to detect utf8mb4 from server variables :
97+
# "character_set_client" && "character_set_connection" (thus UTF-8)
98+
encoding = config.key?(:encoding) ? config[:encoding] : "utf8"
99+
100+
value = MYSQL_ENCODINGS[encoding]
101+
102+
return false if value == false
103+
104+
value || encoding
105+
end
106+
107+
MYSQL_ENCODINGS = {
108+
"big5" => "Big5",
109+
"dec8" => nil,
110+
"hp8" => nil,
111+
"latin1" => "Cp1252",
112+
"latin2" => "ISO8859_2",
113+
"swe7" => nil,
114+
"ascii" => "US-ASCII",
115+
"ujis" => "EUC_JP",
116+
"sjis" => "SJIS",
117+
"hebrew" => "ISO8859_8",
118+
"tis620" => "TIS620",
119+
"euckr" => "EUC_KR",
120+
"gb2312" => "EUC_CN",
121+
"greek" => "ISO8859_7",
122+
"cp1250" => "Cp1250",
123+
"gbk" => "GBK",
124+
"armscii8" => nil,
125+
"ucs2" => "UnicodeBig",
126+
"cp866" => "Cp866",
127+
"keybcs2" => nil,
128+
"macce" => "MacCentralEurope",
129+
"macroman" => "MacRoman",
130+
"cp1251" => "Cp1251",
131+
"cp1256" => "Cp1256",
132+
"cp1257" => "Cp1257",
133+
"binary" => false,
134+
"geostd8" => nil,
135+
"cp932" => "Cp932",
136+
"utf8" => "UTF-8",
137+
"utf8mb4" => false,
138+
"utf16" => false,
139+
"utf32" => false,
140+
# "cp850" => "Cp850",
141+
# "koi8r" => "KOI8-R",
142+
# "koi8u" => "KOI8-R",
143+
# "latin5" => "ISO-8859-9",
144+
# "cp852" => "CP852",
145+
# "latin7" => "ISO-8859-13",
146+
# "eucjpms" => "eucJP-ms"
147+
}.freeze
148+
end
149+
end

lib/arjdbc/postgresql.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
require 'arjdbc'
22
require 'arjdbc/postgresql/adapter'
3-
require 'arjdbc/postgresql/connection_methods'
3+
# require 'arjdbc/postgresql/connection_methods'

lib/arjdbc/postgresql/adapter.rb

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
require 'arjdbc/postgresql/name'
2525
require 'arjdbc/postgresql/database_statements'
2626
require 'arjdbc/postgresql/schema_statements'
27+
require "arjdbc/postgresql/adapter_hash_config"
2728

2829
require 'active_model'
2930

@@ -855,6 +856,7 @@ class PostgreSQLAdapter < AbstractAdapter
855856
include ArJdbc::Abstract::StatementCache
856857
include ArJdbc::Abstract::TransactionSupport
857858
include ArJdbc::PostgreSQL
859+
include ArJdbc::PostgreSQLConfig
858860

859861
require 'arjdbc/postgresql/oid_types'
860862
include ::ArJdbc::PostgreSQL::OIDTypes
@@ -900,7 +902,8 @@ def dbconsole(config, options = {})
900902
def initialize(...)
901903
super
902904

903-
conn_params = @config.compact
905+
# assign arjdbc extra connection params
906+
conn_params = build_connection_config(@config.compact)
904907

905908
@connection_parameters = conn_params
906909

@@ -912,15 +915,6 @@ def initialize(...)
912915
self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
913916
end
914917

915-
def self.database_exists?(config)
916-
conn = ActiveRecord::Base.postgresql_connection(config)
917-
conn && conn.really_valid?
918-
rescue ActiveRecord::NoDatabaseError
919-
false
920-
ensure
921-
conn.disconnect! if conn
922-
end
923-
924918
require 'active_record/connection_adapters/postgresql/schema_definitions'
925919

926920
ColumnMethods = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# frozen_string_literal: true
2+
3+
module ArJdbc
4+
module PostgreSQLConfig
5+
def build_connection_config(config)
6+
config = config.deep_dup
7+
8+
load_jdbc_driver
9+
10+
config[:driver] ||= database_driver_name
11+
12+
host = (config[:host] ||= config[:hostaddr] || ENV["PGHOST"] || "localhost")
13+
port = (config[:port] ||= ENV["PGPORT"] || 5432)
14+
database = config[:database] || config[:dbname] || ENV["PGDATABASE"]
15+
16+
config[:url] ||= "jdbc:postgresql://#{host}:#{port}/#{database}"
17+
18+
config[:url] << config[:pg_params] if config[:pg_params]
19+
20+
config[:username] ||= config[:user] || ENV["PGUSER"] || ENV_JAVA["user.name"]
21+
config[:password] ||= ENV["PGPASSWORD"] unless config.key?(:password)
22+
23+
config[:properties] = build_properties(config)
24+
25+
config
26+
end
27+
28+
private
29+
30+
def load_jdbc_driver
31+
require "jdbc/postgres"
32+
33+
::Jdbc::Postgres.load_driver(:require) if defined?(::Jdbc::Postgres.load_driver)
34+
rescue LoadError
35+
# assuming driver.jar is on the class-path
36+
end
37+
38+
def database_driver_name
39+
return ::Jdbc::Postgres.driver_name if defined?(::Jdbc::Postgres.driver_name)
40+
41+
"org.postgresql.Driver"
42+
end
43+
44+
def build_properties(config)
45+
properties = config[:properties] || {}
46+
47+
# PG :connect_timeout - maximum time to wait for connection to succeed
48+
connect_timeout = config[:connect_timeout] || ENV["PGCONNECT_TIMEOUT"]
49+
50+
properties["socketTimeout"] ||= connect_timeout if connect_timeout
51+
52+
login_timeout = config[:login_timeout]
53+
54+
properties["loginTimeout"] ||= login_timeout if login_timeout
55+
56+
sslmode = config.key?(:sslmode) ? config[:sslmode] : config[:requiressl]
57+
# NOTE: makes not much sense since this needs some JVM options :
58+
sslmode = ENV["PGSSLMODE"] || ENV["PGREQUIRESSL"] if sslmode.nil?
59+
60+
# PG :sslmode - disable|allow|prefer|require
61+
unless sslmode.nil? || !(sslmode == true || sslmode.to_s == "require")
62+
# JRuby/JVM needs to be started with :
63+
# -Djavax.net.ssl.trustStore=mystore -Djavax.net.ssl.trustStorePassword=...
64+
# or a non-validating connection might be used (for testing) :
65+
# :sslfactory = 'org.postgresql.ssl.NonValidatingFactory'
66+
67+
if config[:driver].start_with?("org.postgresql.")
68+
properties["sslfactory"] ||= "org.postgresql.ssl.NonValidatingFactory"
69+
end
70+
71+
properties["ssl"] ||= "true"
72+
end
73+
74+
properties["tcpKeepAlive"] ||= config[:keepalives] if config.key?(:keepalives)
75+
properties["kerberosServerName"] ||= config[:krbsrvname] if config[:krbsrvname]
76+
77+
prepared_statements = config.fetch(:prepared_statements, true)
78+
79+
prepared_statements = false if prepared_statements == "false"
80+
81+
if prepared_statements
82+
# this makes the pgjdbc driver handle hot compatibility internally
83+
properties["autosave"] ||= "conservative"
84+
else
85+
# If prepared statements are off, lets make sure they are really *off*
86+
properties["prepareThreshold"] = 0
87+
end
88+
89+
properties
90+
end
91+
end
92+
end

lib/arjdbc/postgresql/base/array_encoder.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ def initialize(name:, delimiter:)
1212
'text'.freeze
1313
else
1414
base_type = name.chomp('[]').to_sym
15-
ActiveRecord::Base.connection.native_database_types[base_type][:name]
15+
ActiveRecord::Base.with_connection do |connection|
16+
connection.native_database_types[base_type][:name]
17+
end
1618
end
1719
end
1820

lib/arjdbc/postgresql/oid_types.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ def initialize_type_map_inner(m)
112112
m.register_type "int4", Type::Integer.new(limit: 4)
113113
m.register_type "int8", Type::Integer.new(limit: 8)
114114
m.register_type "oid", OID::Oid.new
115-
m.register_type "float4", Type::Float.new
116-
m.alias_type "float8", "float4"
115+
m.register_type "float4", Type::Float.new(limit: 24)
116+
m.register_type "float8", Type::Float.new
117117
m.register_type "text", Type::Text.new
118118
register_class_with_limit m, "varchar", Type::String
119119
m.alias_type "char", "varchar"

lib/arjdbc/sqlite3.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
require 'arjdbc'
22
require 'arjdbc/sqlite3/adapter'
3-
require 'arjdbc/sqlite3/connection_methods'
3+
# require 'arjdbc/sqlite3/connection_methods'

0 commit comments

Comments
 (0)