Skip to content

Commit 81506f0

Browse files
authored
Merge pull request #1153 from JesseChavez/rails_71_support_arjdbc_postgres
Fix some arjdbc postgres tests to support AR 7.1
2 parents 8029660 + 2d3aa77 commit 81506f0

11 files changed

+127
-55
lines changed

lib/arjdbc/abstract/connection_management.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def disconnect!
4141
# DIFFERENCE: we delve into jdbc shared code and this does self.class.new_client.
4242
def connect
4343
@raw_connection = self.class.new_client(@connection_parameters, self)
44-
rescue ConnectionNotEstablished => ex
44+
rescue ActiveRecord::ConnectionNotEstablished => ex
4545
raise ex.set_pool(@pool)
4646
end
4747

lib/arjdbc/postgresql/adapter.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
require 'active_record/connection_adapters/postgresql/schema_statements'
1414
require 'active_record/connection_adapters/postgresql/type_metadata'
1515
require 'active_record/connection_adapters/postgresql/utils'
16+
1617
require 'arjdbc/abstract/core'
1718
require 'arjdbc/abstract/connection_management'
1819
require 'arjdbc/abstract/database_statements'
@@ -21,6 +22,8 @@
2122
require 'arjdbc/postgresql/base/array_decoder'
2223
require 'arjdbc/postgresql/base/array_encoder'
2324
require 'arjdbc/postgresql/name'
25+
require 'arjdbc/postgresql/schema_statements'
26+
2427
require 'active_model'
2528

2629
module ArJdbc
@@ -97,8 +100,7 @@ def configure_connection
97100
end
98101
end
99102

100-
@type_map = Type::HashLookupTypeMap.new
101-
initialize_type_map
103+
reload_type_map
102104
end
103105

104106
# @private
@@ -125,7 +127,7 @@ def configure_connection
125127
inet: { name: 'inet' },
126128
int4range: { name: 'int4range' },
127129
int8range: { name: 'int8range' },
128-
integer: { name: 'integer' },
130+
integer: { name: 'integer', limit: 4 },
129131
interval: { name: 'interval' },
130132
json: { name: 'json' },
131133
jsonb: { name: 'jsonb' },
@@ -752,6 +754,7 @@ class PostgreSQLAdapter < AbstractAdapter
752754

753755
require 'arjdbc/postgresql/oid_types'
754756
include ::ArJdbc::PostgreSQL::OIDTypes
757+
include ::ArJdbc::PostgreSQL::SchemaStatements
755758

756759
include ::ArJdbc::PostgreSQL::ColumnHelpers
757760

lib/arjdbc/postgresql/oid_types.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,15 @@ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
118118
end
119119

120120
def reload_type_map
121-
type_map.clear
121+
@lock.synchronize do
122+
if @type_map
123+
type_map.clear
124+
else
125+
@type_map = Type::HashLookupTypeMap.new
126+
end
127+
122128
initialize_type_map
129+
end
123130
end
124131

125132
def initialize_type_map_inner(m)
@@ -274,10 +281,6 @@ def extract_precision(sql_type)
274281
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
275282
end
276283

277-
def extract_limit(sql_type)
278-
$1.to_i if sql_type =~ /\((.*)\)/
279-
end
280-
281284
# Support arrays/ranges for defining attributes that don't exist in the db
282285
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
283286
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
module ArJdbc
4+
module PostgreSQL
5+
module SchemaStatements
6+
ForeignKeyDefinition = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
7+
Utils = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
8+
9+
def foreign_keys(table_name)
10+
scope = quoted_scope(table_name)
11+
fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
12+
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred, c.conkey, c.confkey, c.conrelid, c.confrelid
13+
FROM pg_constraint c
14+
JOIN pg_class t1 ON c.conrelid = t1.oid
15+
JOIN pg_class t2 ON c.confrelid = t2.oid
16+
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
17+
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
18+
JOIN pg_namespace t3 ON c.connamespace = t3.oid
19+
WHERE c.contype = 'f'
20+
AND t1.relname = #{scope[:name]}
21+
AND t3.nspname = #{scope[:schema]}
22+
ORDER BY c.conname
23+
SQL
24+
25+
fk_info.map do |row|
26+
to_table = Utils.unquote_identifier(row["to_table"])
27+
# conkey = row["conkey"].scan(/\d+/).map(&:to_i)
28+
# confkey = row["confkey"].scan(/\d+/).map(&:to_i)
29+
conkey = row["conkey"]
30+
confkey = row["confkey"]
31+
32+
if conkey.size > 1
33+
column = column_names_from_column_numbers(row["conrelid"], conkey)
34+
primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
35+
else
36+
column = Utils.unquote_identifier(row["column"])
37+
primary_key = row["primary_key"]
38+
end
39+
40+
options = {
41+
column: column,
42+
name: row["name"],
43+
primary_key: primary_key
44+
}
45+
46+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
47+
options[:on_update] = extract_foreign_key_action(row["on_update"])
48+
options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])
49+
50+
options[:validate] = row["valid"]
51+
52+
ForeignKeyDefinition.new(table_name, to_table, options)
53+
end
54+
end
55+
end
56+
end
57+
end

test/db/postgresql/hstore_test.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ class PostgreSQLHstoreTest < Test::Unit::TestCase
66

77
class Hstore < ActiveRecord::Base
88
self.table_name = 'hstores'
9-
store :tags, :accessors => [ :name ]
9+
10+
store_accessor :tags, [:name]
1011
end
1112

1213
def setup
@@ -36,18 +37,17 @@ def teardown
3637

3738
def test_parse
3839
column_type = Hstore.type_for_attribute('tags')
39-
assert_equal({}, column_type.deserialize(''))
40-
assert_equal({'1' => '2'}, column_type.deserialize('"1"=>"2"'))
41-
assert_equal({'key'=>nil}, column_type.deserialize('key => NULL'))
42-
assert_equal({'c'=>'}','"a"'=>'b "a b'}, column_type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
40+
41+
assert_equal({}, column_type.deserialize(''))
42+
assert_equal({ '1' => '2' }, column_type.deserialize('"1"=>"2"'))
4343
end
4444

4545
def test_store_select
4646
@connection.execute "insert into hstores (tags) VALUES ('name=>ferko,type=>suska')"
4747
x = Hstore.first
48+
4849
assert_equal 'ferko', x.name
49-
assert_equal 'suska', x.tags[:type]
50-
assert_instance_of ActiveSupport::HashWithIndifferentAccess, x.tags
50+
assert_equal 'suska', x.tags['type']
51+
assert_instance_of Hash, x.tags
5152
end
52-
5353
end

test/db/postgresql/jdbc_postgres_test.rb

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
module Jdbc
44
class PostgresTest < Test::Unit::TestCase
5-
6-
SKIP_TESTS = false
75
SYSTEM_ENV = ENV_JAVA.dup
86

97
setup do
@@ -16,37 +14,16 @@ class PostgresTest < Test::Unit::TestCase
1614

1715
test('some') { assert Jdbc::Postgres }
1816

19-
test 'returns jdbc version 4.1 on java 7' do
20-
ENV_JAVA[ 'java.specification.version' ] = '1.7'
21-
assert_equal 7, Jdbc::Postgres.send(:jre_version)
22-
end unless SKIP_TESTS
23-
24-
test 'returns jdbc version default (4.2) on java 8/9' do
25-
ENV_JAVA[ 'java.specification.version' ] = '1.8'
26-
assert_nil Jdbc::Postgres.send(:jre_version)
17+
test 'returns jdbc version default (4.2) on java 8' do
18+
ENV_JAVA['java.specification.version'] = '1.8'
2719

28-
ENV_JAVA[ 'java.specification.version' ] = '9'
2920
assert_nil Jdbc::Postgres.send(:jre_version)
30-
end unless SKIP_TESTS
31-
32-
context 'load-driver' do
33-
34-
ROOT_DIR = File.expand_path('../../..', File.dirname(__FILE__))
35-
36-
@@driver_dir = File.join(ROOT_DIR, 'jdbc-postgres/lib')
21+
end
3722

38-
test 'on java 7' do
39-
ENV_JAVA[ 'java.specification.version' ] = '1.7'
40-
Jdbc::Postgres.expects(:load).with do |driver_jar|
41-
assert_match(/.jre7.jar$/, driver_jar)
42-
full_path = File.join(@@driver_dir, driver_jar)
43-
assert File.exist?(full_path), "#{driver_jar.inspect} not found in: #{@@driver_dir.inspect}"
44-
true
45-
end
46-
Jdbc::Postgres.load_driver
47-
end
23+
test 'returns jdbc version 4.2 on java 11' do
24+
ENV_JAVA['java.specification.version'] = '11'
4825

26+
assert_nil Jdbc::Postgres.send(:jre_version)
4927
end
50-
51-
end if defined? JRUBY_VERSION
28+
end
5229
end

test/db/postgresql/native_types_test.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def self.up
1919
"bigint_serial_should_be_integer bigint default nextval('seq_pk_customers')",
2020
"integer_serial_should_be_integer integer default nextval('seq_pk_customers')",
2121
"varchar_should_be_string varchar(2)",
22-
"timestamp_should_be_datetime timestamp",
22+
"timestamp_should_be_timestamp timestamp",
23+
"timestamptz_should_be_timestamptz timestamptz",
2324
"bytea_should_be_binary bytea",
2425
"double_precision_should_be_float double precision",
2526
"real_should_be_float real",
@@ -69,8 +70,14 @@ def test_varchar_should_be_mapped_to_string
6970
assert_instance_of ActiveModel::Type::String, column_type("varchar_should_be_string")
7071
end
7172

72-
def test_timestamp_should_be_mapped_to_datetime
73-
assert_instance_of OID::DateTime, column_type("timestamp_should_be_datetime")
73+
def test_timestamp_should_be_mapped_to_timestamp
74+
assert_kind_of OID::DateTime, column_type("timestamp_should_be_timestamp")
75+
assert_instance_of OID::Timestamp, column_type("timestamp_should_be_timestamp")
76+
end
77+
78+
def test_timestamptz_should_be_mapped_to_timestamptz
79+
assert_kind_of OID::DateTime, column_type("timestamptz_should_be_timestamptz")
80+
assert_instance_of OID::TimestampWithTimeZone, column_type("timestamptz_should_be_timestamptz")
7481
end
7582

7683
def test_bytea_should_be_mapped_to_binary

test/db/postgresql/schema_dump_test.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def test_schema_dump_should_not_have_limits_on_text_or_date
5858
def test_schema_dump_integer_with_no_limit_should_have_no_limit
5959
dump = dump_with_data_types
6060
lines = dump.lines.grep(/sample_integer_no_limit/)
61+
6162
assert ! lines.empty?
6263
lines.each { |line| assert line !~ /:limit|limit:/ }
6364
end

test/db/postgresql/simple_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,27 @@ def test_custom_select_date
5858
assert_equal my_date, sample_date
5959
end
6060

61+
# @override
62+
def test_big_decimal
63+
test_value = BigDecimal('9876543210_9876543210_9876543210.0')
64+
65+
conn = DbType.connection
66+
67+
if conn.prepared_statements?
68+
db_type = DbType.create!(big_decimal: test_value)
69+
db_type = DbType.find(db_type.id)
70+
assert_kind_of Integer, db_type.big_decimal
71+
assert_equal test_value, db_type.big_decimal
72+
else
73+
# it seems the patch applies when prepared statements is disabled
74+
# https://discuss.rubyonrails.org/t/cve-2022-44566-possible-denial-of-service-vulnerability-in-activerecords-postgresql-adapter/82119
75+
# https://github.com/rails/rails/commit/4f44aa9d514e701ada92b5cf08beccf566eeaebf
76+
assert_raise ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting::IntegerOutOf64BitRange do
77+
DbType.create!(big_decimal: test_value)
78+
end
79+
end
80+
end
81+
6182
def test_encoding
6283
assert_not_nil connection.encoding
6384
end
@@ -344,6 +365,7 @@ def test_foreign_keys
344365
t.string :title
345366
t.references :db_type, :index => true, :foreign_key => true
346367
end
368+
347369
assert_equal 1, connection.foreign_keys('db_posts').size
348370
assert_equal 'db_posts', connection.foreign_keys('db_posts')[0].from_table
349371
assert_equal 'db_types', connection.foreign_keys('db_posts')[0].to_table

test/db/postgresql/table_name_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ class SerialWithTrigger < ActiveRecord::Base;
8888

8989
test 'serial with trigger' do
9090
sn = SerialWithTrigger.create! :value => 1234567890.to_s
91-
assert sn.reload
9291

92+
assert sn.reload
9393
SerialWithTrigger.columns
9494
end
9595

test/rake_test_support.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,12 @@ def new_application
122122
# (Test) Helpers :
123123

124124
def create_schema_migrations_table(connection = ActiveRecord::Base.connection)
125-
schema_migration = ActiveRecord::SchemaMigration.table_name
126-
return if connection.data_source_exists?(schema_migration)
127-
connection.create_table(schema_migration, :id => false) do |t|
128-
t.column :version, :string, :null => false
125+
schema_migration = connection.schema_migration
126+
127+
return if schema_migration.table_exists?
128+
129+
connection.create_table(schema_migration.table_name, id: false) do |t|
130+
t.column :version, :string, null: false
129131
end
130132
end
131133

0 commit comments

Comments
 (0)