Skip to content

Fix course filter reporting zero activity for everyone - #3

Merged
POBrien333 merged 1 commit into
mainfrom
fix/course-filter-placeholders
Sep 10, 2026
Merged

POBrien333 merged 1 commit into
mainfrom
fix/course-filter-placeholders

Conversation

@POBrien333

Copy link
Copy Markdown
Owner

The bug

Filtering the student list by course showed every student as having no activity — no completions, no active days — regardless of what they had actually done. The unfiltered list was always correct.

Cause

$wpdb->prepare() fills placeholders in SQL-text order. base_query() pushed parameters in the order the clauses were assembled. The course filter is appended to the activity subquery's WHERE, which sits after the three window comparisons in the SELECT list — so with a course filter every binding shifted by one:

completions_recent   event_time >= 8131            <- the course ID
active_days_long     event_time >= <short window>  <- wrong window
activity WHERE       course_id = 1781259752        <- a unix timestamp

No row has a course_id equal to a timestamp, so the subquery returned nothing, every student left-joined to NULL, and the list read as all zeroes.

The placeholder count still matched, so nothing errored. And the unfiltered shape — the only one exercised during development — was unaffected, which is why this reached a release.

Fix

Parameters are pushed in SQL-text order, with a comment on why assembly order and binding order differ.

Regression test

This is the second placeholder-ordering bug in this file, so a fix alone felt insufficient.

tests/placeholder-order.php substitutes parameters exactly as prepare() does and asserts each value is the right kind of value — timestamp comparisons get timestamps, course filters get post IDs — across six query shapes. No dependencies; runs with plain php.

  • 9 failures against the 0.1.1 code
  • 42/42 pass on this branch

.github/workflows/test.yml runs it plus a lint pass on PHP 7.4 and 8.3, on push and PR.

Also

  • Bumped to 0.1.2 across the header, MSSA_VERSION and the readme stable tag
  • tests/ excluded from the distributed zip — still 14 files
  • Restored the missing 0.1.1 entry in readme.txt's changelog

Verifying after merge

Open the Student Activity screen, pick a course, and confirm students with recorded lessons in it now show their real figures.

Filtering the student list by course showed every student as having no
activity, no completions and no active days, even where they plainly had.

$wpdb->prepare() substitutes placeholders in the order they appear in the SQL
text, but base_query() pushed its parameters in the order the clauses were
assembled. The course filter is appended to the activity subquery's WHERE
clause, which in the finished SQL comes *after* the three window comparisons in
the SELECT list. So with a course filter applied the bindings shifted by one:

  completions_recent   event_time >= 8131          <- the course ID
  active_days_long     event_time >= <short window>
  activity WHERE       course_id = 1781259752      <- a unix timestamp

No row has a course_id equal to a timestamp, so the activity subquery returned
nothing, every student left-joined to NULL and the list read as all zeroes.

The placeholder count still matched, which is why nothing errored and why the
unfiltered list — the only shape exercised during development — was always
correct.

Parameters are now pushed in SQL-text order, with a comment explaining why the
assembly order and the binding order differ.

This is the second placeholder-ordering bug in this file, so it also adds
tests/placeholder-order.php: a dependency-free regression test that substitutes
parameters the way prepare() does and asserts each value is the right kind of
value — a timestamp comparison gets a timestamp, a course filter gets a post ID.
It reports 9 failures against the 0.1.1 code and passes on this one. A CI
workflow runs it, plus a lint pass, on PHP 7.4 and 8.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@POBrien333
POBrien333 merged commit 76e0fdd into main Sep 10, 2026
2 checks passed
@POBrien333
POBrien333 deleted the fix/course-filter-placeholders branch September 10, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant