Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable database prepared statements when query logs are enabled #48631

Merged
merged 1 commit into from Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions activerecord/CHANGELOG.md
@@ -1,3 +1,9 @@
* Disable database prepared statements when query logs are enabled

Prepared Statements and Query Logs are incompatible features due to query logs making every query unique.

*zzak, Jean Boussier*

* Support decrypting data encrypted non-deterministically with a SHA1 hash digest.

This adds a new Active Record encryption option to support decrypting data encrypted
Expand Down
3 changes: 3 additions & 0 deletions activerecord/lib/active_record.rb
Expand Up @@ -174,6 +174,9 @@ module Tasks
autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
end

singleton_class.attr_accessor :disable_prepared_statements
self.disable_prepared_statements = false

# Lazily load the schema cache. This option will load the schema cache
# when a connection is established rather than on boot. If set,
# +config.active_record.use_schema_cache_dump+ will be set to false.
Expand Down
Expand Up @@ -160,7 +160,7 @@ def initialize(config_or_deprecated_connection, deprecated_logger = nil, depreca
@statements = build_statement_pool
self.lock_thread = nil

@prepared_statements = self.class.type_cast_config_to_boolean(
@prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
@config.fetch(:prepared_statements) { default_prepared_statements }
)

Expand Down
1 change: 1 addition & 0 deletions activerecord/lib/active_record/railtie.rb
Expand Up @@ -396,6 +396,7 @@ class Railtie < Rails::Railtie # :nodoc:
db_host: ->(context) { context[:connection].pool.db_config.host },
database: ->(context) { context[:connection].pool.db_config.database }
)
ActiveRecord.disable_prepared_statements = true

if app.config.active_record.query_log_tags.present?
ActiveRecord::QueryLogs.tags = app.config.active_record.query_log_tags
Expand Down
17 changes: 17 additions & 0 deletions activerecord/test/cases/adapter_test.rb
Expand Up @@ -158,6 +158,23 @@ def test_not_specifying_database_name_for_cross_database_selects
end
end

unless in_memory_db?
def test_disable_prepared_statements
original_prepared_statements = ActiveRecord.disable_prepared_statements
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
ActiveRecord::Base.establish_connection(db_config.configuration_hash.merge(prepared_statements: true))

assert_predicate ActiveRecord::Base.connection, :prepared_statements?

ActiveRecord.disable_prepared_statements = true
ActiveRecord::Base.establish_connection(db_config.configuration_hash.merge(prepared_statements: true))
assert_not_predicate ActiveRecord::Base.connection, :prepared_statements?
ensure
ActiveRecord.disable_prepared_statements = original_prepared_statements
ActiveRecord::Base.establish_connection :arunit
end
end

def test_table_alias
def @connection.test_table_alias_length() 10; end
class << @connection
Expand Down
2 changes: 2 additions & 0 deletions guides/source/configuring.md
Expand Up @@ -1345,6 +1345,8 @@ The default value depends on the `config.load_defaults` target version:
Specifies whether or not to enable adapter-level query comments. Defaults to
`false`.

NOTE: When this is set to `true` database prepared statements will be automatically disabled.

#### `config.active_record.query_log_tags`

Define an `Array` specifying the key/value tags to be inserted in an SQL
Expand Down
13 changes: 13 additions & 0 deletions railties/test/application/query_logs_test.rb
Expand Up @@ -10,6 +10,7 @@ class QueryLogsTest < ActiveSupport::TestCase

def setup
build_app(multi_db: true)
add_to_config "config.active_record.sqlite3_production_warning = false"
rails("generate", "scaffold", "Pet", "name:string", "--database=animals")
app_file "app/models/user.rb", <<-RUBY
class User < ActiveRecord::Base
Expand Down Expand Up @@ -78,8 +79,20 @@ def app
assert_includes ActiveRecord.query_transformers, ActiveRecord::QueryLogs
end

test "disables prepared statements when enabled" do
add_to_config "config.active_record.query_log_tags_enabled = true"

boot_app

assert_predicate ActiveRecord, :disable_prepared_statements
end

test "controller and job tags are defined by default" do
add_to_config "config.active_record.query_log_tags_enabled = true"
app_file "config/initializers/active_record.rb", <<-RUBY
raise "Expected prepared_statements to be enabled" unless ActiveRecord::Base.connection.prepared_statements
ActiveRecord::Base.connection.execute("SELECT 1")
RUBY

boot_app

Expand Down