PG::UndefinedObject: ERROR: operator class "pgroonga_text_term_search_ops" does not exist for access method "pgroonga"
前言
筆者的環境如下:
- Ubuntu 20.04
- ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
- Rails 5.2.4.2
- Redmine 4.1.1
- 為Redmine安裝了full_text_search插件
這是筆者在redmine根目錄裡執行:
RAILS_ENV=production rake db:reset DISABLE_DATABASE_ENVIRONMENT_CHECK=1 --trace
時所出現的錯。
錯誤訊息
錯誤訊息如下(重點已用螢光標註):
Caused by:
PG::UndefinedObject: ERROR: operator class “pgroonga_text_term_search_ops” does not exist for access method “pgroonga”
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:75:inasync_exec' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:75:in
block (2 levels) in execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies/interlock.rb:48:inblock in permit_concurrent_loads' /usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/concurrency/share_lock.rb:187:in
yield_shares’
/usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies/interlock.rb:47:inpermit_concurrent_loads' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:74:in
block in execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract_adapter.rb:581:inblock (2 levels) in log' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract_adapter.rb:580:in
synchronize’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract_adapter.rb:580:inblock in log' /usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/notifications/instrumenter.rb:23:in
instrument’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract_adapter.rb:571:inlog' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:73:in
execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/postgresql/schema_statements.rb:466:inadd_index' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract/schema_statements.rb:315:in
block in create_table’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract/schema_statements.rb:314:ineach' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/connection_adapters/abstract/schema_statements.rb:314:in
create_table’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/migration.rb:871:inblock in method_missing' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/migration.rb:840:in
block in say_with_time’
/usr/share/rvm/rubies/ruby-2.7.0/lib/ruby/2.7.0/benchmark.rb:293:inmeasure' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/migration.rb:840:in
say_with_time’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/migration.rb:860:inmethod_missing' /home/redmine/redmine/db/schema.rb:405:in
block in <top (required)>’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/schema.rb:50:ininstance_eval' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/schema.rb:50:in
define’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/schema.rb:46:indefine' /home/redmine/redmine/db/schema.rb:13:in
<top (required)>’
/usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies.rb:285:inload' /usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies.rb:285:in
block in load’
/usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies.rb:257:inload_dependency' /usr/share/rvm/gems/ruby-2.7.0/gems/activesupport-5.2.4.2/lib/active_support/dependencies.rb:285:in
load’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/tasks/database_tasks.rb:245:inload_schema' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/tasks/database_tasks.rb:266:in
block in load_schema_current’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/tasks/database_tasks.rb:316:inblock in each_current_configuration' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/tasks/database_tasks.rb:313:in
each’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/tasks/database_tasks.rb:313:ineach_current_configuration' /usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/tasks/database_tasks.rb:265:in
load_schema_current’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/railties/databases.rake:258:inblock (3 levels) in <top (required)>' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:281:in
block in execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:281:ineach' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:281:in
execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:219:inblock in invoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:in
synchronize’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:ininvoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:188:in
invoke’
/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-5.2.4.2/lib/active_record/railties/databases.rake:262:inblock (3 levels) in <top (required)>' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:281:in
block in execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:281:ineach' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:281:in
execute’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:219:inblock in invoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:in
synchronize’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:ininvoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:243:in
block in invoke_prerequisites’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:241:ineach' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:241:in
invoke_prerequisites’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:218:inblock in invoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:in
synchronize’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:ininvoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:243:in
block in invoke_prerequisites’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:241:ineach' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:241:in
invoke_prerequisites’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:218:inblock in invoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:in
synchronize’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:199:ininvoke_with_call_chain' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/task.rb:188:in
invoke’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:160:ininvoke_task' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:116:in
block (2 levels) in top_level’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:116:ineach' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:116:in
block in top_level’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:125:inrun_with_threads' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:110:in
top_level’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:83:inblock in run' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:186:in
standard_exception_handling’
/usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/lib/rake/application.rb:80:inrun' /usr/share/rvm/gems/ruby-2.7.0/gems/rake-13.0.3/exe/rake:27:in
<top (required)>’
/usr/share/rvm/gems/ruby-2.7.0/bin/rake:23:inload' /usr/share/rvm/gems/ruby-2.7.0/bin/rake:23:in
<main>’
/usr/share/rvm/gems/ruby-2.7.0/bin/ruby_executable_hooks:24:ineval' /usr/share/rvm/gems/ruby-2.7.0/bin/ruby_executable_hooks:24:in
<main>’
Tasks: TOP => db:schema:load
從錯誤訊息的第一行可以看出是跟pgroonga_text_term_search_ops
有關。在<redmine_home>裡尋找線索:
grep pgroonga_text_term_search_ops -rn .
./db/schema.rb:410: t.index [“source”, “destination”], name: “fts_query_expansions_index_pgroonga”, opclass: :pgroonga_text_term_search_ops, using: :pgroonga
./plugins/full_text_search/db/migrate/20190807085000_create_fts_query_expansions.rb:24: “source pgroonga_text_term_search_ops_v2”,
./plugins/full_text_search/db/migrate/20190807085000_create_fts_query_expansions.rb:25: “destination pgroonga_text_term_search_ops_v2”,
可以看到,在plugins/full_text_search/db/migrate/20190807085000_create_fts_query_expansions.rb
裡,operator class的名字是叫pgroonga_text_term_search_ops_v2
,但是在db/schema.rb
裡面,卻把它的名字誤植為pgroonga_text_term_search_ops
。
解決方式
一個權宜之計是手動編輯db/schema.rb
,把裡面的pgroonga_text_term_search_ops
加上_v2
即可。
改完之後就能成功執行db:reset
的指令了!
發生原因
rails的Github上有人提了issue並提出了他的解決方式:
首先要找到redmine所用的rails版本,筆者的是5.2.4.2,這點可以從上面的錯誤訊息中看出來。
然後修改:/usr/share/rvm/gems/ruby-2.7.0/gems/activerecord-<rails_version>/lib/active_record/connection_adapters/postgresql/schema_statements.rb
這個檔案:
把:
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
改成:
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops(_\w+)?)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
這樣rails在生成db/schema.rb
時才能找到正確的operator class名稱。看起來這才是治本的方法。
參考連結
Rails 5 how to clear or delete production postgres database
AR PG opclass schema dumper regex extracts wrong opclass when opclass does not end with _ops