packages/ztd-query-pdo-adapter/bench/PdoQueryBench.php

1<?php
2
3declare(strict_types=1);
4
5namespace Bench;
6
7use PDO;
8use PDOStatement;
9use PhpBench\Attributes as Bench;
10use RuntimeException;
11use ZtdQuery\Adapter\Pdo\ZtdPdo;
12
13/**
14 * Measures the consumer's connection, query and prepared execution paths.
15 * Native schema creation and shadow fixture population run outside timing.
16 */
17final class PdoQueryBench
18{
19    private PDO $native;
20
21    private ZtdPdo $pdo;
22
23    private PDOStatement $statement;
24
25    /**
26     * Populate the shadow and prepare a reusable consumer statement.
27     *
28     * @param array{rows: int} $params
29     * @throws RuntimeException When preparation fails.
30     */
31    public function setUp(array $params): void
32    {
33        $this->native = new PDO('sqlite::memory:');
34        $this->native->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
35        $this->pdo = ZtdPdo::fromPdo($this->native);
36        $rows = [];
37        for ($id = 1; $id <= $params['rows']; $id++) {
38            $rows[] = "({$id}, 'user-{$id}')";
39        }
40        $this->pdo->exec('INSERT INTO users VALUES ' . implode(', ', $rows));
41        $statement = $this->pdo->prepare('SELECT id, name FROM users WHERE id >= ?');
42        if ($statement === false) {
43            throw new RuntimeException('Benchmark preparation failed.');
44        }
45        $this->statement = $statement;
46    }
47
48    /**
49     * Wrap an existing native connection.
50     */
51    #[Bench\BeforeMethods('setUp')]
52    #[Bench\ParamProviders('workloads')]
53    #[Bench\Revs(1000)]
54    public function benchWrapConnection(): void
55    {
56        ZtdPdo::fromPdo($this->native);
57    }
58
59    /**
60     * Rewrite and fetch a shadowed query.
61     *
62     * @throws RuntimeException When the query fails.
63     */
64    #[Bench\BeforeMethods('setUp')]
65    #[Bench\ParamProviders('workloads')]
66    #[Bench\Revs(100)]
67    public function benchQuery(): void
68    {
69        $statement = $this->pdo->query('SELECT id, name FROM users');
70        if ($statement === false) {
71            throw new RuntimeException('Benchmark query failed.');
72        }
73        $statement->fetchAll(PDO::FETCH_ASSOC);
74    }
75
76    /**
77     * Execute a prepared statement against the current shadow.
78     */
79    #[Bench\BeforeMethods('setUp')]
80    #[Bench\ParamProviders('workloads')]
81    #[Bench\Revs(100)]
82    public function benchPreparedExecution(): void
83    {
84        $this->statement->execute([1]);
85        $this->statement->fetchAll(PDO::FETCH_ASSOC);
86    }
87
88    /**
89     * Compare single-row and batch workloads.
90     *
91     * @return iterable<string, array{rows: int}>
92     */
93    public function workloads(): iterable
94    {
95        yield 'single-row' => ['rows' => 1];
96        yield 'hundred-rows' => ['rows' => 100];
97    }
98}
99