« discourse_solved_solved_topics » viole la contrainte NOT NULL lors de la reconstruction

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?

Sauf que cela a été déplacé dans le cœur (core).
Ceux pour lesquels l’id du message (post_id) est Nouvelle Ligne (Newline) sont le problème. Vous devriez simplement supprimer ces enregistrements de la base de données (cela pourrait être possible depuis rails). Vous pouvez probablement demander à Ask.discourse.com de vous dire comment faire.

À titre d’essai possible… Je crois que la suppression d’un sujet supprime automatiquement la solution et efface l’enregistrement dans discourse_solved_solved_topics. Je ne suis pas sûr à 100 % que cela puisse fonctionner sans le plugin solved installé, mais cela pourrait valoir la peine de vérifier ?

Si vous exécutez une requête dans l’explorateur de données pour tous les récupérer, en supprimez un, et exécutez à nouveau la requête pour voir s’il a disparu, ce pourrait être un moyen facile de voir si cela pourrait fonctionner ?

La restauration du sujet ne restaure pas l’enregistrement dans discourse_solved_solved_topics, donc (potentiellement :slight_smile:) un rapide « supprimer et restaurer » pourrait les effacer. :crossed_fingers:

Je suis presque certain que cela ne fonctionnera pas car le code qui fait cela se trouve dans le plugin.

Je suis également presque certain que la base de données est corrompue. Elle n’est pas censée contenir de sauts de ligne dans cette colonne de valeur. Il faut les supprimer de la base de données. Si vous ne le faites pas, vous l’oublierez lorsque vous essaierez de migrer vers un nouveau serveur et de restaurer cette base de données (et cela sera résolu car c’est dans le cœur) et vous devrez comprendre cela à nouveau.

1 « J'aime »

Ah, je pensais que ce serait un coup de poker. Je n’étais pas sûr où était conservé le nettoyage. :slight_smile:

Il semble qu’Alexander ait eu un problème similaire et ait opté pour la voie de la base de données : Error upgrading to latest Discourse -- solved - #4 by Alexander_Wright

1 « J'aime »