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