为什么 db:structure:load 因访问方法 btree 已存在而 "operator family "btree_hstore_ops 失败”?

Why is db:structure:load failing with "operator family "btree_hstore_ops for access method btree already exists"?

我的 Rails 5.1 CI 测试开始失败并出现以下错误:

bundle exec rake db:create db:structure:load
Created database 'my_test'
psql: .../structure.sql:72: ERROR:  operator family "btree_hstore_ops" for access method "btree" already exists
rake aborted!

我正在将 Rails 应用更新到 5.1 版。

Rails 5.1 似乎对模型索引的定义方式进行了一些更改,包括将以下内容添加到 structure.sql.

CREATE OPERATOR FAMILY btree_hstore_ops USING btree;

CREATE OPERATOR FAMILY gin_hstore_ops USING gin;

CREATE OPERATOR FAMILY gist_hstore_ops USING gist;

CREATE OPERATOR FAMILY hash_hstore_ops USING hash; 

这些似乎是原因。

有人遇到过这个问题吗?有没有办法使 CREATE OPERATOR FAMILY 成为条件并检查 btree_hstore_ops 是否已经存在?或者我应该寻找其他地方来解决这个问题?

编辑:

添加跟踪:

-> rake db:structure:load
Running via Spring preloader in process 78735
psql:/Users/me/code/myapp/db/structure.sql:72: ERROR:  operator family "btree_hstore_ops" for access method "btree" already exists
rake aborted!
failed to execute:
psql -v ON_ERROR_STOP=1 -q -f /Users/me/code/myapp/db/structure.sql mw_development

Please check the output above for any errors and make sure that `psql` is installed in your PATH and has proper permissions.

/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/postgresql_database_tasks.rb:108:in `run_cmd'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/postgresql_database_tasks.rb:80:in `structure_load'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:223:in `structure_load'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:236:in `load_schema'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:255:in `block in load_schema_current'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:305:in `block in each_current_configuration'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:302:in `each'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:302:in `each_current_configuration'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/tasks/database_tasks.rb:254:in `load_schema_current'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activerecord-5.1.5/lib/active_record/railties/databases.rake:290:in `block (3 levels) in <top (required)>'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activesupport-5.1.5/lib/active_support/dependencies.rb:286:in `load'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activesupport-5.1.5/lib/active_support/dependencies.rb:286:in `block in load'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activesupport-5.1.5/lib/active_support/dependencies.rb:258:in `load_dependency'
/Users/me/.rvm/gems/ruby-2.4.3@myapp/gems/activesupport-5.1.5/lib/active_support/dependencies.rb:286:in `load'
-e:1:in `<main>'
Tasks: TOP => db:structure:load
(See full trace by running task with --trace)

此问题与 Rails 无关,与 PostgreSQL 相关。尽管将数据库状态转储为一系列 SQL 命令是复制模式的好方法,并且在 Rails 核心文档中提到了它,但它有点 "blind-shooting",因为它假设您的数据库处于清晰状态,而您并非如此。您面临的问题是您以前可能使用过数据库,并且通过这样做您可能已经创建了您的应用程序所需的一些运算符。如果您选择将数据库状态转储为 SQL 命令,加载这些模式只是执行它们包含的语句的问题。根据定义,这将创建数据库结构的完美副本,但不会在执行前检查您以前的数据库状态。正如错误消息所说,您正在尝试做的事情已经完成。为避免这种情况,将查询更改为使用 ALTER OPERATOR FAMILY ... ADD,如下所示:

ALTER OPERATOR FAMILY btree_hstore_ops USING btree ADD

ALTER OPERATOR FAMILY gin_hstore_ops USING gin ADD

ALTER OPERATOR FAMILY gist_hstore_ops USING gist ADD

ALTER OPERATOR FAMILY hash_hstore_ops USING hash ADD

Documentation states ALTER OPERATOR FAMILY 目前不检查运算符族定义是否包含索引方法所需的所有运算符和函数,也不检查运算符和函数是否构成 self-consistent放。定义有效的运算符系列是用户的责任。