From 405d66a15aff978cff940ffe553eea7494367105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Fri, 13 Dec 2024 14:44:16 +0100 Subject: [PATCH 1/4] TASK: Core and neos schemas for pgsql and mysql 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. --- .../DoctrineDbalContentGraphProjection.php | 3 +- .../DoctrineDbalContentGraphSchemaBuilder.php | 73 +++++++------ .../Infrastructure/DbalCheckpointStorage.php | 3 +- .../Infrastructure/DbalSchemaFactory.php | 102 +++++++++++++----- .../Projection/DocumentUriPathProjection.php | 3 +- .../DocumentUriPathSchemaBuilder.php | 38 +++---- .../ChangeProjection.php | 15 ++- 7 files changed, 136 insertions(+), 101 deletions(-) diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php index 28aef165b3f..0810cdcf7b3 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php @@ -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); } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php index d19bb944f78..dbd4261bae1 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php @@ -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; @@ -19,8 +20,6 @@ */ class DoctrineDbalContentGraphSchemaBuilder { - private const DEFAULT_TEXT_COLLATION = 'utf8mb4_unicode_520_ci'; - public function __construct( private readonly ContentGraphTableNames $tableNames ) { @@ -30,27 +29,27 @@ public function __construct( * @param AbstractSchemaManager $schemaManager * @return Schema */ - public function buildSchema(AbstractSchemaManager $schemaManager): Schema + public function buildSchema(Connection $connection): Schema { - 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), @@ -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))), ]); @@ -84,37 +83,37 @@ 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']); @@ -122,12 +121,12 @@ private function createWorkspaceTable(): Table 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), diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalCheckpointStorage.php b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalCheckpointStorage.php index 8551d5febb8..cbd40c12cfa 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalCheckpointStorage.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalCheckpointStorage.php @@ -149,7 +149,6 @@ public function getHighestAppliedSequenceNumber(): SequenceNumber */ private function determineRequiredSqlStatements(): array { - $schemaManager = $this->connection->createSchemaManager(); $tableSchema = new Table( $this->tableName, [ @@ -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); } } diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php index 34b59dc9ab0..be3e2b3d61f 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php @@ -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; @@ -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() @@ -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; } /** @@ -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); @@ -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); @@ -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('{}'); } /** @@ -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) @@ -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 $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); } diff --git a/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php b/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php index de859059ae0..de121b50eae 100644 --- a/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php +++ b/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathProjection.php @@ -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; diff --git a/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathSchemaBuilder.php b/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathSchemaBuilder.php index e91bf7d9e9d..1205fbd87a0 100644 --- a/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathSchemaBuilder.php +++ b/Neos.Neos/Classes/FrontendRouting/Projection/DocumentUriPathSchemaBuilder.php @@ -4,9 +4,9 @@ namespace Neos\Neos\FrontendRouting\Projection; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception as DBALException; 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; @@ -17,43 +17,39 @@ class DocumentUriPathSchemaBuilder { - private const DEFAULT_TEXT_COLLATION = 'utf8mb4_unicode_520_ci'; - public function __construct( private readonly string $tableNamePrefix, ) { } /** - * @param AbstractSchemaManager $schemaManager + * @param Connection $connection * @return Schema * @throws DBALException * @throws SchemaException */ - public function buildSchema(AbstractSchemaManager $schemaManager): Schema + public function buildSchema(Connection $connection): Schema { - $schema = DbalSchemaFactory::createSchemaWithTables($schemaManager, [ - $this->createUriTable() + return DbalSchemaFactory::createSchemaWithTables($connection, [ + $this->createUriTable($connection->getDatabasePlatform()) ]); - - return $schema; } - private function createUriTable(): Table + private function createUriTable(AbstractPlatform $platform): Table { $table = new Table($this->tableNamePrefix . '_uri', [ - DbalSchemaFactory::columnForNodeAggregateId('nodeaggregateid')->setNotNull(true), - (new Column('uripath', Type::getType(Types::STRING)))->setLength(4000)->setDefault('')->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - DbalSchemaFactory::columnForDimensionSpacePointHash('dimensionspacepointhash')->setNotNull(true), + DbalSchemaFactory::columnForNodeAggregateId('nodeaggregateid', $platform)->setNotNull(true), + DbalSchemaFactory::columnForGenericString('uripath', $platform)->setLength(4000)->setDefault('')->setNotnull(true), + DbalSchemaFactory::columnForDimensionSpacePointHash('dimensionspacepointhash', $platform)->setNotNull(true), (new Column('disabled', Type::getType(Types::INTEGER)))->setLength(4)->setUnsigned(true)->setDefault(0)->setNotnull(true), - (new Column('nodeaggregateidpath', Type::getType(Types::STRING)))->setLength(4000)->setDefault('')->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - (new Column('sitenodename', Type::getType(Types::STRING)))->setLength(255)->setDefault('')->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - DbalSchemaFactory::columnForDimensionSpacePointHash('origindimensionspacepointhash')->setNotNull(true), - DbalSchemaFactory::columnForNodeAggregateId('parentnodeaggregateid')->setNotNull(false), - DbalSchemaFactory::columnForNodeAggregateId('precedingnodeaggregateid')->setNotNull(false), - DbalSchemaFactory::columnForNodeAggregateId('succeedingnodeaggregateid')->setNotNull(false), - (new Column('shortcuttarget', Type::getType(Types::STRING)))->setLength(1000)->setNotnull(false)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - DbalSchemaFactory::columnForNodeTypeName('nodetypename'), + DbalSchemaFactory::columnForGenericString('nodeaggregateidpath', $platform)->setLength(4000)->setDefault('')->setNotnull(true), + DbalSchemaFactory::columnForGenericString('sitenodename', $platform)->setLength(255)->setDefault('')->setNotnull(true), + DbalSchemaFactory::columnForDimensionSpacePointHash('origindimensionspacepointhash', $platform)->setNotNull(true), + DbalSchemaFactory::columnForNodeAggregateId('parentnodeaggregateid', $platform)->setNotNull(false), + DbalSchemaFactory::columnForNodeAggregateId('precedingnodeaggregateid', $platform)->setNotNull(false), + DbalSchemaFactory::columnForNodeAggregateId('succeedingnodeaggregateid', $platform)->setNotNull(false), + DbalSchemaFactory::columnForGenericString('shortcuttarget', $platform)->setLength(1000)->setNotnull(false), + DbalSchemaFactory::columnForNodeTypeName('nodetypename', $platform), (new Column('isplaceholder', Type::getType(Types::INTEGER)))->setLength(4)->setUnsigned(true)->setDefault(0)->setNotnull(true), ]); diff --git a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php index 34613131d9b..06bc473e255 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php +++ b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php @@ -21,7 +21,6 @@ use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\ContentStreamRemoval\Event\ContentStreamWasRemoved; @@ -120,19 +119,19 @@ public function status(): ProjectionStatus private function determineRequiredSqlStatements(): array { $connection = $this->dbal; - $schemaManager = $connection->createSchemaManager(); + $platform = $this->dbal->getDatabasePlatform(); $changeTable = new Table($this->tableNamePrefix, [ - DbalSchemaFactory::columnForContentStreamId('contentStreamId')->setNotNull(true), + DbalSchemaFactory::columnForContentStreamId('contentStreamId', $platform)->setNotNull(true), (new Column('created', Type::getType(Types::BOOLEAN)))->setNotnull(true), (new Column('changed', Type::getType(Types::BOOLEAN)))->setNotnull(true), (new Column('moved', Type::getType(Types::BOOLEAN)))->setNotnull(true), - DbalSchemaFactory::columnForNodeAggregateId('nodeAggregateId')->setNotnull(true), - DbalSchemaFactory::columnForDimensionSpacePoint('originDimensionSpacePoint')->setNotnull(false), - DbalSchemaFactory::columnForDimensionSpacePointHash('originDimensionSpacePointHash')->setNotnull(true), + DbalSchemaFactory::columnForNodeAggregateId('nodeAggregateId', $platform)->setNotnull(true), + DbalSchemaFactory::columnForDimensionSpacePoint('originDimensionSpacePoint', $platform)->setNotnull(false), + DbalSchemaFactory::columnForDimensionSpacePointHash('originDimensionSpacePointHash', $platform)->setNotnull(true), (new Column('deleted', Type::getType(Types::BOOLEAN)))->setNotnull(true), // Despite the name suggesting this might be an anchor point of sorts, this is a nodeAggregateId type - DbalSchemaFactory::columnForNodeAggregateId('removalAttachmentPoint')->setNotnull(false) + DbalSchemaFactory::columnForNodeAggregateId('removalAttachmentPoint', $platform)->setNotnull(false) ]); $changeTable->setPrimaryKey([ @@ -141,7 +140,7 @@ private function determineRequiredSqlStatements(): array 'originDimensionSpacePointHash' ]); - $schema = DbalSchemaFactory::createSchemaWithTables($schemaManager, [$changeTable]); + $schema = DbalSchemaFactory::createSchemaWithTables($connection, [$changeTable]); $statements = DbalSchemaDiff::determineRequiredSqlStatements($connection, $schema); return $statements; From 14851318d579ab34f45393253e88d711036445d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Sun, 15 Dec 2024 21:25:01 +0100 Subject: [PATCH 2/4] Add schema utility for properties Ensures mysql gets a column with a case insensitive collation for searching. --- .../src/DoctrineDbalContentGraphSchemaBuilder.php | 8 ++------ .../Classes/Infrastructure/DbalSchemaFactory.php | 11 +++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php index dbd4261bae1..77cda091e68 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php @@ -25,10 +25,6 @@ public function __construct( ) { } - /** - * @param AbstractSchemaManager $schemaManager - * @return Schema - */ public function buildSchema(Connection $connection): Schema { return DbalSchemaFactory::createSchemaWithTables($connection, [ @@ -49,7 +45,7 @@ private function createNodeTable(AbstractPlatform $platform): Table 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), + DbalSchemaFactory::columnForProperties('properties', $platform)->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), @@ -100,7 +96,7 @@ private function createReferenceRelationTable(AbstractPlatform $platform): Table (new Column('name', self::type(Types::STRING)))->setLength(255)->setNotnull(true), (new Column('position', self::type(Types::INTEGER)))->setNotnull(true), DbalSchemaFactory::columnForNodeAnchorPoint('nodeanchorpoint', $platform), - (new Column('properties', self::type(Types::JSON)))->setNotnull(false), + DbalSchemaFactory::columnForProperties('properties', $platform)->setNotnull(false), DbalSchemaFactory::columnForNodeAggregateId('destinationnodeaggregateid', $platform)->setNotnull(true) ]); diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php index be3e2b3d61f..732d5954512 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php @@ -151,6 +151,17 @@ public static function columnForWorkspaceName(string $columnName, AbstractPlatfo return $column; } + public static function columnForProperties(string $columnName, AbstractPlatform $platform): Column + { + $column = (new Column($columnName, Type::getType(Types::JSON))); + + if ($platform instanceof AbstractMySQLPlatform) { + $column = $column->setPlatformOption('collation', self::DEFAULT_MYSQL_COLLATION); + } + + return $column; + } + public static function columnForGenericString(string $columnName, AbstractPlatform $platform): Column { $column = (new Column($columnName, Type::getType(Types::STRING))); From 7e15553068c901261af55575dcf10ec69280a86a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Mon, 16 Dec 2024 09:08:49 +0100 Subject: [PATCH 3/4] Special treatment for MySQL JSON to search case insensitive --- .../Classes/Infrastructure/DbalSchemaFactory.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php index 732d5954512..e6ed0c0d070 100644 --- a/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php +++ b/Neos.ContentRepository.Core/Classes/Infrastructure/DbalSchemaFactory.php @@ -156,7 +156,8 @@ public static function columnForProperties(string $columnName, AbstractPlatform $column = (new Column($columnName, Type::getType(Types::JSON))); if ($platform instanceof AbstractMySQLPlatform) { - $column = $column->setPlatformOption('collation', self::DEFAULT_MYSQL_COLLATION); + $column = (new Column($columnName, Type::getType(Types::TEXT))) + ->setPlatformOption('collation', self::DEFAULT_MYSQL_COLLATION); } return $column; From dfc78723323e4e2e754a3bb118d7c067694adfdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Mon, 16 Dec 2024 10:01:50 +0100 Subject: [PATCH 4/4] Fix new behat testing projection --- .../Classes/TestSuite/DebugEventProjection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neos.ContentRepository.BehavioralTests/Classes/TestSuite/DebugEventProjection.php b/Neos.ContentRepository.BehavioralTests/Classes/TestSuite/DebugEventProjection.php index 556e2d34d41..99fa32562a1 100644 --- a/Neos.ContentRepository.BehavioralTests/Classes/TestSuite/DebugEventProjection.php +++ b/Neos.ContentRepository.BehavioralTests/Classes/TestSuite/DebugEventProjection.php @@ -82,7 +82,7 @@ private function determineRequiredSqlStatements(): array 'sequencenumber' ]); - $schema = DbalSchemaFactory::createSchemaWithTables($schemaManager, [$table]); + $schema = DbalSchemaFactory::createSchemaWithTables($this->dbal, [$table]); $statements = DbalSchemaDiff::determineRequiredSqlStatements($this->dbal, $schema); return $statements;