Skip to content

Inconsistent type handling for real columns in PostgreSQL schema dumps and loads #52742

@krororo

Description

@krororo

Steps to reproduce

When dumping a PostgreSQL schema that includes a real column (defined as float(24)), the column is represented as float in the generated schema dump. However, when loading this schema, the real column is converted to double precision.

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "rails", "7.1.4"
  gem "pg"
end

require "active_record"
require "minitest/autorun"
require "logger"

dbconfig = {
  adapter: "postgresql",
  username: "postgres",
  password: "postgres",
  host: "localhost",
  database: "migration_test"
}
ActiveRecord::Base.establish_connection(dbconfig.merge(database: "postgres"))
ActiveRecord::Base.connection.drop_database("migration_test")
ActiveRecord::Base.connection.create_database("migration_test")
ActiveRecord::Base.establish_connection(dbconfig)

ActiveRecord::Base.logger = Logger.new(STDOUT)

class CreatePaymentWithFloatColumn < ActiveRecord::Migration::Current
  def change
    create_table :payments do |t|
      t.float :amount, limit: 24
    end
  end
end

class Payment < ActiveRecord::Base
end

class BugTest < Minitest::Test
  def test_migration_up
    CreatePaymentWithFloatColumn.migrate(:up)
    Payment.reset_column_information

    assert_equal "real", Payment.columns_hash["amount"].sql_type

    io = StringIO.new
    ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, io)
    assert_includes('t.float "amount", limit: 24', io.string)
  end
end

result:

Run options: --seed 55169

# Running:

==  CreatePaymentWithFloatColumn: migrating ===================================
-- create_table(:payments)
D, [2024-08-29T17:34:22.146929 #38268] DEBUG -- :    (2.8ms)  CREATE TABLE "payments" ("id" bigserial primary key, "amount" float(24))
   -> 0.0187s
==  CreatePaymentWithFloatColumn: migrated (0.0187s) ==========================

F

Finished in 0.036045s, 27.7431 runs/s, 83.2293 assertions/s.

  1) Failure:
BugTest#test_migration_up [migration_test.rb:50]:
Expected "t.float \"amount\", limit: 24" to include "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# This file is the source Rails uses to define your schema when running `bin/rails\n# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to\n# be faster and is potentially less error prone than running all of your\n# migrations from scratch. Old migrations may fail to apply correctly if those\n# migrations use external dependencies or application code.\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema[7.2].define(version: 0) do\n  # These are extensions that must be enabled in order to support this database\n  enable_extension \"plpgsql\"\n\n  create_table \"payments\", force: :cascade do |t|\n    t.float \"amount\"\n  end\nend\n".

1 runs, 3 assertions, 1 failures, 0 errors, 0 skips

Expected behavior

A real column defined in a PostgreSQL schema should be preserved as a real column when dumped and loaded.

see: https://www.postgresql.org/docs/14/datatype-numeric.html#DATATYPE-FLOAT

PostgreSQL also supports the SQL-standard notations float and float(p) for specifying inexact numeric types. Here, p specifies the minimum acceptable precision in binary digits. PostgreSQL accepts float(1) to float(24) as selecting the real type, while float(25) to float(53) select double precision. Values of p outside the allowed range draw an error. float with no precision specified is taken to mean double precision.

Actual behavior

The current behavior results in a conversion from real to double precision during the dump and load process.

System configuration

Rails version: 7.1.4

Ruby version: 3.3.4

PostgreSQL version: 14.7

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions