Skip to content

[3.0]: Member post counts drift in four places outside the create and delete path #9518

Description

@albertlast

Basic Information

While testing #9517 I swept everything that adjusts members.posts, on both engines, over real HTTP. Four defects turned up that have nothing to do with that PR — they behave identically before and after it, because #9517 only flips the polarity of the board flag they read. Filing them together because they are all "the post count and the messages disagree".

1. Quick moderation can never decrease a post count.

Sources/Actions/QuickModeration.php:767:

$members[(int) $row['id_member']] = max(0, $members[(int) $row['id_member']]);

$members[$id] here is the adjustment, not the resulting count. It starts at 0, one -- makes it -1, and max(0, -1) puts it straight back to 0. So moving a topic into a board that does not count posts leaves every poster's count untouched. The + direction (moving into a board that does count) works, which is why this has stayed hidden.

2. TopicMove2 decides the "MOVED:" stub's post count from the wrong board.

The stub topic is created in the board being moved from:

$topicOptions = [
    'board' => Board::$info->id,
    ...
];

$posterOptions = [
    'id' => User::$me->id,
    'update_post_count' => empty($pcounter),   // release-3.0:224
];

but $pcounter was selected WHERE b.id_board = {int:to_board} — the board being moved to. So moving a topic out of a counting board into a quiet one leaves a stub in the counting board that does not count, and the reverse move leaves a stub in the quiet board that does.

3. Recounting member posts counts unapproved messages.

Maintenance::recountPosts() has no m.approved filter anywhere, while Msg::create() and Msg::approve() only ever count approved posts. On my test forum the recount settled on 28 where the approved messages come to 24 — the difference being exactly the unapproved ones. Running the recount on a forum that uses post moderation therefore inflates everybody.

4. The last step of that recount never runs on PostgreSQL.

The "members who have a post count but no posts left" cleanup is gated on $createTemporary, which comes from Sources/Actions/Admin/Maintenance.php:1521:

CREATE TEMPORARY TABLE {db_prefix}tmp_maint_recountposts (
    id_member mediumint(8) unsigned NOT NULL default {string:string_zero},
    PRIMARY KEY (id_member)
)
SELECT m.id_member
...

That is MySQL DDL, and it is issued with db_error_skip, so on PostgreSQL it fails silently, $createTemporary is false, and stale counts are left alone.

Steps to reproduce

For (1), which is the one a user would actually notice:

  1. Two boards: one with "count posts" on, one with it off.
  2. As a member, start a topic and post a reply in the counting board. The post count goes up by 2.
  3. From the message index, use quick moderation to move that topic to the board that does not count posts.

For (3):

  1. Turn post moderation on and let a member's reply sit unapproved.
  2. Admin → Maintenance → Members → Recount member posts.

Expected result

  1. The post count drops by 2.
  2. The recount agrees with the live counting, i.e. it ignores the unapproved reply.

Actual result

  1. The post count is unchanged. Moving the same topic back the other way does adjust it, so the two directions disagree.
  2. The recount includes the unapproved reply, so the count is higher than posting and approving would ever have produced.

Version/Git revision

3.0 Alpha 4, release-3.0 at bfbca5b

Database Engine

All

Database Version

MySQL 8.4 and PostgreSQL 17

PHP Version

8.4.24

Logs

# (4), from the PostgreSQL server log during a recount
ERROR:  syntax error at or near "unsigned" at character 80
STATEMENT:  CREATE TEMPORARY TABLE smf_tmp_maint_recountposts (
                    id_member mediumint(8) unsigned NOT NULL default '0',
                    PRIMARY KEY (id_member)
                )
                SELECT m.id_member
                FROM smf_messages AS m
                    INNER JOIN smf_boards AS b ON m.id_board = b.id_board

Additional Information

Found while testing #9517 (which fixes #9415). None of these four are caused by that PR; I checked each against release-3.0 as well.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions