Skip to content

Commit

Permalink
Merge pull request #1153 from JesseChavez/rails_71_support_arjdbc_pos…
Browse files Browse the repository at this point in the history
…tgres

Fix some arjdbc postgres tests to support AR 7.1
  • Loading branch information
enebo authored Jul 31, 2024
2 parents 8029660 + 2d3aa77 commit 81506f0
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 55 deletions.
2 changes: 1 addition & 1 deletion lib/arjdbc/abstract/connection_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def disconnect!
# DIFFERENCE: we delve into jdbc shared code and this does self.class.new_client.
def connect
@raw_connection = self.class.new_client(@connection_parameters, self)
rescue ConnectionNotEstablished => ex
rescue ActiveRecord::ConnectionNotEstablished => ex
raise ex.set_pool(@pool)
end

Expand Down
9 changes: 6 additions & 3 deletions lib/arjdbc/postgresql/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
require 'active_record/connection_adapters/postgresql/schema_statements'
require 'active_record/connection_adapters/postgresql/type_metadata'
require 'active_record/connection_adapters/postgresql/utils'

require 'arjdbc/abstract/core'
require 'arjdbc/abstract/connection_management'
require 'arjdbc/abstract/database_statements'
Expand All @@ -21,6 +22,8 @@
require 'arjdbc/postgresql/base/array_decoder'
require 'arjdbc/postgresql/base/array_encoder'
require 'arjdbc/postgresql/name'
require 'arjdbc/postgresql/schema_statements'

require 'active_model'

module ArJdbc
Expand Down Expand Up @@ -97,8 +100,7 @@ def configure_connection
end
end

@type_map = Type::HashLookupTypeMap.new
initialize_type_map
reload_type_map
end

# @private
Expand All @@ -125,7 +127,7 @@ def configure_connection
inet: { name: 'inet' },
int4range: { name: 'int4range' },
int8range: { name: 'int8range' },
integer: { name: 'integer' },
integer: { name: 'integer', limit: 4 },
interval: { name: 'interval' },
json: { name: 'json' },
jsonb: { name: 'jsonb' },
Expand Down Expand Up @@ -752,6 +754,7 @@ class PostgreSQLAdapter < AbstractAdapter

require 'arjdbc/postgresql/oid_types'
include ::ArJdbc::PostgreSQL::OIDTypes
include ::ArJdbc::PostgreSQL::SchemaStatements

include ::ArJdbc::PostgreSQL::ColumnHelpers

Expand Down
13 changes: 8 additions & 5 deletions lib/arjdbc/postgresql/oid_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,15 @@ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
end

def reload_type_map
type_map.clear
@lock.synchronize do
if @type_map
type_map.clear
else
@type_map = Type::HashLookupTypeMap.new
end

initialize_type_map
end
end

def initialize_type_map_inner(m)
Expand Down Expand Up @@ -274,10 +281,6 @@ def extract_precision(sql_type)
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
end

def extract_limit(sql_type)
$1.to_i if sql_type =~ /\((.*)\)/
end

# Support arrays/ranges for defining attributes that don't exist in the db
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
Expand Down
57 changes: 57 additions & 0 deletions lib/arjdbc/postgresql/schema_statements.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

module ArJdbc
module PostgreSQL
module SchemaStatements
ForeignKeyDefinition = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
Utils = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils

def foreign_keys(table_name)
scope = quoted_scope(table_name)
fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
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
FROM pg_constraint c
JOIN pg_class t1 ON c.conrelid = t1.oid
JOIN pg_class t2 ON c.confrelid = t2.oid
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
JOIN pg_namespace t3 ON c.connamespace = t3.oid
WHERE c.contype = 'f'
AND t1.relname = #{scope[:name]}
AND t3.nspname = #{scope[:schema]}
ORDER BY c.conname
SQL

fk_info.map do |row|
to_table = Utils.unquote_identifier(row["to_table"])
# conkey = row["conkey"].scan(/\d+/).map(&:to_i)
# confkey = row["confkey"].scan(/\d+/).map(&:to_i)
conkey = row["conkey"]
confkey = row["confkey"]

if conkey.size > 1
column = column_names_from_column_numbers(row["conrelid"], conkey)
primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
else
column = Utils.unquote_identifier(row["column"])
primary_key = row["primary_key"]
end

options = {
column: column,
name: row["name"],
primary_key: primary_key
}

options[:on_delete] = extract_foreign_key_action(row["on_delete"])
options[:on_update] = extract_foreign_key_action(row["on_update"])
options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])

options[:validate] = row["valid"]

ForeignKeyDefinition.new(table_name, to_table, options)
end
end
end
end
end
16 changes: 8 additions & 8 deletions test/db/postgresql/hstore_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class PostgreSQLHstoreTest < Test::Unit::TestCase

class Hstore < ActiveRecord::Base
self.table_name = 'hstores'
store :tags, :accessors => [ :name ]

store_accessor :tags, [:name]
end

def setup
Expand Down Expand Up @@ -36,18 +37,17 @@ def teardown

def test_parse
column_type = Hstore.type_for_attribute('tags')
assert_equal({}, column_type.deserialize(''))
assert_equal({'1' => '2'}, column_type.deserialize('"1"=>"2"'))
assert_equal({'key'=>nil}, column_type.deserialize('key => NULL'))
assert_equal({'c'=>'}','"a"'=>'b "a b'}, column_type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))

assert_equal({}, column_type.deserialize(''))
assert_equal({ '1' => '2' }, column_type.deserialize('"1"=>"2"'))
end

def test_store_select
@connection.execute "insert into hstores (tags) VALUES ('name=>ferko,type=>suska')"
x = Hstore.first

assert_equal 'ferko', x.name
assert_equal 'suska', x.tags[:type]
assert_instance_of ActiveSupport::HashWithIndifferentAccess, x.tags
assert_equal 'suska', x.tags['type']
assert_instance_of Hash, x.tags
end

end
37 changes: 7 additions & 30 deletions test/db/postgresql/jdbc_postgres_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

module Jdbc
class PostgresTest < Test::Unit::TestCase

SKIP_TESTS = false
SYSTEM_ENV = ENV_JAVA.dup

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

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

test 'returns jdbc version 4.1 on java 7' do
ENV_JAVA[ 'java.specification.version' ] = '1.7'
assert_equal 7, Jdbc::Postgres.send(:jre_version)
end unless SKIP_TESTS

test 'returns jdbc version default (4.2) on java 8/9' do
ENV_JAVA[ 'java.specification.version' ] = '1.8'
assert_nil Jdbc::Postgres.send(:jre_version)
test 'returns jdbc version default (4.2) on java 8' do
ENV_JAVA['java.specification.version'] = '1.8'

ENV_JAVA[ 'java.specification.version' ] = '9'
assert_nil Jdbc::Postgres.send(:jre_version)
end unless SKIP_TESTS

context 'load-driver' do

ROOT_DIR = File.expand_path('../../..', File.dirname(__FILE__))

@@driver_dir = File.join(ROOT_DIR, 'jdbc-postgres/lib')
end

test 'on java 7' do
ENV_JAVA[ 'java.specification.version' ] = '1.7'
Jdbc::Postgres.expects(:load).with do |driver_jar|
assert_match(/.jre7.jar$/, driver_jar)
full_path = File.join(@@driver_dir, driver_jar)
assert File.exist?(full_path), "#{driver_jar.inspect} not found in: #{@@driver_dir.inspect}"
true
end
Jdbc::Postgres.load_driver
end
test 'returns jdbc version 4.2 on java 11' do
ENV_JAVA['java.specification.version'] = '11'

assert_nil Jdbc::Postgres.send(:jre_version)
end

end if defined? JRUBY_VERSION
end
end
13 changes: 10 additions & 3 deletions test/db/postgresql/native_types_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def self.up
"bigint_serial_should_be_integer bigint default nextval('seq_pk_customers')",
"integer_serial_should_be_integer integer default nextval('seq_pk_customers')",
"varchar_should_be_string varchar(2)",
"timestamp_should_be_datetime timestamp",
"timestamp_should_be_timestamp timestamp",
"timestamptz_should_be_timestamptz timestamptz",
"bytea_should_be_binary bytea",
"double_precision_should_be_float double precision",
"real_should_be_float real",
Expand Down Expand Up @@ -69,8 +70,14 @@ def test_varchar_should_be_mapped_to_string
assert_instance_of ActiveModel::Type::String, column_type("varchar_should_be_string")
end

def test_timestamp_should_be_mapped_to_datetime
assert_instance_of OID::DateTime, column_type("timestamp_should_be_datetime")
def test_timestamp_should_be_mapped_to_timestamp
assert_kind_of OID::DateTime, column_type("timestamp_should_be_timestamp")
assert_instance_of OID::Timestamp, column_type("timestamp_should_be_timestamp")
end

def test_timestamptz_should_be_mapped_to_timestamptz
assert_kind_of OID::DateTime, column_type("timestamptz_should_be_timestamptz")
assert_instance_of OID::TimestampWithTimeZone, column_type("timestamptz_should_be_timestamptz")
end

def test_bytea_should_be_mapped_to_binary
Expand Down
1 change: 1 addition & 0 deletions test/db/postgresql/schema_dump_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def test_schema_dump_should_not_have_limits_on_text_or_date
def test_schema_dump_integer_with_no_limit_should_have_no_limit
dump = dump_with_data_types
lines = dump.lines.grep(/sample_integer_no_limit/)

assert ! lines.empty?
lines.each { |line| assert line !~ /:limit|limit:/ }
end
Expand Down
22 changes: 22 additions & 0 deletions test/db/postgresql/simple_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,27 @@ def test_custom_select_date
assert_equal my_date, sample_date
end

# @override
def test_big_decimal
test_value = BigDecimal('9876543210_9876543210_9876543210.0')

conn = DbType.connection

if conn.prepared_statements?
db_type = DbType.create!(big_decimal: test_value)
db_type = DbType.find(db_type.id)
assert_kind_of Integer, db_type.big_decimal
assert_equal test_value, db_type.big_decimal
else
# it seems the patch applies when prepared statements is disabled
# https://discuss.rubyonrails.org/t/cve-2022-44566-possible-denial-of-service-vulnerability-in-activerecords-postgresql-adapter/82119
# https://github.com/rails/rails/commit/4f44aa9d514e701ada92b5cf08beccf566eeaebf
assert_raise ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting::IntegerOutOf64BitRange do
DbType.create!(big_decimal: test_value)
end
end
end

def test_encoding
assert_not_nil connection.encoding
end
Expand Down Expand Up @@ -344,6 +365,7 @@ def test_foreign_keys
t.string :title
t.references :db_type, :index => true, :foreign_key => true
end

assert_equal 1, connection.foreign_keys('db_posts').size
assert_equal 'db_posts', connection.foreign_keys('db_posts')[0].from_table
assert_equal 'db_types', connection.foreign_keys('db_posts')[0].to_table
Expand Down
2 changes: 1 addition & 1 deletion test/db/postgresql/table_name_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ class SerialWithTrigger < ActiveRecord::Base;

test 'serial with trigger' do
sn = SerialWithTrigger.create! :value => 1234567890.to_s
assert sn.reload

assert sn.reload
SerialWithTrigger.columns
end

Expand Down
10 changes: 6 additions & 4 deletions test/rake_test_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,12 @@ def new_application
# (Test) Helpers :

def create_schema_migrations_table(connection = ActiveRecord::Base.connection)
schema_migration = ActiveRecord::SchemaMigration.table_name
return if connection.data_source_exists?(schema_migration)
connection.create_table(schema_migration, :id => false) do |t|
t.column :version, :string, :null => false
schema_migration = connection.schema_migration

return if schema_migration.table_exists?

connection.create_table(schema_migration.table_name, id: false) do |t|
t.column :version, :string, null: false
end
end

Expand Down

0 comments on commit 81506f0

Please sign in to comment.