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