aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/adapters/postgresql/connection_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/adapters/postgresql/connection_test.rb')
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb272
1 files changed, 272 insertions, 0 deletions
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
new file mode 100644
index 0000000000..d1b3c434e1
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -0,0 +1,272 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "support/connection_helper"
+
+module ActiveRecord
+ class PostgresqlConnectionTest < ActiveRecord::PostgreSQLTestCase
+ include ConnectionHelper
+
+ class NonExistentTable < ActiveRecord::Base
+ end
+
+ fixtures :comments
+
+ def setup
+ super
+ @subscriber = SQLSubscriber.new
+ @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", @subscriber)
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ ActiveSupport::Notifications.unsubscribe(@subscription)
+ super
+ end
+
+ def test_truncate
+ count = ActiveRecord::Base.connection.execute("select count(*) from comments").first["count"].to_i
+ assert_operator count, :>, 0
+ ActiveRecord::Base.connection.truncate("comments")
+ count = ActiveRecord::Base.connection.execute("select count(*) from comments").first["count"].to_i
+ assert_equal 0, count
+ end
+
+ def test_encoding
+ assert_queries(1) do
+ assert_not_nil @connection.encoding
+ end
+ end
+
+ def test_collation
+ assert_queries(1) do
+ assert_not_nil @connection.collation
+ end
+ end
+
+ def test_ctype
+ assert_queries(1) do
+ assert_not_nil @connection.ctype
+ end
+ end
+
+ def test_default_client_min_messages
+ assert_equal "warning", @connection.client_min_messages
+ end
+
+ # Ensure, we can set connection params using the example of Generic
+ # Query Optimizer (geqo). It is 'on' per default.
+ def test_connection_options
+ params = ActiveRecord::Base.connection_config.dup
+ params[:options] = "-c geqo=off"
+ NonExistentTable.establish_connection(params)
+
+ # Verify the connection param has been applied.
+ expect = NonExistentTable.connection.query("show geqo").first.first
+ assert_equal "off", expect
+ end
+
+ def test_reset
+ @connection.query("ROLLBACK")
+ @connection.query("SET geqo TO off")
+
+ # Verify the setting has been applied.
+ expect = @connection.query("show geqo").first.first
+ assert_equal "off", expect
+
+ @connection.reset!
+
+ # Verify the setting has been cleared.
+ expect = @connection.query("show geqo").first.first
+ assert_equal "on", expect
+ end
+
+ def test_reset_with_transaction
+ @connection.query("ROLLBACK")
+ @connection.query("SET geqo TO off")
+
+ # Verify the setting has been applied.
+ expect = @connection.query("show geqo").first.first
+ assert_equal "off", expect
+
+ @connection.query("BEGIN")
+ @connection.reset!
+
+ # Verify the setting has been cleared.
+ expect = @connection.query("show geqo").first.first
+ assert_equal "on", expect
+ end
+
+ def test_tables_logs_name
+ @connection.tables
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ def test_indexes_logs_name
+ @connection.indexes("items")
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ def test_table_exists_logs_name
+ @connection.table_exists?("items")
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ def test_table_alias_length_logs_name
+ @connection.instance_variable_set("@max_identifier_length", nil)
+ @connection.table_alias_length
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ def test_current_database_logs_name
+ @connection.current_database
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ def test_encoding_logs_name
+ @connection.encoding
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ def test_schema_names_logs_name
+ @connection.schema_names
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
+ end
+
+ if ActiveRecord::Base.connection.prepared_statements
+ def test_statement_key_is_logged
+ bind = Relation::QueryAttribute.new(nil, 1, Type::Value.new)
+ @connection.exec_query("SELECT $1::integer", "SQL", [bind], prepare: true)
+ name = @subscriber.payloads.last[:statement_name]
+ assert name
+ res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(1)")
+ plan = res.column_types["QUERY PLAN"].deserialize res.rows.first.first
+ assert_operator plan.length, :>, 0
+ end
+ end
+
+ # Must have PostgreSQL >= 9.2, or with_manual_interventions set to
+ # true for this test to run.
+ #
+ # When prompted, restart the PostgreSQL server with the
+ # "-m fast" option or kill the individual connection assuming
+ # you know the incantation to do that.
+ # To restart PostgreSQL 9.1 on OS X, installed via MacPorts, ...
+ # sudo su postgres -c "pg_ctl restart -D /opt/local/var/db/postgresql91/defaultdb/ -m fast"
+ def test_reconnection_after_actual_disconnection_with_verify
+ original_connection_pid = @connection.query("select pg_backend_pid()")
+
+ # Sanity check.
+ assert_predicate @connection, :active?
+
+ if @connection.send(:postgresql_version) >= 90200
+ secondary_connection = ActiveRecord::Base.connection_pool.checkout
+ secondary_connection.query("select pg_terminate_backend(#{original_connection_pid.first.first})")
+ ActiveRecord::Base.connection_pool.checkin(secondary_connection)
+ elsif ARTest.config["with_manual_interventions"]
+ puts "Kill the connection now (e.g. by restarting the PostgreSQL " \
+ 'server with the "-m fast" option) and then press enter.'
+ $stdin.gets
+ else
+ # We're not capable of terminating the backend ourselves, and
+ # we're not allowed to seek assistance; bail out without
+ # actually testing anything.
+ return
+ end
+
+ @connection.verify!
+
+ assert_predicate @connection, :active?
+
+ # If we get no exception here, then either we re-connected successfully, or
+ # we never actually got disconnected.
+ new_connection_pid = @connection.query("select pg_backend_pid()")
+
+ assert_not_equal original_connection_pid, new_connection_pid,
+ "umm -- looks like you didn't break the connection, because we're still " \
+ "successfully querying with the same connection pid."
+ ensure
+ # Repair all fixture connections so other tests won't break.
+ @fixture_connections.each(&:verify!)
+ end
+
+ def test_set_session_variable_true
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: true }))
+ set_true = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN"
+ assert_equal set_true.rows, [["on"]]
+ end
+ end
+
+ def test_set_session_variable_false
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: false }))
+ set_false = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN"
+ assert_equal set_false.rows, [["off"]]
+ end
+ end
+
+ def test_set_session_variable_nil
+ run_without_connection do |orig_connection|
+ # This should be a no-op that does not raise an error
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: nil }))
+ end
+ end
+
+ def test_set_session_variable_default
+ run_without_connection do |orig_connection|
+ # This should execute a query that does not raise an error
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: :default }))
+ end
+ end
+
+ def test_set_session_timezone
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { timezone: "America/New_York" }))
+ assert_equal "America/New_York", ActiveRecord::Base.connection.query_value("SHOW TIME ZONE")
+ end
+ end
+
+ def test_get_and_release_advisory_lock
+ lock_id = 5295901941911233559
+ list_advisory_locks = <<-SQL
+ SELECT locktype,
+ (classid::bigint << 32) | objid::bigint AS lock_id
+ FROM pg_locks
+ WHERE locktype = 'advisory'
+ SQL
+
+ got_lock = @connection.get_advisory_lock(lock_id)
+ assert got_lock, "get_advisory_lock should have returned true but it didn't"
+
+ advisory_lock = @connection.query(list_advisory_locks).find { |l| l[1] == lock_id }
+ assert advisory_lock,
+ "expected to find an advisory lock with lock_id #{lock_id} but there wasn't one"
+
+ released_lock = @connection.release_advisory_lock(lock_id)
+ assert released_lock, "expected release_advisory_lock to return true but it didn't"
+
+ advisory_locks = @connection.query(list_advisory_locks).select { |l| l[1] == lock_id }
+ assert_empty advisory_locks,
+ "expected to have released advisory lock with lock_id #{lock_id} but it was still held"
+ end
+
+ def test_release_non_existent_advisory_lock
+ fake_lock_id = 2940075057017742022
+ with_warning_suppression do
+ released_non_existent_lock = @connection.release_advisory_lock(fake_lock_id)
+ assert_equal released_non_existent_lock, false,
+ "expected release_advisory_lock to return false when there was no lock to release"
+ end
+ end
+
+ private
+
+ def with_warning_suppression
+ log_level = @connection.client_min_messages
+ @connection.client_min_messages = "error"
+ yield
+ @connection.client_min_messages = log_level
+ end
+ end
+end