انتهاك قيد "عدم القابلية للتغيير" لـ "discourse_solved_solved_topics" أثناء إعادة البناء

Hello everyone!

During a rebuild on a forum in the stable branch, I have an error related to the solved plugin.

Here are the logs:

2025-12-20 03:48:21.715 UTC [1122] discourse@discourse ERROR:  null value in column "answer_post_id" of relation "discourse_solved_solved_topics" violates not-null constraint
2025-12-20 03:48:21.715 UTC [1122] discourse@discourse DETAIL:  Failing row contains (3735, 35506, null, -1, null, 2021-05-12 18:38:58.11516, 2021-05-12 18:38:58.11516).
2025-12-20 03:48:21.715 UTC [1122] discourse@discourse STATEMENT:  INSERT INTO discourse_solved_solved_topics (
	  topic_id,
	  answer_post_id,
	  topic_timer_id,
	  accepter_user_id,
	  created_at,
	  updated_at
	)
	SELECT
	  tc.topic_id,
	  tc.answer_post_id,
	  tc.topic_timer_id,
	  tc.accepter_user_id,
	  tc.created_at,
	  tc.updated_at
	FROM (
	  SELECT
	    tc.topic_id,
	    CAST(tc.value AS INTEGER) AS answer_post_id,
	    CAST(tc2.value AS INTEGER) AS topic_timer_id,
	    COALESCE(ua.acting_user_id, -1) AS accepter_user_id,
	    tc.created_at,
	    tc.updated_at,
	    ROW_NUMBER() OVER (PARTITION BY tc.topic_id ORDER BY tc.created_at ASC) AS rn_topic,
	    ROW_NUMBER() OVER (PARTITION BY CAST(tc.value AS INTEGER) ORDER BY tc.created_at ASC) AS rn_answer
	  FROM topic_custom_fields tc
	  LEFT JOIN topic_custom_fields tc2 ON tc2.topic_id = tc.topic_id AND tc2.name = 'solved_auto_close_topic_timer_id'
	  LEFT JOIN user_actions ua ON ua.target_topic_id = tc.topic_id AND ua.action_type = 15
	  WHERE tc.name = 'accepted_answer_post_id'
	    AND tc.id > 0
	    AND tc.id <= 0 + 10000
	) tc
	WHERE tc.rn_topic = 1 AND tc.rn_answer = 1
	ON CONFLICT DO NOTHING
	
rake aborted!
StandardError: An error has occurred, all later migrations canceled: (StandardError)

ERROR:  null value in column "answer_post_id" of relation "discourse_solved_solved_topics" violates not-null constraint
DETAIL:  Failing row contains (3735, 35506, null, -1, null, 2021-05-12 18:38:58.11516, 2021-05-12 18:38:58.11516).
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in `exec'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in `async_exec'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/postgres/connection.rb:217:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:38:in `block in run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:34:in `block in with_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.2.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:34:in `with_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:38:in `run'
/var/www/discourse/lib/mini_sql_multisite_connection.rb:109:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/postgres/connection.rb:196:in `exec'
/var/www/discourse/plugins/discourse-solved/db/migrate/20250318024953_copy_solved_topic_custom_field_to_discourse_solved_solved_topics.rb:17:in `up'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:994:in `public_send'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:994:in `exec_migration'
/var/www/discourse/lib/freedom_patches/schema_migration_details.rb:8:in `block in exec_migration'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.1/lib/benchmark.rb:305:in `measure'
/var/www/discourse/lib/freedom_patches/schema_migration_details.rb:8:in `exec_migration'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:975:in `block (2 levels) in migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.2.1/lib/active_support/benchmark.rb:17:in `realtime'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:974:in `block in migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:412:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:973:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1187:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1535:in `block in execute_migration_in_transaction'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1588:in `ddl_transaction'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1534:in `execute_migration_in_transaction'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1509:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1509:in `migrate_without_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1454:in `block in migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1606:in `with_advisory_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1454:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1261:in `up'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1236:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/tasks/database_tasks.rb:270:in `migrate'
/var/www/discourse/lib/tasks/db.rake:267:in `block (2 levels) in <main>'
/var/www/discourse/lib/distributed_mutex.rb:53:in `block in synchronize'
/var/www/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/var/www/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/var/www/discourse/lib/distributed_mutex.rb:34:in `synchronize'
/var/www/discourse/lib/tasks/db.rake:242:in `block in <main>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.3.0/exe/rake:27:in `<top (required)>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'

Caused by:
PG::NotNullViolation: ERROR:  null value in column "answer_post_id" of relation "discourse_solved_solved_topics" violates not-null constraint (PG::NotNullViolation)
DETAIL:  Failing row contains (3735, 35506, null, -1, null, 2021-05-12 18:38:58.11516, 2021-05-12 18:38:58.11516).
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in `exec'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in `async_exec'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/postgres/connection.rb:217:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:38:in `block in run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:34:in `block in with_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.2.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:34:in `with_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/active_record_postgres/connection.rb:38:in `run'
/var/www/discourse/lib/mini_sql_multisite_connection.rb:109:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_sql-1.6.0/lib/mini_sql/postgres/connection.rb:196:in `exec'
/var/www/discourse/plugins/discourse-solved/db/migrate/20250318024953_copy_solved_topic_custom_field_to_discourse_solved_solved_topics.rb:17:in `up'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:994:in `public_send'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:994:in `exec_migration'
/var/www/discourse/lib/freedom_patches/schema_migration_details.rb:8:in `block in exec_migration'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.1/lib/benchmark.rb:305:in `measure'
/var/www/discourse/lib/freedom_patches/schema_migration_details.rb:8:in `exec_migration'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:975:in `block (2 levels) in migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.2.1/lib/active_support/benchmark.rb:17:in `realtime'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:974:in `block in migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:412:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:973:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1187:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1535:in `block in execute_migration_in_transaction'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1588:in `ddl_transaction'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1534:in `execute_migration_in_transaction'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1509:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1509:in `migrate_without_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1454:in `block in migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1606:in `with_advisory_lock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1454:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1261:in `up'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/migration.rb:1236:in `migrate'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.2.1/lib/active_record/tasks/database_tasks.rb:270:in `migrate'
/var/www/discourse/lib/tasks/db.rake:267:in `block (2 levels) in <main>'
/var/www/discourse/lib/distributed_mutex.rb:53:in `block in synchronize'
/var/www/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/var/www/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/var/www/discourse/lib/distributed_mutex.rb:34:in `synchronize'
/var/www/discourse/lib/tasks/db.rake:242:in `block in <main>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.3.0/exe/rake:27:in `<top (required)>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
I, [2025-12-20T03:48:21.794538 #1]  INFO -- : gem install geocoder -v 1.8.3 -i /var/www/discourse/plugins/discourse-locations/gems/3.3.8 --no-document --ignore-dependencies --no-user-install
Successfully installed geocoder-1.8.3
1 gem installed
== 20250318024953 CopySolvedTopicCustomFieldToDiscourseSolvedSolvedTopics: migrating 

I, [2025-12-20T03:48:21.795736 #1]  INFO -- : Terminating async processes
I, [2025-12-20T03:48:21.795838 #1]  INFO -- : Sending INT to HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/15/bin/postmaster -D /etc/postgresql/15/main pid: 45
I, [2025-12-20T03:48:21.795934 #1]  INFO -- : Sending TERM to exec chpst -u redis -U redis /usr/bin/redis-server /etc/redis/redis.conf pid: 112
112:signal-handler (1766202501) Received SIGTERM scheduling shutdown...
2025-12-20 03:48:21.796 UTC [45] LOG:  received fast shutdown request
2025-12-20 03:48:21.800 UTC [45] LOG:  aborting any active transactions
2025-12-20 03:48:21.808 UTC [45] LOG:  background worker "logical replication launcher" (PID 59) exited with exit code 1
2025-12-20 03:48:21.809 UTC [54] LOG:  shutting down
2025-12-20 03:48:21.811 UTC [54] LOG:  checkpoint starting: shutdown immediate
2025-12-20 03:48:21.861 UTC [54] LOG:  checkpoint complete: wrote 18 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.039 s, sync=0.006 s, total=0.052 s; sync files=10, longest=0.003 s, average=0.001 s; distance=161 kB, estimate=161 kB
112:M 20 Dec 2025 03:48:21.892 # User requested shutdown...
112:M 20 Dec 2025 03:48:21.892 * Saving the final RDB snapshot before exiting.
2025-12-20 03:48:21.918 UTC [45] LOG:  database system is shut down
112:M 20 Dec 2025 03:48:22.621 * DB saved on disk
112:M 20 Dec 2025 03:48:22.621 # Redis is now ready to exit, bye bye...


FAILED
--------------------
Pups::ExecError: cd /var/www/discourse && su discourse -c 'bundle exec rake db:migrate' failed with return #<Process::Status: pid 1016 exit 1>
Location of failure: /usr/local/lib/ruby/gems/3.3.0/gems/pups-1.3.0/lib/pups/exec_command.rb:131:in `spawn'
exec failed with the params {"cd"=>"$home", "tag"=>"migrate", "hook"=>"db_migrate", "cmd"=>["su discourse -c 'bundle exec rake db:migrate'"]}
bootstrap failed with exit code 1
** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one.
./discourse-doctor may help diagnose the problem.

I managed to rebuild by deleting the solved plugin in the app.yml (using the - rm -rf discourse-solved command), and the owner of the forum could probably do without this plugin but I’d still like to fix this.

I don’t think the issue comes from the plugin, it may be a bad manipulation on the forum. But when I explore the database, I have at least 20-30 \N entries in value

Here is an extract:

id topic_id name value created at updated at
1579 35497 accepted_answer_post_id 301435 2021-05-10 20:18:57.780444 2021-05-10 20:18:57.780444
1583 35506 accepted_answer_post_id \N 2021-05-12 18:38:58.11516 2021-05-12 18:38:58.11516
423 21211 accepted_answer_post_id 153039 2019-09-04 21:01:37.811417 2019-09-04 21:01:37.811417
424 21685 accepted_answer_post_id 156850 2019-09-09 04:39:28.388591 2019-09-09 04:39:28.388591
425 21711 accepted_answer_post_id 157323 2019-09-11 11:51:14.375185 2019-09-11 11:51:14.375185
428 20982 accepted_answer_post_id 157807 2019-09-14 08:53:16.079032 2019-09-14 08:53:16.079032
429 4949 accepted_answer_post_id 157515 2019-09-16 07:57:00.862694 2019-09-16 07:57:00.862694
430 15439 accepted_answer_post_id 158645 2019-09-19 00:56:20.112603 2019-09-19 00:56:20.112603
1603 35710 accepted_answer_post_id \N 2021-05-19 20:58:06.598697 2021-05-19 20:58:06.598697
701 25377 accepted_answer_post_id 195459 2020-03-26 20:42:14.168898 2020-03-26 20:42:14.168898
434 21868 accepted_answer_post_id 159024 2019-09-22 10:09:09.671605 2019-09-22 10:09:09.671605

I need to tell you that I suck at sql queries, especially via command lines. I don’t really trust myself to delete these lines via terminal.

But anyway, what route do you recommand for me to fix this? A query (what type of querie?) ? A script, or something can be done in the forum directly?

باستثناء أنه تم نقله إلى النواة (core).
تلك التي يكون فيها post_id هو سطر جديد (Newline) هي المشكلة. يجب عليك فقط حذف تلك السجلات من قاعدة البيانات (قد يكون ذلك ممكنًا من خلال Rails). ربما يمكنك أن تطلب من Ask.discourse.com إخبارك كيف.

كحل محتمل… أعتقد أن حذف موضوع الآن يزيل الحل تلقائيًا ويحذف السجل في discourse_solved_solved_topics. لست متأكدًا بنسبة 100٪ مما إذا كان هذا يمكن أن يعمل بدون تثبيت إضافة solved، ولكن قد يستحق التحقق؟

إذا قمت بتشغيل استعلام في مستكشف البيانات للحصول عليها جميعًا، وحذفت واحدًا، وقمت بتشغيل الاستعلام مرة أخرى لمعرفة ما إذا كان قد اختفى، فقد تكون هذه طريقة سهلة لمعرفة ما إذا كان يمكن أن ينجح؟

استعادة الموضوع لا تستعيد السجل في discourse_solved_solved_topics، لذا (ربما :slight_smile:) قد يؤدي “الحذف والاستعادة” السريع إلى مسحها. :crossed_fingers:

أنا متأكد تمامًا من أنه لن ينجح لأن الكود الذي يقوم بذلك موجود في المكون الإضافي.

أنا متأكد أيضًا تمامًا من أن قاعدة البيانات معطلة. لا يُفترض أن تحتوي على فواصل أسطر في عمود القيمة هذا. ما يجب القيام به هو حذفها من قاعدة البيانات. إذا لم تفعل ذلك، فستنسى عندما تحاول الانتقال إلى خادم جديد واستعادة قاعدة البيانات هذه (وستكون قد حُلت لأنها في الكود الأساسي) وستضطر إلى اكتشاف هذا الأمر مرة أخرى.

إعجاب واحد (1)

آه، اعتقدت أنها قد تكون محاولة بعيدة المنال. لم أكن متأكدًا من مكان الاحتفاظ بالتنظيف. :slight_smile:

يبدو أن ألكسندر واجه مشكلة مماثلة واتجه إلى مسار قاعدة البيانات: Error upgrading to latest Discourse -- solved - #4 by Alexander_Wright

إعجاب واحد (1)