setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "✅ Database connection established\n\n"; $compiler = new PostgreSQLSchemaCompiler(); // Cleanup echo "Cleanup...\n"; $pdo->exec("DROP TABLE IF EXISTS sales_2024_q1 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS sales_2024_q2 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS sales_2024 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS customers_eu CASCADE"); $pdo->exec("DROP TABLE IF EXISTS customers_us CASCADE"); $pdo->exec("DROP TABLE IF EXISTS customers_asia CASCADE"); $pdo->exec("DROP TABLE IF EXISTS customers CASCADE"); $pdo->exec("DROP TABLE IF EXISTS orders_p0 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS orders_p1 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS orders_p2 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS orders_p3 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS orders CASCADE"); echo "✅ Cleanup complete\n\n"; // Test 1: RANGE Partitioning (by date) echo "Test 1: RANGE Partitioning\n"; echo "===========================\n"; $blueprint = new Blueprint('sales_2024'); $blueprint->bigInteger('id'); // Changed from bigIncrements to avoid auto PK $blueprint->string('product_name'); $blueprint->decimal('amount', 10, 2); $blueprint->date('sale_date'); $blueprint->primary('id', 'sale_date'); // PK must include partition key $blueprint->partitionByRange('sale_date'); $createCommand = new CreateTableCommand('sales_2024', $blueprint); $sql = $compiler->compile($createCommand); echo "Creating partitioned table:\n"; echo $sql . "\n\n"; $pdo->exec($sql); echo "✅ Partitioned table 'sales_2024' created\n\n"; // Create partitions echo "Creating partitions for Q1 and Q2:\n"; $q1Partition = Partition::range('sales_2024_q1', "FOR VALUES FROM ('2024-01-01') TO ('2024-04-01')"); $q1Sql = $q1Partition->toSql('sales_2024'); echo $q1Sql . "\n"; $pdo->exec($q1Sql); echo "✅ Partition Q1 created\n"; $q2Partition = Partition::range('sales_2024_q2', "FOR VALUES FROM ('2024-04-01') TO ('2024-07-01')"); $q2Sql = $q2Partition->toSql('sales_2024'); echo $q2Sql . "\n"; $pdo->exec($q2Sql); echo "✅ Partition Q2 created\n\n"; // Insert test data echo "Inserting test data:\n"; $pdo->exec(" INSERT INTO sales_2024 (id, product_name, amount, sale_date) VALUES (1, 'Laptop', 1200.00, '2024-02-15'), (2, 'Mouse', 25.00, '2024-03-10'), (3, 'Keyboard', 75.00, '2024-05-20') "); echo "✅ 3 records inserted (2 in Q1, 1 in Q2)\n\n"; // Verify data distribution echo "Verifying partition data:\n"; $q1Count = $pdo->query("SELECT COUNT(*) FROM sales_2024_q1")->fetchColumn(); $q2Count = $pdo->query("SELECT COUNT(*) FROM sales_2024_q2")->fetchColumn(); echo " - Q1: {$q1Count} records\n"; echo " - Q2: {$q2Count} records\n"; echo ($q1Count == 2 && $q2Count == 1) ? "✅ Data correctly distributed\n\n" : "❌ Wrong distribution\n\n"; // Test 2: LIST Partitioning (by region) echo "Test 2: LIST Partitioning\n"; echo "==========================\n"; $blueprint = new Blueprint('customers'); $blueprint->bigInteger('id'); $blueprint->string('name'); $blueprint->string('email'); $blueprint->string('region', 10); $blueprint->primary('id', 'region'); // PK must include partition key $blueprint->partitionByList('region'); $createCommand = new CreateTableCommand('customers', $blueprint); $sql = $compiler->compile($createCommand); echo "Creating list-partitioned table:\n"; echo $sql . "\n\n"; $pdo->exec($sql); echo "✅ List-partitioned table 'customers' created\n\n"; // Create list partitions echo "Creating regional partitions:\n"; $euPartition = Partition::list('customers_eu', "FOR VALUES IN ('DE', 'FR', 'IT', 'ES')"); $euSql = $euPartition->toSql('customers'); echo $euSql . "\n"; $pdo->exec($euSql); echo "✅ EU partition created\n"; $usPartition = Partition::list('customers_us', "FOR VALUES IN ('US', 'CA', 'MX')"); $usSql = $usPartition->toSql('customers'); echo $usSql . "\n"; $pdo->exec($usSql); echo "✅ US partition created\n"; $asiaPartition = Partition::list('customers_asia', "FOR VALUES IN ('JP', 'CN', 'IN', 'KR')"); $asiaSql = $asiaPartition->toSql('customers'); echo $asiaSql . "\n"; $pdo->exec($asiaSql); echo "✅ ASIA partition created\n\n"; // Insert test data echo "Inserting regional customers:\n"; $pdo->exec(" INSERT INTO customers (id, name, email, region) VALUES (1, 'Hans Mueller', 'hans@example.de', 'DE'), (2, 'Pierre Dupont', 'pierre@example.fr', 'FR'), (3, 'John Smith', 'john@example.com', 'US'), (4, 'Yuki Tanaka', 'yuki@example.jp', 'JP') "); echo "✅ 4 customers inserted across regions\n\n"; // Verify regional distribution echo "Verifying regional distribution:\n"; $euCount = $pdo->query("SELECT COUNT(*) FROM customers_eu")->fetchColumn(); $usCount = $pdo->query("SELECT COUNT(*) FROM customers_us")->fetchColumn(); $asiaCount = $pdo->query("SELECT COUNT(*) FROM customers_asia")->fetchColumn(); echo " - EU: {$euCount} customers\n"; echo " - US: {$usCount} customers\n"; echo " - ASIA: {$asiaCount} customers\n"; echo ($euCount == 2 && $usCount == 1 && $asiaCount == 1) ? "✅ Regional distribution correct\n\n" : "❌ Wrong distribution\n\n"; // Test 3: HASH Partitioning (for distributed load) echo "Test 3: HASH Partitioning\n"; echo "==========================\n"; $blueprint = new Blueprint('orders'); $blueprint->bigInteger('id'); $blueprint->bigInteger('customer_id'); $blueprint->decimal('total', 10, 2); $blueprint->timestamp('created_at'); $blueprint->primary('id', 'customer_id'); // PK must include partition key $blueprint->partitionByHash('customer_id'); $createCommand = new CreateTableCommand('orders', $blueprint); $sql = $compiler->compile($createCommand); echo "Creating hash-partitioned table:\n"; echo $sql . "\n\n"; $pdo->exec($sql); echo "✅ Hash-partitioned table 'orders' created\n\n"; // Create hash partitions (4 partitions, modulus 4) echo "Creating 4 hash partitions:\n"; for ($i = 0; $i < 4; $i++) { $partition = Partition::hash("orders_p{$i}", "FOR VALUES WITH (MODULUS 4, REMAINDER {$i})"); $partSql = $partition->toSql('orders'); echo $partSql . "\n"; $pdo->exec($partSql); echo "✅ Partition p{$i} created\n"; } echo "\n"; // Insert test data echo "Inserting orders:\n"; for ($orderId = 1; $orderId <= 20; $orderId++) { $customerId = ($orderId % 10) + 1; // Distribute across 10 customers $pdo->exec(" INSERT INTO orders (id, customer_id, total, created_at) VALUES ({$orderId}, {$customerId}, " . ($orderId * 50.00) . ", NOW()) "); } echo "✅ 20 orders inserted (distributed by hash)\n\n"; // Verify hash distribution echo "Verifying hash distribution:\n"; for ($i = 0; $i < 4; $i++) { $count = $pdo->query("SELECT COUNT(*) FROM orders_p{$i}")->fetchColumn(); echo " - Partition p{$i}: {$count} orders\n"; } $totalOrders = $pdo->query("SELECT COUNT(*) FROM orders")->fetchColumn(); echo ($totalOrders == 20) ? "✅ All orders accessible via parent table\n\n" : "❌ Missing orders\n\n"; // Test 4: Check partition metadata echo "Test 4: Partition Metadata\n"; echo "===========================\n"; $stmt = $pdo->query(" SELECT inhrelid::regclass AS partition_name, inhparent::regclass AS parent_table FROM pg_inherits WHERE inhparent::regclass::text IN ('sales_2024', 'customers', 'orders') ORDER BY parent_table, partition_name "); $partitions = $stmt->fetchAll(PDO::FETCH_ASSOC); echo "Found " . count($partitions) . " partitions:\n"; foreach ($partitions as $partition) { echo " - {$partition['partition_name']} → {$partition['parent_table']}\n"; } echo (count($partitions) == 9) ? "\n✅ All partitions registered\n\n" : "\n❌ Wrong partition count\n\n"; // Cleanup echo "Cleanup...\n"; $pdo->exec("DROP TABLE IF EXISTS sales_2024 CASCADE"); $pdo->exec("DROP TABLE IF EXISTS customers CASCADE"); $pdo->exec("DROP TABLE IF EXISTS orders CASCADE"); echo "✅ Test data cleaned up\n"; echo "\n✅ All Table Partitioning tests passed!\n"; echo "\nSummary:\n"; echo "========\n"; echo "✅ RANGE partitioning works (by date)\n"; echo "✅ LIST partitioning works (by discrete values)\n"; echo "✅ HASH partitioning works (distributed load)\n"; echo "✅ Data correctly routed to partitions\n"; echo "✅ Parent table provides unified access\n"; echo "✅ Partition metadata correctly tracked\n"; } catch (\Exception $e) { echo "❌ Error: " . $e->getMessage() . "\n"; echo "Stack trace:\n" . $e->getTraceAsString() . "\n"; exit(1); }