【问题标题】:How do I use an ICU collation as my default in Rails?如何在 Rails 中使用 ICU 排序规则作为我的默认值?
【发布时间】:2020-03-25 19:34:13
【问题描述】:

我正在开发一个 Rails / Postgres 应用程序。我在 Mac 上开发。其他人使用Linux。生产在 Heroku 上。

排序规则在 Mac 上被破坏,所以我得到的排序与 Linux 和 Heroku 略有不同。这会导致涉及排序的测试偶尔失败或行为不一致。解决方法是使用 ICU 排序规则来获得一致的排序,但我不知道如何将其设为默认值。

Postgres will not create a database with an ICU collation。如果我在 database.yml 中将排序规则设置为 en-US-x-icu...

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: <%= ENV['DB_HOST'] || 'localhost' %>
  username: <%= ENV['DB_USER'] || 'postgres' %>
  password: <%= ENV['DB_PASSWORD'] || '' %>
  collation: en-US-x-icu

我收到一个错误,invalid locale name: "en-US-x-icu",尽管它出现在 pg_collation

$ rails db:migrate:reset
Dropped database 'email_integrator_development'
Dropped database 'email_integrator_test'
PG::WrongObjectType: ERROR:  invalid locale name: "en-US-x-icu"
Couldn't create 'email_integrator_development' database. Please check your configuration.
rails aborted!
ActiveRecord::StatementInvalid: PG::WrongObjectType: ERROR:  invalid locale name: "en-US-x-icu"

也许有办法让 Rails 为每个连接或每个表设置排序规则?

我正在使用 Rails 6 和 Postgres 11,但如有必要,我可以迁移到 Postgres 12。

【问题讨论】:

  • 我担心您必须将排序规则添加到每个字符串列定义中。
  • @LaurenzAlbe 我很害怕。我想知道我是否可以在我的 Mac 上修复排序规则。如果您将其发布为答案,我会接受。谢谢你。你知道 Postgres 13 中是否有 ICU 改进吗?

标签: ruby-on-rails postgresql ruby-on-rails-6


【解决方案1】:

恐怕您必须将排序规则添加到每个字符串列定义中。

目前,您不能使用 ICU 排序规则作为数据库默认值。 There have been efforts to improve that,但还没有任何可提交的结果。

但是之后修复数据库很容易。使用psql,可以运行

SELECT format(
          'ALTER TABLE %I.%I ALTER %I TYPE %I%s;',
          table_schema,
          table_name,
          column_name,
          data_type,
          '(' || character_maximum_length || ')'
       )
FROM information_schema.columns
WHERE data_type IN ('character', 'character varying', 'text')
  AND table_schema NOT IN ('pg_catalog', 'information_schema', 'pg_toast') \gexec

【讨论】:

  • 感谢您的回答和脚本。我假设这会重建索引?
【解决方案2】:

你可以猴子补丁 ActiveRecord:

module ActiveRecord
  module ConnectionAdapters
    module PostgreSQL
      module ColumnMethods
        def new_column_definition(name, type, **options) # :nodoc:
          if integer_like_primary_key?(type, options)
            type = integer_like_primary_key_type(type, options)
          end
          type = aliased_types(type.to_s, type)
          options[:primary_key] ||= type == :primary_key
          options[:null] = false if options[:primary_key]
          # set default collation for a column if not set explicitly
          options[:collation] = "en-US-x-icu" if options[:collation].blank? && (type == :string || type == :text)
          create_column_definition(name, type, options)
        end
      end
    end
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 2011-02-19
    • 2018-12-19
    • 2023-03-22
    • 1970-01-01
    • 2010-10-19
    相关资源
    最近更新 更多