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