Skip to content

Commit

Permalink
TASK: Core and neos schemas for pgsql and mysql
Browse files Browse the repository at this point in the history
Related: #3855

Extends the ideas of the DbalSchemaFactory and provides separate implementations for MySql/MariaDB and PostgreSQL, allowing all core tables to be created on both platforms.
  • Loading branch information
kitsunet committed Dec 13, 2024
1 parent adb6713 commit 405d66a
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -790,8 +790,7 @@ private function whenWorkspaceWasRemoved(WorkspaceWasRemoved $event): void
*/
private function determineRequiredSqlStatements(): array
{
$schemaManager = $this->dbal->createSchemaManager();
$schema = (new DoctrineDbalContentGraphSchemaBuilder($this->tableNames))->buildSchema($schemaManager);
$schema = (new DoctrineDbalContentGraphSchemaBuilder($this->tableNames))->buildSchema($this->dbal);
return DbalSchemaDiff::determineRequiredSqlStatements($this->dbal, $schema);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Neos\ContentGraph\DoctrineDbalAdapter;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
Expand All @@ -19,8 +20,6 @@
*/
class DoctrineDbalContentGraphSchemaBuilder
{
private const DEFAULT_TEXT_COLLATION = 'utf8mb4_unicode_520_ci';

public function __construct(
private readonly ContentGraphTableNames $tableNames
) {
Expand All @@ -30,27 +29,27 @@ public function __construct(
* @param AbstractSchemaManager<AbstractPlatform> $schemaManager
* @return Schema
*/
public function buildSchema(AbstractSchemaManager $schemaManager): Schema
public function buildSchema(Connection $connection): Schema

Check failure on line 32 in Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test linting-unit-functionaltests-mysql (deps: highest)

PHPDoc tag @param references unknown parameter: $schemaManager

Check failure on line 32 in Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 Test linting-unit-functionaltests-mysql (deps: highest)

PHPDoc tag @param references unknown parameter: $schemaManager
{
return DbalSchemaFactory::createSchemaWithTables($schemaManager, [
$this->createNodeTable(),
$this->createHierarchyRelationTable(),
$this->createReferenceRelationTable(),
$this->createDimensionSpacePointsTable(),
$this->createWorkspaceTable(),
$this->createContentStreamTable(),
return DbalSchemaFactory::createSchemaWithTables($connection, [
$this->createNodeTable($connection->getDatabasePlatform()),
$this->createHierarchyRelationTable($connection->getDatabasePlatform()),
$this->createReferenceRelationTable($connection->getDatabasePlatform()),
$this->createDimensionSpacePointsTable($connection->getDatabasePlatform()),
$this->createWorkspaceTable($connection->getDatabasePlatform()),
$this->createContentStreamTable($connection->getDatabasePlatform()),
]);
}

private function createNodeTable(): Table
private function createNodeTable(AbstractPlatform $platform): Table
{
$table = self::createTable($this->tableNames->node(), [
DbalSchemaFactory::columnForNodeAnchorPoint('relationanchorpoint')->setAutoincrement(true),
DbalSchemaFactory::columnForNodeAggregateId('nodeaggregateid')->setNotnull(false),
DbalSchemaFactory::columnForDimensionSpacePointHash('origindimensionspacepointhash')->setNotnull(false),
DbalSchemaFactory::columnForNodeTypeName('nodetypename'),
(new Column('name', self::type(Types::STRING)))->setLength(255)->setNotnull(false)->setPlatformOption('charset', 'ascii')->setPlatformOption('collation', 'ascii_general_ci'),
(new Column('properties', self::type(Types::TEXT)))->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
DbalSchemaFactory::columnForNodeAnchorPoint('relationanchorpoint', $platform)->setAutoincrement(true),
DbalSchemaFactory::columnForNodeAggregateId('nodeaggregateid', $platform)->setNotnull(false),
DbalSchemaFactory::columnForDimensionSpacePointHash('origindimensionspacepointhash', $platform)->setNotnull(false),
DbalSchemaFactory::columnForNodeTypeName('nodetypename', $platform),
(new Column('name', self::type(Types::STRING)))->setLength(255)->setNotnull(false),
(new Column('properties', self::type(Types::JSON)))->setNotnull(true),
(new Column('classification', self::type(Types::BINARY)))->setLength(20)->setNotnull(true),
(new Column('created', self::type(Types::DATETIME_IMMUTABLE)))->setDefault('CURRENT_TIMESTAMP')->setNotnull(true),
(new Column('originalcreated', self::type(Types::DATETIME_IMMUTABLE)))->setDefault('CURRENT_TIMESTAMP')->setNotnull(true),
Expand All @@ -64,14 +63,14 @@ private function createNodeTable(): Table
->addIndex(['nodetypename']);
}

private function createHierarchyRelationTable(): Table
private function createHierarchyRelationTable(AbstractPlatform $platform): Table
{
$table = self::createTable($this->tableNames->hierarchyRelation(), [
(new Column('position', self::type(Types::INTEGER)))->setNotnull(true),
DbalSchemaFactory::columnForContentStreamId('contentstreamid')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePointHash('dimensionspacepointhash')->setNotnull(true),
DbalSchemaFactory::columnForNodeAnchorPoint('parentnodeanchor'),
DbalSchemaFactory::columnForNodeAnchorPoint('childnodeanchor'),
DbalSchemaFactory::columnForContentStreamId('contentstreamid', $platform)->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePointHash('dimensionspacepointhash', $platform)->setNotnull(true),
DbalSchemaFactory::columnForNodeAnchorPoint('parentnodeanchor', $platform),
DbalSchemaFactory::columnForNodeAnchorPoint('childnodeanchor', $platform),
(new Column('subtreetags', self::type(Types::JSON))),
]);

Expand All @@ -84,50 +83,50 @@ private function createHierarchyRelationTable(): Table
->addIndex(['contentstreamid', 'dimensionspacepointhash']);
}

private function createDimensionSpacePointsTable(): Table
private function createDimensionSpacePointsTable(AbstractPlatform $platform): Table
{
$table = self::createTable($this->tableNames->dimensionSpacePoints(), [
DbalSchemaFactory::columnForDimensionSpacePointHash('hash')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePoint('dimensionspacepoint')->setNotnull(true)
DbalSchemaFactory::columnForDimensionSpacePointHash('hash', $platform)->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePoint('dimensionspacepoint', $platform)->setNotnull(true)
]);

return $table
->setPrimaryKey(['hash']);
}

private function createReferenceRelationTable(): Table
private function createReferenceRelationTable(AbstractPlatform $platform): Table
{
$table = self::createTable($this->tableNames->referenceRelation(), [
(new Column('name', self::type(Types::STRING)))->setLength(255)->setNotnull(true)->setPlatformOption('charset', 'ascii')->setPlatformOption('collation', 'ascii_general_ci'),
(new Column('name', self::type(Types::STRING)))->setLength(255)->setNotnull(true),
(new Column('position', self::type(Types::INTEGER)))->setNotnull(true),
DbalSchemaFactory::columnForNodeAnchorPoint('nodeanchorpoint'),
(new Column('properties', self::type(Types::TEXT)))->setNotnull(false)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
DbalSchemaFactory::columnForNodeAggregateId('destinationnodeaggregateid')->setNotnull(true)
DbalSchemaFactory::columnForNodeAnchorPoint('nodeanchorpoint', $platform),
(new Column('properties', self::type(Types::JSON)))->setNotnull(false),
DbalSchemaFactory::columnForNodeAggregateId('destinationnodeaggregateid', $platform)->setNotnull(true)
]);

return $table
->setPrimaryKey(['name', 'position', 'nodeanchorpoint']);
}

private function createWorkspaceTable(): Table
private function createWorkspaceTable(AbstractPlatform $platform): Table
{
$workspaceTable = self::createTable($this->tableNames->workspace(), [
DbalSchemaFactory::columnForWorkspaceName('name')->setNotnull(true),
DbalSchemaFactory::columnForWorkspaceName('baseWorkspaceName')->setNotnull(false),
DbalSchemaFactory::columnForContentStreamId('currentContentStreamId')->setNotNull(true),
DbalSchemaFactory::columnForWorkspaceName('name', $platform)->setNotnull(true),
DbalSchemaFactory::columnForWorkspaceName('baseWorkspaceName', $platform)->setNotnull(false),
DbalSchemaFactory::columnForContentStreamId('currentContentStreamId', $platform)->setNotNull(true),
]);

$workspaceTable->addUniqueIndex(['currentContentStreamId']);

return $workspaceTable->setPrimaryKey(['name']);
}

private function createContentStreamTable(): Table
private function createContentStreamTable(AbstractPlatform $platform): Table
{
$contentStreamTable = self::createTable($this->tableNames->contentStream(), [
DbalSchemaFactory::columnForContentStreamId('id')->setNotnull(true),
DbalSchemaFactory::columnForContentStreamId('id', $platform)->setNotnull(true),
(new Column('version', Type::getType(Types::INTEGER)))->setNotnull(true),
DbalSchemaFactory::columnForContentStreamId('sourceContentStreamId')->setNotnull(false),
DbalSchemaFactory::columnForContentStreamId('sourceContentStreamId', $platform)->setNotnull(false),
(new Column('sourceContentStreamVersion', Type::getType(Types::INTEGER)))->setNotnull(false),
(new Column('closed', Type::getType(Types::BOOLEAN)))->setNotnull(true),
(new Column('hasChanges', Type::getType(Types::BOOLEAN)))->setNotnull(true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ public function getHighestAppliedSequenceNumber(): SequenceNumber
*/
private function determineRequiredSqlStatements(): array
{
$schemaManager = $this->connection->createSchemaManager();
$tableSchema = new Table(
$this->tableName,
[
Expand All @@ -158,7 +157,7 @@ private function determineRequiredSqlStatements(): array
]
);
$tableSchema->setPrimaryKey(['subscriberid']);
$schema = DbalSchemaFactory::createSchemaWithTables($schemaManager, [$tableSchema]);
$schema = DbalSchemaFactory::createSchemaWithTables($this->connection, [$tableSchema]);
return DbalSchemaDiff::determineRequiredSqlStatements($this->connection, $schema);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace Neos\ContentRepository\Core\Infrastructure;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
Expand All @@ -23,11 +24,11 @@
* Provide doctrine DBAL column schema definitions for common types in the content repository to
* produce consistent columns across projections.
*
* @internal Because we might need to check for platform later on and generally change the input and output format of functions within.
* @internal Because platform checks are limited for now and the input and output format of functions might change.
*/
final class DbalSchemaFactory
{
private const DEFAULT_TEXT_COLLATION = 'utf8mb4_unicode_520_ci';
private const DEFAULT_MYSQL_COLLATION = 'utf8mb4_unicode_520_ci';

// This class only contains static members and should not be constructed
private function __construct()
Expand All @@ -39,12 +40,18 @@ private function __construct()
*
* @see NodeAggregateId
*/
public static function columnForNodeAggregateId(string $columnName): Column
public static function columnForNodeAggregateId(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::STRING)))
->setLength(64)
->setPlatformOption('charset', 'ascii')
->setPlatformOption('collation', 'ascii_general_ci');
$column = (new Column($columnName, Type::getType(Types::STRING)))
->setLength(64);

if ($platform instanceof AbstractMySQLPlatform) {
$column = $column
->setPlatformOption('charset', 'ascii')
->setPlatformOption('collation', 'ascii_general_ci');
}

return $column;
}

/**
Expand All @@ -57,7 +64,7 @@ public static function columnForNodeAggregateId(string $columnName): Column
*
* @see ContentStreamId
*/
public static function columnForContentStreamId(string $columnName): Column
public static function columnForContentStreamId(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::BINARY)))
->setLength(36);
Expand All @@ -71,7 +78,7 @@ public static function columnForContentStreamId(string $columnName): Column
* A simpler and faster format would be preferable though, int or a shorter binary format if possible. Fortunately
* this is a pure projection information and therefore could change by replay.
*/
public static function columnForNodeAnchorPoint(string $columnName): Column
public static function columnForNodeAnchorPoint(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::BIGINT)))
->setNotnull(true);
Expand All @@ -85,11 +92,10 @@ public static function columnForNodeAnchorPoint(string $columnName): Column
*
* @see DimensionSpacePoint
*/
public static function columnForDimensionSpacePoint(string $columnName): Column
public static function columnForDimensionSpacePoint(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::TEXT)))
->setDefault('{}')
->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION);
return (new Column($columnName, Type::getType(Types::JSON)))
->setDefault('{}');
}

/**
Expand All @@ -100,7 +106,7 @@ public static function columnForDimensionSpacePoint(string $columnName): Column
*
* @see DimensionSpacePoint
*/
public static function columnForDimensionSpacePointHash(string $columnName): Column
public static function columnForDimensionSpacePointHash(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::BINARY)))
->setLength(32)
Expand All @@ -112,39 +118,77 @@ public static function columnForDimensionSpacePointHash(string $columnName): Col
*
* @see NodeTypeName
*/
public static function columnForNodeTypeName(string $columnName): Column
public static function columnForNodeTypeName(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::STRING)))
$column = (new Column($columnName, Type::getType(Types::STRING)))
->setLength(255)
->setNotnull(true)
->setPlatformOption('charset', 'ascii')
->setPlatformOption('collation', 'ascii_general_ci');
->setNotnull(true);

if ($platform instanceof AbstractMySQLPlatform) {
$column = $column
->setPlatformOption('charset', 'ascii')
->setPlatformOption('collation', 'ascii_general_ci');
}

return $column;
}

/**
* @see WorkspaceName
*/
public static function columnForWorkspaceName(string $columnName): Column
public static function columnForWorkspaceName(string $columnName, AbstractPlatform $platform): Column
{
return (new Column($columnName, Type::getType(Types::STRING)))
$column = (new Column($columnName, Type::getType(Types::STRING)))
->setLength(WorkspaceName::MAX_LENGTH)
->setNotnull(true)
->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION);
->setNotnull(true);

if ($platform instanceof AbstractMySQLPlatform) {
$column = $column
->setPlatformOption('charset', 'ascii')
->setPlatformOption('collation', 'ascii_general_ci');
}

return $column;
}

public static function columnForGenericString(string $columnName, AbstractPlatform $platform): Column
{
$column = (new Column($columnName, Type::getType(Types::STRING)));

if ($platform instanceof AbstractMySQLPlatform) {
$column = $column->setPlatformOption('collation', self::DEFAULT_MYSQL_COLLATION);
}

return $column;
}

public static function columnForGenericText(string $columnName, AbstractPlatform $platform): Column
{
$column = (new Column($columnName, Type::getType(Types::TEXT)));
if ($platform instanceof AbstractMySQLPlatform) {
$column = $column->setPlatformOption('collation', self::DEFAULT_MYSQL_COLLATION);
}

return $column;
}

/**
* @param AbstractSchemaManager<AbstractPlatform> $schemaManager
* @param Connection $dbal
* @param Table[] $tables
* @return Schema
* @throws Exception
* @throws SchemaException
*/
public static function createSchemaWithTables(AbstractSchemaManager $schemaManager, array $tables): Schema
public static function createSchemaWithTables(Connection $dbal, array $tables): Schema
{
$schemaManager = $dbal->createSchemaManager();
$schemaConfig = $schemaManager->createSchemaConfig();
$schemaConfig->setDefaultTableOptions([
'charset' => 'utf8mb4'
]);

if ($dbal->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
$schemaConfig->setDefaultTableOptions([
'charset' => 'utf8mb4'
]);
}

return new Schema($tables, [], $schemaConfig);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ public function status(): ProjectionStatus
*/
private function determineRequiredSqlStatements(): array
{
$schemaManager = $this->dbal->createSchemaManager();
$schema = (new DocumentUriPathSchemaBuilder($this->tableNamePrefix))->buildSchema($schemaManager);
$schema = (new DocumentUriPathSchemaBuilder($this->tableNamePrefix))->buildSchema($this->dbal);
$statements = DbalSchemaDiff::determineRequiredSqlStatements($this->dbal, $schema);

return $statements;
Expand Down
Loading

0 comments on commit 405d66a

Please sign in to comment.