Add active_record.postgresql_adapter_decode_dates

to toggle automatic decoding of dates column with the
PostgresqlAdapter.

PR #51483 is a breaking change and should have been gated behind a
config.
This commit is contained in:
Joé Dupuis 2024-05-08 16:37:33 -07:00 committed by Rafael Mendonça França
parent 72fccfb5d6
commit 478874ae1b
No known key found for this signature in database
GPG Key ID: FC23B6D0F1EEE948
7 changed files with 103 additions and 8 deletions

@ -122,6 +122,15 @@ def dbconsole(config, options = {})
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
class_attribute :datetime_type, default: :timestamp
##
# :singleton-method:
# Toggles automatic decoding of date columns.
#
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
class_attribute :decode_dates, default: false
NATIVE_DATABASE_TYPES = {
primary_key: "bigserial primary key",
string: { name: "character varying" },
@ -1159,8 +1168,8 @@ def add_pg_decoders
"bool" => PG::TextDecoder::Boolean,
"timestamp" => PG::TextDecoder::TimestampUtc,
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
"date" => PG::TextDecoder::Date,
}
coders_by_name["date"] = PG::TextDecoder::Date if decode_dates
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
query = <<~SQL % known_coder_types.join(", ")

@ -217,6 +217,16 @@ class Railtie < Rails::Railtie # :nodoc:
end
end
initializer "active_record.postgresql_adapter_decode_dates" do
config.after_initialize do
if config.active_record.postgresql_adapter_decode_dates
ActiveSupport.on_load(:active_record_postgresqladapter) do
self.decode_dates = true
end
end
end
end
initializer "active_record.set_configs" do |app|
configs = app.config.active_record
@ -245,7 +255,8 @@ class Railtie < Rails::Railtie # :nodoc:
:cache_query_log_tags,
:sqlite3_adapter_strict_strings_by_default,
:check_schema_cache_dump_version,
:use_schema_cache_dump
:use_schema_cache_dump,
:postgresql_adapter_decode_dates,
)
configs.each do |k, v|

@ -39,10 +39,4 @@ def test_bc_date_year_zero
topic = Topic.create!(last_read: date)
assert_equal date, Topic.find(topic.id).last_read
end
def test_date_decoder
date = ActiveRecord::Base.connection.select_value("select '2024-01-01'::date")
assert_equal Date.new(2024, 01, 01), date
assert_equal Date, date.class
end
end

@ -649,7 +649,34 @@ def test_does_not_raise_notice_level_warnings
end
end
def test_date_decoding_enabled
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
connection = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.new(db_config.configuration_hash)
with_postgresql_apdater_decode_dates do
date = connection.select_value("select '2024-01-01'::date")
assert_equal Date.new(2024, 01, 01), date
assert_equal Date, date.class
end
end
def test_date_decoding_disabled
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
connection = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.new(db_config.configuration_hash)
date = connection.select_value("select '2024-01-01'::date")
assert_equal "2024-01-01", date
assert_equal String, date.class
end
private
def with_postgresql_apdater_decode_dates
PostgreSQLAdapter.decode_dates = true
yield
ensure
PostgreSQLAdapter.decode_dates = false
end
def with_example_table(definition = "id serial primary key, number integer, data character varying(255)", &block)
super(@connection, "ex", definition, &block)
end

@ -62,6 +62,7 @@ Below are the default values associated with each target version. In cases of co
- [`config.active_job.enqueue_after_transaction_commit`](#config-active-job-enqueue-after-transaction-commit): `:default`
- [`config.active_record.automatically_invert_plural_associations`](#config-active-record-automatically-invert-plural-associations): `true`
- [`config.active_record.postgresql_adapter_decode_dates`](#config-active-record-postgresql-adapter-decode-dates): `true`
- [`config.active_record.validate_migration_timestamps`](#config-active-record-validate-migration-timestamps): `true`
- [`config.active_storage.web_image_content_types`](#config-active-storage-web-image-content-types): `%w[image/png image/jpeg image/gif image/webp]`
@ -1490,6 +1491,24 @@ The default value depends on the `config.load_defaults` target version:
| (original) | `false` |
| 7.1 | `true` |
#### `config.active_record.postgresql_adapter_decode_dates`
Specifies whether the PostgresqlAdapter should decode date columns.
```ruby
ActiveRecord::Base.connection
.select_value("select '2024-01-01'::date").class #=> Date
```
The default value depends on the `config.load_defaults` target version:
| Starting with version | The default value is |
| --------------------- | -------------------- |
| (original) | `false` |
| 7.2 | `true` |
#### `config.active_record.async_query_executor`
Specifies how asynchronous queries are pooled.

@ -327,6 +327,7 @@ def load_defaults(target_version)
end
if respond_to?(:active_record)
active_record.postgresql_adapter_decode_dates = true
active_record.validate_migration_timestamps = true
active_record.automatically_invert_plural_associations = true
end

@ -2793,6 +2793,40 @@ def index
assert_equal false, ActiveRecord::Base.run_commit_callbacks_on_first_saved_instances_in_transaction
end
test "PostgresqlAdapter.decode_dates is true by default for new apps" do
app_file "config/initializers/active_record.rb", <<~RUBY
ActiveRecord::Base.establish_connection(adapter: "postgresql")
RUBY
app "development"
assert_equal true, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates
end
test "PostgresqlAdapter.decode_dates is false by default for upgraded apps" do
remove_from_config '.*config\.load_defaults.*\n'
app_file "config/initializers/active_record.rb", <<~RUBY
ActiveRecord::Base.establish_connection(adapter: "postgresql")
RUBY
app "development"
assert_equal false, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates
end
test "PostgresqlAdapter.decode_dates can be configured via config.active_record.postgresql_adapter_decode_dates" do
remove_from_config '.*config\.load_defaults.*\n'
add_to_config "config.active_record.postgresql_adapter_decode_dates = true"
app_file "config/initializers/active_record.rb", <<~RUBY
ActiveRecord::Base.establish_connection(adapter: "postgresql")
RUBY
app "development"
assert_equal true, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates
end
test "SQLite3Adapter.strict_strings_by_default is true by default for new apps" do
app_file "config/initializers/active_record.rb", <<~RUBY
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")