Skip to content

fix: replace having alias with whereRaw for postgresql compatibility#125

Merged
ManukMinasyan merged 2 commits intomainfrom
fix/postgresql-having-alias-compatibility
Feb 16, 2026
Merged

fix: replace having alias with whereRaw for postgresql compatibility#125
ManukMinasyan merged 2 commits intomainfrom
fix/postgresql-having-alias-compatibility

Conversation

@ManukMinasyan
Copy link
Copy Markdown
Contributor

PostgreSQL does not allow referencing column aliases in HAVING clauses. Use whereRaw with the full subquery expression instead.

PostgreSQL does not allow referencing column aliases in HAVING clauses.
Use whereRaw with the full subquery expression instead.
Copilot AI review requested due to automatic review settings February 16, 2026 02:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the TopTeamsTableWidget query to be PostgreSQL-compatible by avoiding the use of a HAVING clause that references a SELECT alias (records_count), instead reusing the full computed expression in the filter.

Changes:

  • Refactors the computed records_count SQL into a reusable expression string.
  • Replaces having('records_count', '>', 0) with whereRaw("({$expression}) > 0") to avoid alias usage in HAVING.

Comment on lines 116 to 120
private function buildRecordsCountExpression(string $systemSource, string $startStr, string $endStr): string
{
$subqueries = collect(self::ENTITY_TABLES)->map(
fn (string $table): string => "(SELECT COUNT(*) FROM {$table} WHERE {$table}.team_id = teams.id AND {$table}.deleted_at IS NULL AND {$table}.creation_source != '{$systemSource}' AND {$table}.created_at BETWEEN '{$startStr}' AND '{$endStr}')"
);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildRecordsCountExpression() interpolates $systemSource, $startStr, and $endStr directly into raw SQL strings that are then used in both DB::raw() and whereRaw(). This is brittle (quoting/escaping) and bypasses query bindings; consider switching to selectRaw() / whereRaw() with ? placeholders + bindings (or building these subqueries with the query builder) so values are safely parameterized and won’t break if they contain quotes.

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +113
return Team::query()
->where('personal_team', false)
->addSelect([
'teams.*',
$this->buildRecordsCountSelect($systemSource, $startStr, $endStr),
DB::raw("({$recordsCountExpression}) as records_count"),
DB::raw('(SELECT COUNT(*) FROM team_user WHERE team_user.team_id = teams.id) as members_count'),
DB::raw('(SELECT COUNT(*) FROM custom_fields WHERE custom_fields.tenant_id = teams.id) as custom_fields_count'),
$this->buildLastActivitySelect($systemSource),
])
->having('records_count', '>', 0);
->whereRaw("({$recordsCountExpression}) > 0");
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filtering was changed from having('records_count', '>', 0) to whereRaw(...) for PostgreSQL compatibility; this is important query behavior and currently has no direct automated coverage. Please add a Pest test that executes the widget’s query (or renders the widget/table) and asserts teams with zero records in the period are excluded, so future refactors don’t regress PostgreSQL compatibility.

Copilot uses AI. Check for mistakes.
$this->buildLastActivitySelect($systemSource),
])
->having('records_count', '>', 0);
->whereRaw("({$recordsCountExpression}) > 0");
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recordsCountExpression is now inlined in both the SELECT (to expose records_count) and the WHERE (to filter > 0). That can force the database to evaluate the same correlated subqueries twice per team. To avoid the double work, consider computing records_count in a subquery/CTE (or fromSub() wrapper) and then applying where('records_count', '>', 0) on the outer query.

Suggested change
->whereRaw("({$recordsCountExpression}) > 0");
->having('records_count', '>', 0);

Copilot uses AI. Check for mistakes.
Use query parameter placeholders instead of string interpolation
for values in raw SQL expressions to prevent potential injection.
@ManukMinasyan ManukMinasyan merged commit 5acfd74 into main Feb 16, 2026
8 checks passed
@ManukMinasyan ManukMinasyan deleted the fix/postgresql-having-alias-compatibility branch February 16, 2026 03:10
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.

2 participants