Skip to content
Merged
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
45 changes: 45 additions & 0 deletions src/Db/Action/SetPartitioning.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);

/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace Migrations\Db\Action;

use Migrations\Db\Table\Partition;
use Migrations\Db\Table\TableMetadata;

/**
* Add partitioning to an existing non-partitioned table
*/
class SetPartitioning extends Action
{
/**
* @var \Migrations\Db\Table\Partition
*/
protected Partition $partition;

/**
* Constructor
*
* @param \Migrations\Db\Table\TableMetadata $table The table to add partitioning to
* @param \Migrations\Db\Table\Partition $partition The partition configuration
*/
public function __construct(TableMetadata $table, Partition $partition)
{
parent::__construct($table);
$this->partition = $partition;
}

/**
* Returns the partition configuration
*
* @return \Migrations\Db\Table\Partition
*/
public function getPartition(): Partition
{
return $this->partition;
}
}
102 changes: 68 additions & 34 deletions src/Db/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use Migrations\Db\Action\RemoveColumn;
use Migrations\Db\Action\RenameColumn;
use Migrations\Db\Action\RenameTable;
use Migrations\Db\Action\SetPartitioning;
use Migrations\Db\AlterInstructions;
use Migrations\Db\InsertMode;
use Migrations\Db\Literal;
Expand All @@ -45,7 +46,7 @@
use Migrations\Db\Table\Column;
use Migrations\Db\Table\ForeignKey;
use Migrations\Db\Table\Index;
use Migrations\Db\Table\PartitionDefinition;
use Migrations\Db\Table\Partition;
use Migrations\Db\Table\TableMetadata;
use Migrations\MigrationInterface;
use Migrations\SeedInterface;
Expand Down Expand Up @@ -1549,32 +1550,6 @@ public function dropCheckConstraint(string $tableName, string $constraintName):
*/
abstract protected function getDropCheckConstraintInstructions(string $tableName, string $constraintName): AlterInstructions;

/**
* Returns the instructions to add a partition to an existing partitioned table.
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param \Migrations\Db\Table\PartitionDefinition $partition The partition definition to add
* @throws \RuntimeException If partitioning is not supported
* @return \Migrations\Db\AlterInstructions
*/
protected function getAddPartitionInstructions(TableMetadata $table, PartitionDefinition $partition): AlterInstructions
{
throw new RuntimeException('Table partitioning is not supported by this adapter');
}

/**
* Returns the instructions to drop a partition from an existing partitioned table.
*
* @param string $tableName The table name
* @param string $partitionName The partition name to drop
* @throws \RuntimeException If partitioning is not supported
* @return \Migrations\Db\AlterInstructions
*/
protected function getDropPartitionInstructions(string $tableName, string $partitionName): AlterInstructions
{
throw new RuntimeException('Table partitioning is not supported by this adapter');
}

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -1656,6 +1631,12 @@ public function executeActions(TableMetadata $table, array $actions): void
{
$instructions = new AlterInstructions();

// Collect partition actions separately as they need special batching
/** @var \Migrations\Db\Table\PartitionDefinition[] $addPartitions */
$addPartitions = [];
/** @var string[] $dropPartitions */
$dropPartitions = [];

foreach ($actions as $action) {
switch (true) {
case $action instanceof AddColumn:
Expand Down Expand Up @@ -1764,17 +1745,19 @@ public function executeActions(TableMetadata $table, array $actions): void

case $action instanceof AddPartition:
/** @var \Migrations\Db\Action\AddPartition $action */
$instructions->merge($this->getAddPartitionInstructions(
$table,
$action->getPartition(),
));
$addPartitions[] = $action->getPartition();
break;

case $action instanceof DropPartition:
/** @var \Migrations\Db\Action\DropPartition $action */
$instructions->merge($this->getDropPartitionInstructions(
$table->getName(),
$action->getPartitionName(),
$dropPartitions[] = $action->getPartitionName();
break;

case $action instanceof SetPartitioning:
/** @var \Migrations\Db\Action\SetPartitioning $action */
$instructions->merge($this->getSetPartitioningInstructions(
$table,
$action->getPartition(),
));
break;

Expand All @@ -1785,6 +1768,57 @@ public function executeActions(TableMetadata $table, array $actions): void
}
}

// Handle batched partition operations
if ($addPartitions) {
$instructions->merge($this->getAddPartitionsInstructions($table, $addPartitions));
}
if ($dropPartitions) {
$instructions->merge($this->getDropPartitionsInstructions($table->getName(), $dropPartitions));
}

$this->executeAlterSteps($table->getName(), $instructions);
}

/**
* Get instructions for adding multiple partitions to an existing table.
*
* This method handles batching multiple partition additions into a single
* ALTER TABLE statement where supported by the database.
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param array<\Migrations\Db\Table\PartitionDefinition> $partitions The partitions to add
* @return \Migrations\Db\AlterInstructions
*/
protected function getAddPartitionsInstructions(TableMetadata $table, array $partitions): AlterInstructions
{
throw new RuntimeException('Table partitioning is not supported by this adapter');
}

/**
* Get instructions for dropping multiple partitions from an existing table.
*
* This method handles batching multiple partition drops into a single
* ALTER TABLE statement where supported by the database.
*
* @param string $tableName The table name
* @param array<string> $partitionNames The partition names to drop
* @return \Migrations\Db\AlterInstructions
*/
protected function getDropPartitionsInstructions(string $tableName, array $partitionNames): AlterInstructions
{
throw new RuntimeException('Table partitioning is not supported by this adapter');
}

/**
* Get instructions for adding partitioning to an existing table.
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param \Migrations\Db\Table\Partition $partition The partition configuration
* @throws \RuntimeException If partitioning is not supported
* @return \Migrations\Db\AlterInstructions
*/
protected function getSetPartitioningInstructions(TableMetadata $table, Partition $partition): AlterInstructions
{
throw new RuntimeException('Adding partitioning to existing tables is not supported by this adapter');
}
}
91 changes: 69 additions & 22 deletions src/Db/Adapter/MysqlAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -1339,18 +1339,80 @@ protected function quotePartitionValue(mixed $value): string
}

/**
* Get instructions for adding a partition to an existing table.
* Get instructions for adding partitioning to an existing table.
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param \Migrations\Db\Table\PartitionDefinition $partition The partition to add
* @param \Migrations\Db\Table\Partition $partition The partition configuration
* @return \Migrations\Db\AlterInstructions
*/
protected function getAddPartitionInstructions(TableMetadata $table, PartitionDefinition $partition): AlterInstructions
protected function getSetPartitioningInstructions(TableMetadata $table, Partition $partition): AlterInstructions
{
$sql = $this->getPartitionSqlDefinition($partition);

return new AlterInstructions([$sql]);
}

/**
* Get instructions for adding multiple partitions to an existing table.
*
* MySQL requires all partitions in a single ADD PARTITION clause:
* ADD PARTITION (PARTITION p1 ..., PARTITION p2 ...)
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param array<\Migrations\Db\Table\PartitionDefinition> $partitions The partitions to add
* @return \Migrations\Db\AlterInstructions
*/
protected function getAddPartitionsInstructions(TableMetadata $table, array $partitions): AlterInstructions
{
if (empty($partitions)) {
return new AlterInstructions();
}

$partitionDefs = [];
foreach ($partitions as $partition) {
$partitionDefs[] = $this->getAddPartitionSql($partition);
}

$sql = 'ADD PARTITION (' . implode(', ', $partitionDefs) . ')';

return new AlterInstructions([$sql]);
}

/**
* Get instructions for dropping multiple partitions from an existing table.
*
* MySQL allows dropping multiple partitions in a single statement:
* DROP PARTITION p1, p2, p3
*
* @param string $tableName The table name
* @param array<string> $partitionNames The partition names to drop
* @return \Migrations\Db\AlterInstructions
*/
protected function getDropPartitionsInstructions(string $tableName, array $partitionNames): AlterInstructions
{
if (empty($partitionNames)) {
return new AlterInstructions();
}

$quotedNames = array_map(fn($name) => $this->quoteColumnName($name), $partitionNames);
$sql = 'DROP PARTITION ' . implode(', ', $quotedNames);

return new AlterInstructions([$sql]);
}

/**
* Generate the SQL definition for a single partition when adding to existing table.
*
* This method is used when adding partitions to an existing table and must
* infer the partition type from the value format since we don't have table metadata.
*
* @param \Migrations\Db\Table\PartitionDefinition $partition The partition definition
* @return string
*/
protected function getAddPartitionSql(PartitionDefinition $partition): string
{
// For MySQL, we need to know the partition type to generate correct SQL
// This is a simplified version - in practice you'd need to query the table's partition type
$value = $partition->getValue();
$sql = 'ADD PARTITION (PARTITION ' . $this->quoteColumnName($partition->getName());
$sql = 'PARTITION ' . $this->quoteColumnName($partition->getName());

// Detect RANGE vs LIST based on value type (simplified heuristic)
if ($value === 'MAXVALUE' || is_scalar($value)) {
Expand All @@ -1370,23 +1432,8 @@ protected function getAddPartitionInstructions(TableMetadata $table, PartitionDe
if ($partition->getComment()) {
$sql .= ' COMMENT = ' . $this->quoteString($partition->getComment());
}
$sql .= ')';

return new AlterInstructions([$sql]);
}

/**
* Get instructions for dropping a partition from an existing table.
*
* @param string $tableName The table name
* @param string $partitionName The partition name to drop
* @return \Migrations\Db\AlterInstructions
*/
protected function getDropPartitionInstructions(string $tableName, string $partitionName): AlterInstructions
{
$sql = 'DROP PARTITION ' . $this->quoteColumnName($partitionName);

return new AlterInstructions([$sql]);
return $sql;
}

/**
Expand Down
42 changes: 38 additions & 4 deletions src/Db/Adapter/PostgresAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -1439,13 +1439,30 @@ protected function quotePartitionValue(mixed $value): string
}

/**
* Get instructions for adding a partition to an existing table.
* Get instructions for adding multiple partitions to an existing table.
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param array<\Migrations\Db\Table\PartitionDefinition> $partitions The partitions to add
* @return \Migrations\Db\AlterInstructions
*/
protected function getAddPartitionsInstructions(TableMetadata $table, array $partitions): AlterInstructions
{
$instructions = new AlterInstructions();
foreach ($partitions as $partition) {
$instructions->merge($this->getAddPartitionSql($table, $partition));
}

return $instructions;
}

/**
* Get instructions for adding a single partition to an existing table.
*
* @param \Migrations\Db\Table\TableMetadata $table The table
* @param \Migrations\Db\Table\PartitionDefinition $partition The partition to add
* @return \Migrations\Db\AlterInstructions
*/
protected function getAddPartitionInstructions(TableMetadata $table, PartitionDefinition $partition): AlterInstructions
private function getAddPartitionSql(TableMetadata $table, PartitionDefinition $partition): AlterInstructions
{
// PostgreSQL requires creating partition tables using CREATE TABLE ... PARTITION OF
// This is more complex as we need the partition type info
Expand Down Expand Up @@ -1483,13 +1500,30 @@ protected function getAddPartitionInstructions(TableMetadata $table, PartitionDe
}

/**
* Get instructions for dropping a partition from an existing table.
* Get instructions for dropping multiple partitions from an existing table.
*
* @param string $tableName The table name
* @param array<string> $partitionNames The partition names to drop
* @return \Migrations\Db\AlterInstructions
*/
protected function getDropPartitionsInstructions(string $tableName, array $partitionNames): AlterInstructions
{
$instructions = new AlterInstructions();
foreach ($partitionNames as $partitionName) {
$instructions->merge($this->getDropPartitionSql($tableName, $partitionName));
}

return $instructions;
}

/**
* Get instructions for dropping a single partition from an existing table.
*
* @param string $tableName The table name
* @param string $partitionName The partition name to drop
* @return \Migrations\Db\AlterInstructions
*/
protected function getDropPartitionInstructions(string $tableName, string $partitionName): AlterInstructions
private function getDropPartitionSql(string $tableName, string $partitionName): AlterInstructions
{
// In PostgreSQL, partitions are tables, so we drop the partition table
// The partition name is typically the table_partitionname
Expand Down
Loading