Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.x] Add a method to connection for driver and version comparison #49723

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/Illuminate/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
use Illuminate\Database\Schema\Builder as SchemaBuilder;
use Illuminate\Support\Arr;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
use PDO;
use PDOStatement;
use RuntimeException;
Expand Down Expand Up @@ -1318,7 +1320,7 @@ public function getConfig($option = null)
}

/**
* Get the PDO driver name.
* Get the driver name from the configuration options.
*
* @return string
*/
Expand Down Expand Up @@ -1612,6 +1614,56 @@ public function getServerVersion(): string
return $this->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
}

/**
* Determine if the connection matches the given driver name and version.
*
* @template TValue
*
* @param string|array|\Closure(string,string): TValue $driver
* @param string|null $operator
* @param string|null $version
hafezdivandari marked this conversation as resolved.
Show resolved Hide resolved
* @return bool|TValue
*/
public function is(string|array|Closure $driver, ?string $operator = null, ?string $version = null)
{
if (func_num_args() === 2 || (! is_string($driver) && func_num_args() === 3)) {
throw new InvalidArgumentException('Illegal arguments combination.');
}

$actualDriver = $this->getDriverName() ?? $this->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME);
$actualVersion = $this->getConfig('version') ?? $this->getServerVersion();

[$actualDriver, $actualVersion] = match (true) {
Str::contains($actualVersion, 'MariaDB') => ['mariadb', Str::between($actualVersion, '5.5.5-', '-MariaDB')],
Str::contains($actualVersion, ['vitess', 'PlanetScale']) => ['vitess', Str::before($actualVersion, '-')],
default => [strtolower($actualDriver), $actualVersion],
};

if ($driver instanceof Closure) {
return $driver($actualDriver, $actualVersion);
}

if (is_array($driver)) {
foreach ($driver as $key => $value) {
[$name, $operator, $version] = match (true) {
is_string($key) => [$key, $value[0], $value[1]],
is_array($value) => $value,
default => [$value, null, null],
};

if (strtolower($name) === $actualDriver
&& (! $operator || ! $version || version_compare($actualVersion, $version, $operator))) {
return true;
}
}

return false;
}

return strtolower($driver) === $actualDriver
&& (! $operator || ! $version || version_compare($actualVersion, $version, $operator));
}

/**
* Register a connection resolver.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ abstract class DatabaseInspectionCommand extends Command
protected function getConnectionName(ConnectionInterface $connection, $database)
{
return match (true) {
$connection instanceof MySqlConnection && $connection->isMaria() => 'MariaDB',
$connection instanceof MySqlConnection && $connection->is('mariadb') => 'MariaDB',
$connection instanceof MySqlConnection => 'MySQL',
$connection instanceof PostgresConnection => 'PostgreSQL',
$connection instanceof SQLiteConnection => 'SQLite',
Expand Down
24 changes: 0 additions & 24 deletions src/Illuminate/Database/MySqlConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
use Illuminate\Database\Schema\MySqlBuilder;
use Illuminate\Database\Schema\MySqlSchemaState;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use PDO;

class MySqlConnection extends Connection
{
Expand Down Expand Up @@ -38,28 +36,6 @@ protected function isUniqueConstraintError(Exception $exception)
return boolval(preg_match('#Integrity constraint violation: 1062#i', $exception->getMessage()));
}

/**
* Determine if the connected database is a MariaDB database.
*
* @return bool
*/
public function isMaria()
{
return str_contains($this->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), 'MariaDB');
}

/**
* Get the server version for the connection.
*
* @return string
*/
public function getServerVersion(): string
{
return str_contains($version = parent::getServerVersion(), 'MariaDB')
? Str::between($version, '5.5.5-', '-MariaDB')
: $version;
}

/**
* Get the default query grammar instance.
*
Expand Down
5 changes: 1 addition & 4 deletions src/Illuminate/Database/Query/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Illuminate\Database\Query\Builder;
use Illuminate\Support\Str;
use PDO;

class MySqlGrammar extends Grammar
{
Expand Down Expand Up @@ -116,9 +115,7 @@ protected function compileGroupLimit(Builder $query)
*/
public function useLegacyGroupLimit(Builder $query)
{
$version = $query->getConnection()->getReadPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);

return ! $query->getConnection()->isMaria() && version_compare($version, '8.0.11') < 0;
return $query->getConnection()->is('mysql', '<', '8.0.11');
}

/**
Expand Down
5 changes: 1 addition & 4 deletions src/Illuminate/Database/Query/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use PDO;

class SQLiteGrammar extends Grammar
{
Expand Down Expand Up @@ -193,9 +192,7 @@ protected function compileJsonContainsKey($column)
*/
protected function compileGroupLimit(Builder $query)
{
$version = $query->getConnection()->getReadPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);

if (version_compare($version, '3.25.0') >= 0) {
if ($query->getConnection()->is('sqlite', '>=', '3.25.0')) {
return parent::compileGroupLimit($query);
}

Expand Down
5 changes: 1 addition & 4 deletions src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,7 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent
*/
public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
{
$version = $connection->getServerVersion();

if (($connection->isMaria() && version_compare($version, '10.5.2', '<')) ||
(! $connection->isMaria() && version_compare($version, '8.0.3', '<'))) {
if ($connection->is([['mariadb', '<', '10.5.2'], ['mysql', '<', '8.0.3']])) {
$column = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable()))
->firstWhere('name', $command->from);

Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Database/Schema/MySqlSchemaState.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected function baseDumpCommand()
{
$command = 'mysqldump '.$this->connectionString().' --no-tablespaces --skip-add-locks --skip-comments --skip-set-charset --tz-utc --column-statistics=0';

if (! $this->connection->isMaria()) {
if (! $this->connection->is('mariadb')) {
$command .= ' --set-gtid-purged=OFF';
}

Expand Down
25 changes: 7 additions & 18 deletions src/Illuminate/Queue/DatabaseQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
use Illuminate\Queue\Jobs\DatabaseJob;
use Illuminate\Queue\Jobs\DatabaseJobRecord;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use PDO;

class DatabaseQueue extends Queue implements QueueContract, ClearableQueue
{
Expand Down Expand Up @@ -255,25 +253,16 @@ protected function getNextAvailableJob($queue)
*/
protected function getLockForPopping()
{
$databaseEngine = $this->database->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME);
$databaseVersion = $this->database->getConfig('version') ?? $this->database->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);

if (Str::of($databaseVersion)->contains('MariaDB')) {
$databaseEngine = 'mariadb';
$databaseVersion = Str::before(Str::after($databaseVersion, '5.5.5-'), '-');
} elseif (Str::of($databaseVersion)->contains(['vitess', 'PlanetScale'])) {
$databaseEngine = 'vitess';
$databaseVersion = Str::before($databaseVersion, '-');
}

if (($databaseEngine === 'mysql' && version_compare($databaseVersion, '8.0.1', '>=')) ||
($databaseEngine === 'mariadb' && version_compare($databaseVersion, '10.6.0', '>=')) ||
($databaseEngine === 'pgsql' && version_compare($databaseVersion, '9.5', '>=')) ||
($databaseEngine === 'vitess' && version_compare($databaseVersion, '19.0', '>='))) {
if ($this->database->is([
['mysql', '>=', '8.0.1'],
['mariadb', '>=', '10.6.0'],
['pgsql', '>=', '9.5'],
['vitess', '>=', '19.0'],
])) {
return 'FOR UPDATE SKIP LOCKED';
}

if ($databaseEngine === 'sqlsrv') {
if ($this->database->is('sqlsrv')) {
return 'with(rowlock,updlock,readpast)';
}

Expand Down
1 change: 1 addition & 0 deletions src/Illuminate/Support/Facades/DB.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
* @method static \Illuminate\Database\Connection setTablePrefix(string $prefix)
* @method static \Illuminate\Database\Grammar withTablePrefix(\Illuminate\Database\Grammar $grammar)
* @method static string getServerVersion()
* @method static bool is(string|array|\Closure $driver, string|null $operator = null, string|null $version = null)
* @method static void resolverFor(string $driver, \Closure $callback)
* @method static mixed getResolver(string $driver)
* @method static mixed transaction(\Closure $callback, int $attempts = 1)
Expand Down
78 changes: 78 additions & 0 deletions tests/Database/DatabaseConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,84 @@ public function testGetRawQueryLog()
$this->assertEquals(1.23, $log[0]['time']);
}

public function testIsWithConfig()
{
$connection = new Connection(m::mock(PDO::class), config: ['driver' => 'mysql', 'version' => '5.7.2']);

$this->assertTrue($connection->is('mysql'));
$this->assertTrue($connection->is('mysql', '<', '8.0.11'));
$this->assertTrue($connection->is([['mysql', '<', '8.0.11'], ['sqlite', '>=', '3.5.0']]));
$this->assertTrue($connection->is(['mysql' => ['<', '8.0.11'], 'sqlite' => ['>=', '3.5.0']]));
$this->assertTrue($connection->is(['mysql', 'sqlite']));
$this->assertTrue($connection->is(
fn ($driver, $version) => $driver === 'mysql' && version_compare($version, '8.0.11', '<')
));
$this->assertEquals(['mysql', '5.7.2'], $connection->is(fn ($driver, $version) => [$driver, $version]));

$this->assertFalse($connection->is('mariadb'));
$this->assertFalse($connection->is('mysql', '>=', '8.0.11'));
$this->assertFalse($connection->is([['mysql', '>', '8.0.11'], ['sqlite', '>=', '3.5.0']]));
$this->assertFalse($connection->is(['sqlsrv' => ['<', '8.0.11'], 'vitess' => ['>=', '3.5.0']]));
$this->assertFalse($connection->is(['vitess', 'sqlite']));
$this->assertFalse($connection->is(
fn ($driver, $version) => $driver === 'pgsql' && version_compare($version, '8.0.11', '<')
));
}

public function testIsWithPDO()
{
$pdo = m::mock(PDO::class);
$pdo->shouldReceive('getAttribute')->with(PDO::ATTR_DRIVER_NAME)->andReturn('mysql');
$pdo->shouldReceive('getAttribute')->with(PDO::ATTR_SERVER_VERSION)->andReturn('5.5.5-10.5.2-MariaDB');
$connection = new Connection($pdo, config: []);

$this->assertTrue($connection->is('mariadb'));
$this->assertTrue($connection->is('mariadb', '<', '10.10.10'));
$this->assertTrue($connection->is([['mysql', '>', '8.0.11'], ['mariadb', '>=', '9.5.0']]));
$this->assertTrue($connection->is(['mysql' => ['>', '8.0.11'], 'mariadb' => ['>=', '9.5.0']]));
$this->assertTrue($connection->is(['mariadb', 'sqlite']));
$this->assertTrue($connection->is(
fn ($driver, $version) => $driver === 'mariadb' && version_compare($version, '10.10.10', '<')
));
$this->assertEquals(['mariadb', '10.5.2'], $connection->is(fn ($driver, $version) => [$driver, $version]));

$this->assertFalse($connection->is('mysql'));
$this->assertFalse($connection->is('mysql', '>=', '8.0.11'));
$this->assertFalse($connection->is([['mysql', '>', '8.0.11'], ['sqlite', '>=', '3.5.0']]));
$this->assertFalse($connection->is(['sqlsrv' => ['<', '8.0.11'], 'vitess' => ['>=', '3.5.0']]));
$this->assertFalse($connection->is(['vitess', 'sqlite']));
$this->assertFalse($connection->is(
fn ($driver, $version) => $driver === 'pgsql' && version_compare($version, '8.0.11', '<')
));
}

public function testIsWithStringException()
{
$this->expectException(\InvalidArgumentException::class);

$connection = new Connection(m::mock(PDO::class));

$connection->is('mysql', '8.0.11');
}

public function testIsWithArrayException()
{
$this->expectException(\InvalidArgumentException::class);

$connection = new Connection(m::mock(PDO::class));

$connection->is(['mysql'], '<', '8.0.11');
}

public function testIsWithClosureException()
{
$this->expectException(\InvalidArgumentException::class);

$connection = new Connection(m::mock(PDO::class));

$connection->is(fn () => 'mysql', '<', '8.0.11');
}

protected function getMockConnection($methods = [], $pdo = null)
{
$pdo = $pdo ?: new DatabaseConnectionTestMockPDO;
Expand Down
9 changes: 3 additions & 6 deletions tests/Database/DatabaseSchemaBlueprintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ public function testRenameColumn()
});

$connection = m::mock(Connection::class);
$connection->shouldReceive('getServerVersion')->andReturn('8.0.4');
$connection->shouldReceive('isMaria')->andReturn(false);
$connection->shouldReceive('is')->with([['mariadb', '<', '10.5.2'], ['mysql', '<', '8.0.3']])->andReturn(false);

$blueprint = clone $base;
$this->assertEquals(['alter table `users` rename column `foo` to `bar`'], $blueprint->toSql($connection, new MySqlGrammar));
Expand All @@ -192,8 +191,7 @@ public function testNativeRenameColumnOnMysql57()
});

$connection = m::mock(Connection::class);
$connection->shouldReceive('isMaria')->andReturn(false);
$connection->shouldReceive('getServerVersion')->andReturn('5.7');
$connection->shouldReceive('is')->with([['mariadb', '<', '10.5.2'], ['mysql', '<', '8.0.3']])->andReturn(true);
$connection->shouldReceive('getSchemaBuilder->getColumns')->andReturn([
['name' => 'name', 'type' => 'varchar(255)', 'type_name' => 'varchar', 'nullable' => true, 'collation' => 'utf8mb4_unicode_ci', 'default' => 'foo', 'comment' => null, 'auto_increment' => false],
['name' => 'id', 'type' => 'bigint unsigned', 'type_name' => 'bigint', 'nullable' => false, 'collation' => null, 'default' => null, 'comment' => 'lorem ipsum', 'auto_increment' => true],
Expand All @@ -213,8 +211,7 @@ public function testNativeRenameColumnOnLegacyMariaDB()
});

$connection = m::mock(Connection::class);
$connection->shouldReceive('isMaria')->andReturn(true);
$connection->shouldReceive('getServerVersion')->andReturn('10.1.35');
$connection->shouldReceive('is')->with([['mariadb', '<', '10.5.2'], ['mysql', '<', '8.0.3']])->andReturn(true);
$connection->shouldReceive('getSchemaBuilder->getColumns')->andReturn([
['name' => 'name', 'type' => 'varchar(255)', 'type_name' => 'varchar', 'nullable' => true, 'collation' => 'utf8mb4_unicode_ci', 'default' => 'foo', 'comment' => null, 'auto_increment' => false],
['name' => 'id', 'type' => 'bigint unsigned', 'type_name' => 'bigint', 'nullable' => false, 'collation' => null, 'default' => null, 'comment' => 'lorem ipsum', 'auto_increment' => true],
Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/Database/SchemaBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ public function testAlteringTableWithForeignKeyConstraintsEnabled()

public function testSystemVersionedTables()
{
if ($this->driver !== 'mysql' || ! $this->getConnection()->isMaria()) {
if (! $this->getConnection()->is('mariadb')) {
$this->markTestSkipped('Test requires a MariaDB connection.');
}

Expand Down