Skip to content

Conversation

@jamisonbryant
Copy link
Contributor

@jamisonbryant jamisonbryant commented Jan 6, 2026

Continuation of #988

Summary

The plugin currently supports table partitioning in two scenarios:

  1. Creating a new partitioned table: partitionBy() + create() works correctly
  2. Adding partitions to an already-partitioned table - addPartitionToExisting() + save() works correctly

However, there's a third real-world scenario that wasn't supported: converting an existing non-partitioned table to a partitioned one. This is common when:

  • A table has grown large and needs partitioning for query performance
  • Migrating a legacy schema to use partitions
  • Incrementally rolling out partitioning across an existing database

Root cause: partitionBy() and addPartition() store configuration on the TableMetadata object but don't create an Action. The create() path reads this config when building CREATE TABLE, but the update() path had no corresponding action to trigger ALTER TABLE ... PARTITION BY ....

Fix

This PR adds a new SetPartitioning action that bridges this gap, enabling:

  $this->table('orders')
      ->partitionBy(Partition::TYPE_RANGE_COLUMNS, 'created')
      ->addPartition('p2023', '2024-01-01')
      ->addPartition('p2024', '2025-01-01')
      ->update();

To generate:

  ALTER TABLE `orders` PARTITION BY RANGE COLUMNS (created) (
      PARTITION `p2023` VALUES LESS THAN ('2024-01-01'),
      PARTITION `p2024` VALUES LESS THAN ('2025-01-01')
  );

dereuromark and others added 5 commits January 6, 2026 02:50
When adding multiple partitions to an existing table, MySQL requires:
ALTER TABLE foo ADD PARTITION (PARTITION p1 ..., PARTITION p2 ...)

Previously, each AddPartition action generated its own ADD PARTITION clause,
which when joined with commas resulted in invalid SQL:
ALTER TABLE foo ADD PARTITION (...), ADD PARTITION (...)

This fix:
- Batches AddPartition actions together in executeActions()
- Adds new getAddPartitionsInstructions() method to AbstractAdapter with
  a default implementation that calls the single partition method
- Overrides getAddPartitionsInstructions() in MysqlAdapter to generate
  correct batched SQL: ADD PARTITION (PARTITION p1, PARTITION p2)
- Similarly batches DropPartition actions for efficiency
- Adds gatherPartitions() to Plan.php to properly gather partition actions
- Includes extensive tests for single/multiple partition add/drop scenarios

Refs #986
Enables adding partitioning to existing non-partitioned tables using:

    $table->partitionBy(Partition::TYPE_RANGE_COLUMNS, 'created')
        ->addPartition('p2023', '2024-01-01')
        ->update();

Previously this generated no SQL. Now it properly generates:

    ALTER TABLE `table` PARTITION BY RANGE COLUMNS (created) (...)

Changes:
- Add SetPartitioning action class
- Update Plan to handle SetPartitioning in gatherPartitions()
- Add getSetPartitioningInstructions() to AbstractAdapter/MysqlAdapter
- Create SetPartitioning action in Table::executeActions() when updating
@dereuromark
Copy link
Member

dereuromark commented Jan 6, 2026

One minor gap: There's no explicit test for composite partition keys with multiple columns like ['year', 'month'], but the code supports it.

  ->partitionBy(Partition::TYPE_RANGE_COLUMNS, ['year', 'month'])
  ->addPartition('p202401', [2024, 2])

You could add

      3425 +    public function testCreateTableWithCompositePartitionKey(): void                                                                                                                                                                                                                            
      3426 +    {                                                                                                                                                                                                                                                                                           
      3427 +        // Test composite partition keys - partitioning by multiple columns                                                                                                                                                                                                                     
      3428 +        // MySQL RANGE COLUMNS supports multiple columns                                                                                                                                                                                                                                        
      3429 +        $table = new Table('composite_partitioned', ['id' => false, 'primary_key' => ['id', 'year', 'month']], $this->adapter);                                                                                                                                                                 
      3430 +        $table->addColumn('id', 'integer')                                                                                                                                                                                                                                                      
      3431 +            ->addColumn('year', 'integer')                                                                                                                                                                                                                                                      
      3432 +            ->addColumn('month', 'integer')                                                                                                                                                                                                                                                     
      3433 +            ->addColumn('data', 'string', ['limit' => 100])                                                                                                                                                                                                                                     
      3434 +            ->partitionBy(Partition::TYPE_RANGE_COLUMNS, ['year', 'month'])                                                                                                                                                                                                                     
      3435 +            ->addPartition('p202401', [2024, 2])                                                                                                                                                                                                                                                
      3436 +            ->addPartition('p202402', [2024, 3])                                                                                                                                                                                                                                                
      3437 +            ->addPartition('p202403', [2024, 4])                                                                                                                                                                                                                                                
      3438 +            ->create();                                                                                                                                                                                                                                                                         
      3439 +                                                                                                                                                                                                                                                                                                
      3440 +        $this->assertTrue($this->adapter->hasTable('composite_partitioned'));                                                                                                                                                                                                                   
      3441 +                                                                                                                                                                                                                                                                                                
      3442 +        // Verify partitioning works by inserting data into different partitions                                                                                                                                                                                                                
      3443 +        $this->adapter->execute(                                                                                                                                                                                                                                                                
      3444 +            "INSERT INTO composite_partitioned (id, year, month, data) VALUES (1, 2024, 1, 'January')",                                                                                                                                                                                         
      3445 +        );                                                                                                                                                                                                                                                                                      
      3446 +        $this->adapter->execute(                                                                                                                                                                                                                                                                
      3447 +            "INSERT INTO composite_partitioned (id, year, month, data) VALUES (2, 2024, 2, 'February')",                                                                                                                                                                                        
      3448 +        );                                                                                                                                                                                                                                                                                      
      3449 +        $this->adapter->execute(                                                                                                                                                                                                                                                                
      3450 +            "INSERT INTO composite_partitioned (id, year, month, data) VALUES (3, 2024, 3, 'March')",                                                                                                                                                                                           
      3451 +        );                                                                                                                                                                                                                                                                                      
      3452 +                                                                                                                                                                                                                                                                                                
      3453 +        $rows = $this->adapter->fetchAll('SELECT * FROM composite_partitioned ORDER BY month');                                                                                                                                                                                                 
      3454 +        $this->assertCount(3, $rows);                                                                                                                                                                                                                                                           
      3455 +        $this->assertEquals('January', $rows[0]['data']);                                                                                                                                                                                                                                       
      3456 +        $this->assertEquals('February', $rows[1]['data']);                                                                                                                                                                                                                                      
      3457 +        $this->assertEquals('March', $rows[2]['data']);                                                                                                                                                                                                                                         
      3458 +    }   

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No queries are being built when calling update() at end of chain when using partitionBy() Partitioning support - Questions for refinement

3 participants