我在使用Postgresql 13的Rails 7应用程序中进行了此迁移:
class AddContractsDateRangeConstraint < ActiveRecord::Migration[7.0]
def up
execute "CREATE EXTENSION IF NOT EXISTS btree_gist"
execute <<~SQL
ALTER TABLE contracts
ADD CONSTRAINT date_overlap_exclude
EXCLUDE USING GIST (
employee_id WITH =,
daterange(starts_on, ends_on) WITH &&
)
SQL
end
def down
execute "ALTER TABLE contracts DROP CONSTRAINT date_overlap_exclude"
end
end
当通过rails db:migrate
执行时,约束像这样被添加到DB中,一切都工作:
app_development=# d+ contracts
...
Indexes:
...
"date_overlap_exclude" EXCLUDE USING gist (employee_id WITH =, daterange(starts_on, ends_on) WITH &&)
...
从Rails生成的schema.rb
看起来像这样:
create_table "contracts", force: :cascade do |t|
# ...
t.index "employee_id, daterange(starts_on, ends_on)", name: "date_overlap_exclude", using: :gist
# ...
end
这看起来很可疑,因为它缺少我约束的整个EXCLUDE
部分。实际上,当使用rails db:schema:load
从生成的模式创建数据库时,约束被打破,生成如下:
app_development=# d+ contracts
...
Indexes:
"contracts_pkey" PRIMARY KEY, btree (id)
"date_overlap_exclude" gist (employee_id, daterange(starts_on, ends_on))
当然,整个约束不再有效。知道怎么解吗?
Rails Ruby模式转储器对于简单的应用程序和用例工作得非常好,但是它实际上只能理解SQL的一个非常有限的子集。正如您在这里所经历的那样,数据库模式中任何无法理解的部分都将在翻译中丢失。
一旦你需要使用函数、约束、物化视图或任何其他数据库特定的特性,你就需要使用SQL模式转储,它的保真度要高得多,因为它只是使用数据库自己的转储工具将模式输出为SQL。当您失去模式的简单性和与数据库无关的特性时。随着应用程序复杂度的增加,这是不可避免的变化。
您可以在配置中使用简单的一行来切换模式格式:
module YourApp
class Application < Rails::Application
# Add this line:
config.active_record.schema_format = :sql
end
end
当你运行rails db:schema:dump
时,它现在会输出一个structure.sql
文件。此时,您应该删除schema.rb
并将structure.sql
检入版本控制。
:
- 使用结构的利弊。sql在你的Ruby on Rails应用程序
- Rails指南:Schema dump and You