Skip to content

DatabaseTests fails on SQL Server #13

@lcharette

Description

@lcharette

I've added SQL Server to the test workflow, and I get stuck on this issue. So I'm sharing in case someone as an idea and because writing things down sometimes helps me figure things out.

1) UserFrosting\Sprinkle\Core\Tests\Integration\Database\DatabaseTests::testBelongsToManyThroughPaginatedWithOrderByAggregateColumn
Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 20018 Column 'permissions.slug' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. [20018] (severity 16) [select top 1 [permissions].*, (select count(*) from [roles] inner join [permission_roles] on [roles].[id] = [permission_roles].[role_id] where [permissions].[id] = [permission_roles].[permission_id]) as [roles_count], [permissions].[id] from [permissions] inner join [permission_roles] on [permissions].[id] = [permission_roles].[permission_id] inner join [role_users] on [role_users].[role_id] = [permission_roles].[role_id] where [user_id] = 1 group by [permissions].[id] order by [roles_count] desc] (SQL: select top 1 [permissions].*, (select count(*) from [roles] inner join [permission_roles] on [roles].[id] = [permission_roles].[role_id] where [permissions].[id] = [permission_roles].[permission_id]) as [roles_count], [permissions].[id] from [permissions] inner join [permission_roles] on [permissions].[id] = [permission_roles].[permission_id] inner join [role_users] on [role_users].[role_id] = [permission_roles].[role_id] where [user_id] = 1 group by [permissions].[id] order by [roles_count] desc)

~/userfrosting/sprinkle-core/vendor/illuminate/database/Connection.php:712
~/userfrosting/sprinkle-core/vendor/illuminate/database/Connection.php:672
~/userfrosting/sprinkle-core/vendor/illuminate/database/Connection.php:376
~/userfrosting/sprinkle-core/vendor/illuminate/database/Query/Builder.php:2414
~/userfrosting/sprinkle-core/app/src/Database/Builder.php:115
~/userfrosting/sprinkle-core/vendor/illuminate/database/Eloquent/Builder.php:625
~/userfrosting/sprinkle-core/vendor/illuminate/database/Eloquent/Builder.php:609
~/userfrosting/sprinkle-core/app/src/Database/Relations/Concerns/Unique.php:300
~/userfrosting/sprinkle-core/app/src/Database/Relations/Concerns/Unique.php:340
~/userfrosting/sprinkle-core/app/src/Database/Relations/Concerns/Unique.php:250
~/userfrosting/sprinkle-core/app/tests/Integration/Database/DatabaseTests.php:750

Actual problem is this: https://stackoverflow.com/a/13999903/445757

It doesn't affect MySQL because MySQL is smart enough to understand how to handle that (somehow). And it's a "fake" issue in our case, because they are really the same slug (same id).

The failed test is this one :

$paginatedPermissions = $user->permissions()->withCount('roles')->orderBy('roles_count', 'desc')->take(1)->offset(0);

But it's not the query that cause the error. The error is caused by this query :

$modelIds = $constrainedBuilder->get()->pluck($primaryKeyName)->toArray();

Here's the "beautified" SQL:

SELECT
	top 1 [permissions].*,
	(
		SELECT
			count(*)
		FROM
			[roles]
			INNER JOIN [permission_roles] ON [roles].[id] = [permission_roles].[role_id]
		WHERE
			[permissions].[id] = [permission_roles].[permission_id]) AS [roles_count], [permissions].[id]
	FROM
		[permissions]
		INNER JOIN [permission_roles] ON [permissions].[id] = [permission_roles].[permission_id]
		INNER JOIN [role_users] ON [role_users].[role_id] = [permission_roles].[role_id]
	WHERE
		[user_id] = 1
	GROUP BY
		[permissions].[id]
	ORDER BY
		[roles_count] DESC

Problematic part is this : [permissions].*,. Remove the wildcard, remove the slug from columns.

Failed Solution n° 1

We could replace all existing columns (except the required "id" for the group by):

$subQuery->addSelect($relatedPivotKeyName)

...but this removes roles_count and mess up the sort.

There's no way I know to remove * without removing the roles_count (or other user defined select).

Failed Solution n° 2

The issue is with GROUP BY, so we remove the group by right?

Here's the result without Group By and limit :

role_count id
3 2
3 2
1 3
1 1

So it does work with Limit 1, but Limit 2 won't work (testBelongsToManyThroughPaginated test will fail in this case highlighting this).

Group could be done on the Collection, but this means Limit and Offset also needs to be done In the Collection. Would make the whole thing less efficient.

Failed Solution n° 3

DISTINCT instead of GROUP BY doesn't help too.

$uniqueIdScope = function (Builder $subQuery) use ($relatedPivotKeyName) {
            $subQuery->addSelect($relatedPivotKeyName)
                ->distinct();
                    //  ->groupBy($relatedPivotKeyName);
        };

It does fix testBelongsToManyThroughPaginatedWithOrderByAggregateColumn, but it doesn't fix testBelongsToManyThroughPaginated:

SELECT
	*
FROM ( SELECT DISTINCT
		[permissions].[id],
		row_number() OVER (ORDER BY (
			SELECT
				0)) AS row_num
	FROM
		[permissions]
		INNER JOIN [permission_roles] ON [permissions].[id] = [permission_roles].[permission_id]
		INNER JOIN [role_users] ON [role_users].[role_id] = [permission_roles].[role_id]
	WHERE
		[user_id] = 1) AS temp_table
WHERE
	row_num BETWEEN 2 AND 3
ORDER BY
	row_num

Which returns ID [2, 2] instead of [2, 3]

Original SQL :

SELECT
	*
FROM (
	SELECT
		[permissions].[id],
		row_number() OVER (ORDER BY (
			SELECT
				0)) AS row_num
	FROM
		[permissions]
		INNER JOIN [permission_roles] ON [permissions].[id] = [permission_roles].[permission_id]
		INNER JOIN [role_users] ON [role_users].[role_id] = [permission_roles].[role_id]
	WHERE
		[user_id] = 1
	GROUP BY
		[permissions].[id]) AS temp_table
WHERE
	row_num BETWEEN 2 AND 3
ORDER BY
	row_num

Other solutions

We could "Cheat" the test and force select only 'id' :

$paginatedPermissions = $user->permissions()
    ->select('id')
    ->withCount('roles')
    ->orderBy('roles_count', 'desc')
    ->take(1)
    ->offset(0);

But slug is not part of the results anymore (and bug is still there).


So I'm bit stuck now. SQL Server doesn't support this


Linked Branch : https://github.com/userfrosting/sprinkle-core/tree/5.0-mssql
Other references:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Not Started

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions