packages/ztd-query-core/src/RewriteRefusal.php
1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery;
6
7use ZtdQuery\Config\UnknownSchemaBehavior;
8use ZtdQuery\Config\UnsupportedSqlBehavior;
9use ZtdQuery\Config\ZtdConfig;
10use ZtdQuery\Connection\Exception\DatabaseException;
11use ZtdQuery\Exception\UnknownSchemaException;
12use ZtdQuery\Exception\UnsupportedSqlException;
13use ZtdQuery\Rewrite\QueryKind;
14use ZtdQuery\Rewrite\RewritePlan;
15
16/**
17 * What a session makes of a statement the rewriter would not take.
18 *
19 * A rewriter refuses for two reasons: the statement is written in a way ZTD
20 * cannot simulate, or it names a table nothing declared. What should happen
21 * then is the caller's to say, and they say it in the configuration — raise
22 * it, say something and carry on, or let the statement through untouched —
23 * so turning a refusal into a plan is a question of configuration and not of
24 * rewriting.
25 */
26final class RewriteRefusal
27{
28 /**
29 * @param ZtdConfig $config What the caller said should happen
30 */
31 public function __construct(private readonly ZtdConfig $config)
32 {
33 }
34
35 /**
36 * Answers the plan for a statement ZTD cannot simulate.
37 *
38 * @param UnsupportedSqlException $refusal Why the rewriter would not take it
39 * @param string $sql Statement as it was written
40 *
41 * @return RewritePlan A plan that does nothing, where the caller allows that
42 *
43 * @throws DatabaseException When the caller asked to be told by being refused
44 */
45 public function forUnsupported(UnsupportedSqlException $refusal, string $sql): RewritePlan
46 {
47 $behavior = $this->config->resolveUnsupportedBehavior($sql);
48 if ($behavior === UnsupportedSqlBehavior::Exception) {
49 throw new DatabaseException($refusal->getMessage(), null, 0, $refusal);
50 }
51 if ($behavior === UnsupportedSqlBehavior::Notice) {
52 trigger_error(sprintf('[ZTD Notice] Unsupported SQL ignored: %s', $sql), E_USER_NOTICE);
53 }
54
55 return new RewritePlan($sql, QueryKind::SKIPPED);
56 }
57
58 /**
59 * Answers the plan for a statement naming a table nothing declared.
60 *
61 * @param UnknownSchemaException $refusal Why the rewriter would not take it
62 * @param string $sql Statement as it was written
63 * @param string $emptyResultSelect Statement that reads nothing back, in the dialect at hand
64 *
65 * @return RewritePlan A plan that lets the statement through, or one that reads nothing back
66 *
67 * @throws DatabaseException When the caller asked to be told by being refused
68 */
69 public function forUnknownSchema(
70 UnknownSchemaException $refusal,
71 string $sql,
72 string $emptyResultSelect,
73 ): RewritePlan {
74 $behavior = $this->config->unknownSchemaBehavior();
75 if ($behavior === UnknownSchemaBehavior::Exception) {
76 throw new DatabaseException($refusal->getMessage(), null, 0, $refusal);
77 }
78 if ($behavior === UnknownSchemaBehavior::Passthrough) {
79 return new RewritePlan($sql, QueryKind::READ);
80 }
81 if ($behavior === UnknownSchemaBehavior::Notice) {
82 trigger_error(
83 sprintf('[ZTD Notice] Unknown table referenced: %s', $refusal->getIdentifier()),
84 E_USER_NOTICE,
85 );
86 }
87
88 return new RewritePlan($emptyResultSelect, QueryKind::READ);
89 }
90}
91