Skip to content

Commit 6c543ef

Browse files
authored
Add MariaDB test support and fix MySQL-family platform detection (#672)
* fix: detect MySQL family via AbstractMySQLPlatform so MariaDB uses MySQL SQL dialect MariaDBPlatform does not extend MySQLPlatform (both extend AbstractMySQLPlatform), so projection state storage emitted PostgreSQL ON CONFLICT syntax against MariaDB. Switch platform checks to AbstractMySQLPlatform and add MariaDB to docker-compose. * ci: add MariaDB pass to split-testing for all DBAL components * test: fix MariaDB strictness in event-sourcing integration fixtures - CustomEventStreamTest: select MariaDbSingleStreamStrategy on MariaDBPlatform instead of falling back to MySqlSingleStreamStrategy (MySQL-8 DDL rejected by MariaDB) - GapDetection fixtures: write created_at without timezone offset (DATE_ATOM -> Y-m-d\TH:i:s) so MariaDB strict mode accepts it * fixes * fixes * test(laravel): pin doctrine/dbal ^4 in require-dev for prefer-lowest CI Config/PDO/Connection.php implements the DBAL-4-only ServerVersionProvider interface, so the Laravel package's own test suite needs DBAL 4. Without a pin, --prefer-lowest resolved DBAL 3.9 and failed with 'Interface Doctrine\DBAL\ServerVersionProvider not found'. Pin it in require-dev only, so the constraint scopes to this package's tests and does not propagate to consumers or the DBAL-3 Laravel quickstarts via the path repository. * fix(data-protection): use null comparison instead of isset for nullable param PHPStan 2.2 flags isset($length) on the always-defined ?int parameter (isset.variable). Use $length === null, which is behaviorally identical.
1 parent 1723b8f commit 6c543ef

14 files changed

Lines changed: 50 additions & 33 deletions

File tree

docker-compose.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ services:
1717
SQLITE_DATABASE_DSN: sqlite:////tmp/ecotone_test.db
1818
SECONDARY_DATABASE_DSN: mysql://ecotone:secret@database-mysql:3306/ecotone?serverVersion=8.0
1919
DATABASE_MYSQL: mysql://ecotone:secret@database-mysql:3306/ecotone?serverVersion=8.0
20+
DATABASE_MARIADB: mysql://ecotone:secret@database-mariadb:3306/ecotone?serverVersion=11.4.5-MariaDB
2021
SQS_DSN: sqs:?key=key&secret=secret&region=us-east-1&endpoint=http://localstack:4566&version=latest
2122
REDIS_DSN: redis://redis:6379
2223
KAFKA_DSN: kafka:9092
@@ -40,6 +41,7 @@ services:
4041
SQLITE_DATABASE_DSN: sqlite:////tmp/ecotone_test.db
4142
SECONDARY_DATABASE_DSN: pgsql://ecotone:secret@database:5432/ecotone?serverVersion=16
4243
DATABASE_MYSQL: mysql://ecotone:secret@database-mysql:3306/ecotone?serverVersion=8.0
44+
DATABASE_MARIADB: mysql://ecotone:secret@database-mariadb:3306/ecotone?serverVersion=11.4.5-MariaDB
4345
SQS_DSN: sqs:?key=key&secret=secret&region=us-east-1&endpoint=http://localstack:4566&version=latest
4446
REDIS_DSN: redis://redis:6379
4547
KAFKA_DSN: kafka:9092
@@ -61,6 +63,15 @@ services:
6163
MYSQL_DATABASE: "ecotone"
6264
ports:
6365
- "${MYSQL_PORT:-0}:3306"
66+
database-mariadb:
67+
image: mariadb:11.4
68+
environment:
69+
MARIADB_ROOT_PASSWORD: "secret"
70+
MARIADB_USER: "ecotone"
71+
MARIADB_PASSWORD: "secret"
72+
MARIADB_DATABASE: "ecotone"
73+
ports:
74+
- "${MARIADB_PORT:-0}:3306"
6475
rabbitmq:
6576
build: ./.docker/rabbitmq
6677
environment:

packages/DataProtection/src/Encryption/Core.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public static function substr(string $str, int $start, ?int $length = null): boo
173173
// mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP 5.3,
174174
// so we have to find the length ourselves. Also, substr() doesn't
175175
// accept null for the length.
176-
if (! isset($length)) {
176+
if ($length === null) {
177177
if ($start >= 0) {
178178
$length = $input_len - $start;
179179
} else {

packages/Dbal/src/DbalTransaction/ImplicitCommit.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
namespace Ecotone\Dbal\DbalTransaction;
99

1010
use Doctrine\DBAL\Connection;
11-
use Doctrine\DBAL\Platforms\MySQLPlatform;
11+
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
1212
use Throwable;
1313

1414
class ImplicitCommit
1515
{
1616
public static function isImplicitCommitException(Throwable $exception, Connection $connection): bool
1717
{
18-
if (! ($connection->getDriver()->getDatabasePlatform($connection) instanceof MySQLPlatform)) {
18+
if (! ($connection->getDriver()->getDatabasePlatform($connection) instanceof AbstractMySQLPlatform)) {
1919
return false;
2020
}
2121

packages/Dbal/tests/Integration/DeduplicationModuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public function test_deduplication_inserts_before_handler_when_transaction_is_ac
158158
// Set global lock timeout for the session
159159
if ($platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform) {
160160
$connection2->executeStatement('SET lock_timeout = 1000'); // 1 second
161-
} elseif ($platform instanceof \Doctrine\DBAL\Platforms\MySQLPlatform) {
161+
} elseif ($platform instanceof \Doctrine\DBAL\Platforms\AbstractMySQLPlatform) {
162162
// For MySQL, we need to set this on the session level
163163
$connection2->executeStatement('SET SESSION innodb_lock_wait_timeout = 1'); // 1 second
164164
}

packages/Laravel/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"nesbot/carbon": "^2.71|^3.0",
6565
"moneyphp/money": "^4.1.0",
6666
"ecotone/dbal": "~1.314.0",
67+
"doctrine/dbal": "^4.0",
6768
"timacdonald/log-fake": "^2.0"
6869
},
6970
"extra": {

packages/PdoEventSourcing/src/Database/ProjectionStateTableManager.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Ecotone\EventSourcing\Database;
66

77
use Doctrine\DBAL\Connection;
8-
use Doctrine\DBAL\Platforms\MySQLPlatform;
8+
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
99
use Ecotone\Dbal\Compatibility\SchemaManagerCompatibility;
1010
use Ecotone\Dbal\Database\DbalTableManager;
1111
use Ecotone\Messaging\Config\Container\Definition;
@@ -46,7 +46,7 @@ public function getTableName(): string
4646

4747
public function getCreateTableSql(Connection $connection): string|array
4848
{
49-
if ($connection->getDatabasePlatform() instanceof MySQLPlatform) {
49+
if ($connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
5050
return $this->getMysqlCreateSql();
5151
}
5252

packages/PdoEventSourcing/src/Projecting/PartitionState/DbalProjectionStateStorage.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace Ecotone\EventSourcing\Projecting\PartitionState;
99

1010
use Doctrine\DBAL\Connection;
11-
use Doctrine\DBAL\Platforms\MySQLPlatform;
11+
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
1212
use Ecotone\Dbal\AlreadyConnectedDbalConnectionFactory;
1313
use Ecotone\Dbal\MultiTenant\MultiTenantConnectionFactory;
1414
use Ecotone\EventSourcing\Database\ProjectionStateTableManager;
@@ -115,7 +115,7 @@ public function initPartition(string $projectionName, ?string $partitionKey = nu
115115

116116
// Try to insert the partition state, ignoring if it already exists
117117
$insertQuery = match (true) {
118-
$connection->getDatabasePlatform() instanceof MySQLPlatform => <<<SQL
118+
$connection->getDatabasePlatform() instanceof AbstractMySQLPlatform => <<<SQL
119119
INSERT INTO {$tableName} (projection_name, partition_key, last_position, user_state, metadata)
120120
VALUES (:projectionName, :partitionKey, :lastPosition, :userState, :metadata)
121121
ON DUPLICATE KEY UPDATE projection_name = projection_name -- no-op to ignore
@@ -155,7 +155,7 @@ public function savePartition(ProjectionPartitionState $projectionState): void
155155
$tableName = $this->getTableName();
156156

157157
$saveStateQuery = match (true) {
158-
$connection->getDatabasePlatform() instanceof MySQLPlatform => <<<SQL
158+
$connection->getDatabasePlatform() instanceof AbstractMySQLPlatform => <<<SQL
159159
INSERT INTO {$tableName} (projection_name, partition_key, last_position, user_state, metadata)
160160
VALUES (:projectionName, :partitionKey, :lastPosition, :userState, :metadata)
161161
ON DUPLICATE KEY UPDATE last_position = :lastPosition, user_state = :userState, metadata = :metadata

packages/PdoEventSourcing/tests/Integration/CustomEventStreamTest.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
namespace Test\Ecotone\EventSourcing\Integration;
66

7-
use Doctrine\DBAL\Driver\PDO\PgSQL\Driver;
7+
use Doctrine\DBAL\Platforms\MariaDBPlatform;
8+
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
89
use Ecotone\EventSourcing\EventSourcingConfiguration;
910
use Ecotone\EventSourcing\Prooph\FromProophMessageToArrayConverter;
1011
use Ecotone\Lite\EcotoneLite;
1112
use Ecotone\Messaging\Config\ModulePackageList;
1213
use Ecotone\Messaging\Config\ServiceConfiguration;
1314
use Enqueue\Dbal\DbalConnectionFactory;
15+
use Prooph\EventStore\Pdo\PersistenceStrategy;
16+
use Prooph\EventStore\Pdo\PersistenceStrategy\MariaDbSingleStreamStrategy;
1417
use Prooph\EventStore\Pdo\PersistenceStrategy\MySqlSingleStreamStrategy;
1518
use Prooph\EventStore\Pdo\PersistenceStrategy\PostgresSingleStreamStrategy;
1619
use Test\Ecotone\EventSourcing\EventSourcingMessagingTestCase;
@@ -42,11 +45,7 @@ public function test_handling_custom_event_stream_when_custom_stream_persistence
4245
])
4346
->withExtensionObjects([
4447
EventSourcingConfiguration::createWithDefaults()
45-
->withCustomPersistenceStrategy(
46-
$this->isPostgres()
47-
? new PostgresSingleStreamStrategy(new FromProophMessageToArrayConverter())
48-
: new MySqlSingleStreamStrategy(new FromProophMessageToArrayConverter())
49-
),
48+
->withCustomPersistenceStrategy($this->persistenceStrategy()),
5049
]),
5150
pathToRootCatalog: __DIR__ . '/../../',
5251
runForProductionEventStore: true
@@ -60,8 +59,14 @@ public function test_handling_custom_event_stream_when_custom_stream_persistence
6059
self::assertEquals(2, $ecotone->sendQueryWithRouting('action_collector.getCount'));
6160
}
6261

63-
private function isPostgres(): bool
62+
private function persistenceStrategy(): PersistenceStrategy
6463
{
65-
return $this->getConnection()->getDriver() instanceof Driver;
64+
$platform = $this->getConnection()->getDatabasePlatform();
65+
66+
return match (true) {
67+
$platform instanceof PostgreSQLPlatform => new PostgresSingleStreamStrategy(new FromProophMessageToArrayConverter()),
68+
$platform instanceof MariaDBPlatform => new MariaDbSingleStreamStrategy(new FromProophMessageToArrayConverter()),
69+
default => new MySqlSingleStreamStrategy(new FromProophMessageToArrayConverter()),
70+
};
6671
}
6772
}

packages/PdoEventSourcing/tests/Integration/GapDetectionInPollingProjectionTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ protected function setUp(): void
6161

6262
$metadata = json_decode($connection->fetchOne(sprintf('select metadata from %s where no = ?', $streamName), [1]), true);
6363
$metadata['timestamp'] = $initialTimestamp;
64-
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date(DATE_ATOM, $initialTimestamp)], ['no' => 1]);
64+
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date('Y-m-d\TH:i:s', $initialTimestamp)], ['no' => 1]);
6565

6666
$metadata = json_decode($connection->fetchOne(sprintf('select metadata from %s where no = ?', $streamName), [3]), true);
6767
$metadata['timestamp'] = $initialTimestamp + 10;
68-
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date(DATE_ATOM, $initialTimestamp + 10)], ['no' => 3]);
68+
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date('Y-m-d\TH:i:s', $initialTimestamp + 10)], ['no' => 3]);
6969

7070
$metadata = json_decode($connection->fetchOne(sprintf('select metadata from %s where no = ?', $streamName), [4]), true);
7171
$metadata['timestamp'] = $initialTimestamp + 20;
72-
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date(DATE_ATOM, $initialTimestamp + 20)], ['no' => 4]);
72+
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date('Y-m-d\TH:i:s', $initialTimestamp + 20)], ['no' => 4]);
7373
}
7474

7575
public function test_detecting_gaps_without_detection_window(): void

packages/PdoEventSourcing/tests/Integration/GapDetectionInSynchronousProjectionTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ protected function setUp(): void
5858

5959
$metadata = json_decode($connection->fetchOne(sprintf('select metadata from %s where no = ?', $streamName), [1]), true);
6060
$metadata['timestamp'] = $initialTimestamp;
61-
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date(DATE_ATOM, $initialTimestamp)], ['no' => 1]);
61+
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date('Y-m-d\TH:i:s', $initialTimestamp)], ['no' => 1]);
6262

6363
$metadata = json_decode($connection->fetchOne(sprintf('select metadata from %s where no = ?', $streamName), [3]), true);
6464
$metadata['timestamp'] = $initialTimestamp + 10;
65-
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date(DATE_ATOM, $initialTimestamp + 10)], ['no' => 3]);
65+
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date('Y-m-d\TH:i:s', $initialTimestamp + 10)], ['no' => 3]);
6666

6767
$metadata = json_decode($connection->fetchOne(sprintf('select metadata from %s where no = ?', $streamName), [4]), true);
6868
$metadata['timestamp'] = $initialTimestamp + 20;
69-
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date(DATE_ATOM, $initialTimestamp + 20)], ['no' => 4]);
69+
$connection->update($streamName, ['metadata' => json_encode($metadata), 'created_at' => date('Y-m-d\TH:i:s', $initialTimestamp + 20)], ['no' => 4]);
7070
}
7171

7272
public function test_detecting_gaps_without_detection_window(): void

0 commit comments

Comments
 (0)