packages/ztd-query-mysqli-adapter/bench/ResultMetadataBench.php
1<?php
2
3declare(strict_types=1);
4
5namespace Bench;
6
7use Container\MySql80Container;
8use mysqli;
9use mysqli_result;
10use PhpBench\Attributes as Bench;
11use RuntimeException;
12use Testcontainers\Testcontainers;
13use ZtdQuery\Adapter\Mysqli\Driver\MysqliResultColumnExtractor;
14use ZtdQuery\Platform\MySql\MySqlResultColumnTypeResolver;
15
16/**
17 * Measures metadata adaptation independently of database round trips.
18 */
19#[Bench\BeforeMethods('setUp')]
20#[Bench\AfterMethods('tearDown')]
21#[Bench\ParamProviders('columns')]
22#[Bench\Revs(10000)]
23final class ResultMetadataBench
24{
25 private mysqli $connection;
26 private mysqli_result $result;
27 private MySqlResultColumnTypeResolver $resolver;
28
29 /**
30 * Prepare one native result; setup and cleanup are outside measured work.
31 *
32 * @param array{columns: int} $params
33 * @throws RuntimeException If the native SELECT does not produce metadata.
34 */
35 public function setUp(array $params): void
36 {
37 $container = Testcontainers::run(MySql80Container::class);
38 $this->connection = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
39 $this->connection->set_charset('utf8mb4');
40 $projection = [];
41 for ($column = 0; $column < $params['columns']; $column++) {
42 $projection[] = $column . ' AS column_' . $column;
43 }
44 $result = $this->connection->query('SELECT ' . implode(', ', $projection));
45 if (!$result instanceof mysqli_result) {
46 throw new RuntimeException('Benchmark SELECT did not return a result.');
47 }
48 $this->result = $result;
49 $this->resolver = new MySqlResultColumnTypeResolver();
50 }
51
52 /**
53 * Release native resources after timing has finished.
54 */
55 public function tearDown(): void
56 {
57 $this->result->free();
58 $this->connection->close();
59 Testcontainers::stop();
60 }
61
62 /**
63 * Exercise narrow, ordinary and wide query projections.
64 *
65 * @return iterable<string, array{columns: int}>
66 */
67 public function columns(): iterable
68 {
69 yield 'one-column' => ['columns' => 1];
70 yield 'sixteen-columns' => ['columns' => 16];
71 yield 'sixty-four-columns' => ['columns' => 64];
72 }
73
74 /**
75 * Convert field metadata using the same resolver as the production adapter.
76 */
77 public function benchExtract(): void
78 {
79 MysqliResultColumnExtractor::extract($this->result, $this->resolver);
80 }
81}
82