Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
71.74% |
33 / 46 |
|
58.33% |
7 / 12 |
CRAP | |
0.00% |
0 / 1 |
DeleteQueryBuilder | |
71.74% |
33 / 46 |
|
58.33% |
7 / 12 |
34.94 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
connection | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
queryInfo | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
4.05 | |||
table | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
deleteFrom | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
delete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
where | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
5.03 | |||
andWhere | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
conds | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
caller | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
42.86% |
3 / 7 |
|
0.00% |
0 / 1 |
4.68 | |||
getQueryInfo | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Wikimedia\Rdbms; |
4 | |
5 | use InvalidArgumentException; |
6 | use UnexpectedValueException; |
7 | |
8 | // Very long type annotations :( |
9 | // phpcs:disable Generic.Files.LineLength |
10 | |
11 | /** |
12 | * A query builder for DELETE queries with a fluent interface. |
13 | * |
14 | * Any particular query builder object should only be used for a single database query, |
15 | * and not be reused afterwards. However, to run multiple similar queries, |
16 | * you can create a “template” query builder to set up most of the query, |
17 | * and then clone the object (and potentially modify the clone) for each individual query. |
18 | * |
19 | * @stable to extend |
20 | * @since 1.41 |
21 | * @ingroup Database |
22 | */ |
23 | class DeleteQueryBuilder { |
24 | /** |
25 | * The table name to be passed to IDatabase::delete() |
26 | */ |
27 | private string $table = ''; |
28 | |
29 | /** |
30 | * The conditions to be passed to IDatabase::delete() |
31 | */ |
32 | private array $conds = []; |
33 | |
34 | /** |
35 | * The caller (function name) to be passed to IDatabase::delete() |
36 | */ |
37 | private string $caller = __CLASS__; |
38 | |
39 | protected IDatabase $db; |
40 | |
41 | /** |
42 | * Only for use in subclasses and Database::newDeleteQueryBuilder. |
43 | * To create a DeleteQueryBuilder instance, use `$db->newDeleteQueryBuilder()` instead. |
44 | * |
45 | * @param IDatabase $db |
46 | */ |
47 | public function __construct( IDatabase $db ) { |
48 | $this->db = $db; |
49 | } |
50 | |
51 | /** |
52 | * Change the IDatabase object the query builder is bound to. The specified |
53 | * IDatabase will subsequently be used to execute the query. |
54 | * |
55 | * @param IDatabase $db |
56 | * @return $this |
57 | */ |
58 | public function connection( IDatabase $db ): DeleteQueryBuilder { |
59 | if ( $this->db->getType() !== $db->getType() ) { |
60 | throw new InvalidArgumentException( |
61 | __METHOD__ . ' cannot switch to a database of a different type.' |
62 | ); |
63 | } |
64 | $this->db = $db; |
65 | return $this; |
66 | } |
67 | |
68 | /** |
69 | * Set the query parameters to the given values, appending to the values |
70 | * which were already set. This can be used to interface with legacy code. |
71 | * If a key is omitted, the previous value will be retained. |
72 | * |
73 | * The parameters must be formatted as required by Database::delete. |
74 | * |
75 | * @param array $info Associative array of query info, with keys: |
76 | * - table: The table name to be passed to IDatabase::delete() |
77 | * - conds: The conditions |
78 | * - caller: The caller signature |
79 | * |
80 | * @return $this |
81 | */ |
82 | public function queryInfo( array $info ): DeleteQueryBuilder { |
83 | if ( isset( $info['table'] ) ) { |
84 | $this->table( $info['table'] ); |
85 | } |
86 | if ( isset( $info['conds'] ) ) { |
87 | $this->where( $info['conds'] ); |
88 | } |
89 | if ( isset( $info['caller'] ) ) { |
90 | $this->caller( $info['caller'] ); |
91 | } |
92 | return $this; |
93 | } |
94 | |
95 | /** |
96 | * Manually set the table name to be passed to IDatabase::delete() |
97 | * |
98 | * @param string $table The unqualified name of a table |
99 | * @param-taint $table exec_sql |
100 | * @return $this |
101 | */ |
102 | public function table( string $table ): DeleteQueryBuilder { |
103 | $this->table = $table; |
104 | return $this; |
105 | } |
106 | |
107 | /** |
108 | * Set table for the query. Alias for table(). |
109 | * |
110 | * @param string $table The unqualified name of a table |
111 | * @param-taint $table exec_sql |
112 | * @return $this |
113 | */ |
114 | public function deleteFrom( string $table ): DeleteQueryBuilder { |
115 | return $this->table( $table ); |
116 | } |
117 | |
118 | /** |
119 | * Set table for the query. Alias for table(). |
120 | * |
121 | * @param string $table The unqualified name of a table |
122 | * @param-taint $table exec_sql |
123 | * @return $this |
124 | */ |
125 | public function delete( string $table ): DeleteQueryBuilder { |
126 | return $this->table( $table ); |
127 | } |
128 | |
129 | /** |
130 | * Add conditions to the query. The supplied conditions will be appended |
131 | * to the existing conditions, separated by AND. |
132 | * |
133 | * @param string|IExpression|array<string,?scalar|non-empty-array<int,?scalar>|RawSQLValue>|array<int,string|IExpression> $conds |
134 | * @param-taint $conds exec_sql_numkey |
135 | * |
136 | * May be either a string containing a single condition, or an array of |
137 | * conditions. If an array is given, the conditions constructed from each |
138 | * element are combined with AND. |
139 | * |
140 | * Array elements may take one of two forms: |
141 | * |
142 | * - Elements with a numeric key are interpreted as raw SQL fragments. |
143 | * - Elements with a string key are interpreted as equality conditions, |
144 | * where the key is the field name. |
145 | * - If the value of such an array element is a scalar (such as a |
146 | * string), it will be treated as data and thus quoted appropriately. |
147 | * If it is null, an IS NULL clause will be added. |
148 | * - If the value is an array, an IN (...) clause will be constructed |
149 | * from its non-null elements, and an IS NULL clause will be added |
150 | * if null is present, such that the field may match any of the |
151 | * elements in the array. The non-null elements will be quoted. |
152 | * |
153 | * Note that expressions are often DBMS-dependent in their syntax. |
154 | * DBMS-independent wrappers are provided for constructing several types of |
155 | * expression commonly used in condition queries. See: |
156 | * - IDatabase::buildLike() |
157 | * - IDatabase::conditional() |
158 | * |
159 | * Untrusted user input is safe in the values of string keys, however untrusted |
160 | * input must not be used in the array key names or in the values of numeric keys. |
161 | * Escaping of untrusted input used in values of numeric keys should be done via |
162 | * IDatabase::addQuotes() |
163 | * |
164 | * @return $this |
165 | */ |
166 | public function where( $conds ): DeleteQueryBuilder { |
167 | if ( is_array( $conds ) ) { |
168 | foreach ( $conds as $key => $cond ) { |
169 | if ( is_int( $key ) ) { |
170 | $this->conds[] = $cond; |
171 | } elseif ( isset( $this->conds[$key] ) ) { |
172 | // @phan-suppress-previous-line PhanTypeMismatchDimFetch |
173 | // T288882 |
174 | $this->conds[] = $this->db->makeList( |
175 | [ $key => $cond ], IDatabase::LIST_AND ); |
176 | } else { |
177 | $this->conds[$key] = $cond; |
178 | } |
179 | } |
180 | } else { |
181 | $this->conds[] = $conds; |
182 | } |
183 | return $this; |
184 | } |
185 | |
186 | /** |
187 | * Add conditions to the query. Alias for where(). |
188 | * |
189 | * @param string|IExpression|array<string,?scalar|non-empty-array<int,?scalar>|RawSQLValue>|array<int,string|IExpression> $conds |
190 | * @param-taint $conds exec_sql_numkey |
191 | * @return $this |
192 | */ |
193 | public function andWhere( $conds ): DeleteQueryBuilder { |
194 | return $this->where( $conds ); |
195 | } |
196 | |
197 | /** |
198 | * Add conditions to the query. Alias for where(). |
199 | * |
200 | * @param string|IExpression|array<string,?scalar|non-empty-array<int,?scalar>|RawSQLValue>|array<int,string|IExpression> $conds |
201 | * @param-taint $conds exec_sql_numkey |
202 | * @return $this |
203 | */ |
204 | public function conds( $conds ): DeleteQueryBuilder { |
205 | return $this->where( $conds ); |
206 | } |
207 | |
208 | /** |
209 | * Set the method name to be included in an SQL comment. |
210 | * |
211 | * @param string $fname |
212 | * @param-taint $fname exec_sql |
213 | * @return $this |
214 | */ |
215 | public function caller( string $fname ): DeleteQueryBuilder { |
216 | $this->caller = $fname; |
217 | return $this; |
218 | } |
219 | |
220 | /** |
221 | * Run the constructed DELETE query. |
222 | */ |
223 | public function execute(): void { |
224 | if ( !$this->conds ) { |
225 | throw new UnexpectedValueException( |
226 | __METHOD__ . ' expects at least one condition to be set' ); |
227 | } |
228 | if ( $this->table === '' ) { |
229 | throw new UnexpectedValueException( |
230 | __METHOD__ . ' expects table not to be empty' ); |
231 | } |
232 | $this->db->delete( $this->table, $this->conds, $this->caller ); |
233 | } |
234 | |
235 | /** |
236 | * Get an associative array describing the query in terms of its raw parameters to |
237 | * IDatabase::delete(). This can be used to interface with legacy code. |
238 | * |
239 | * @return array The query info array, with keys: |
240 | * - table: The table name |
241 | * - conds: The conditions |
242 | * - caller: The caller signature |
243 | */ |
244 | public function getQueryInfo(): array { |
245 | $info = [ |
246 | 'table' => $this->table, |
247 | 'conds' => $this->conds, |
248 | ]; |
249 | if ( $this->caller !== __CLASS__ ) { |
250 | $info['caller'] = $this->caller; |
251 | } |
252 | return $info; |
253 | } |
254 | } |