Translated using Weblate (Portuguese)
[phpmyadmin.git] / src / Table / Search.php
blob8c7a1879b55f045de8e5e44c3c993bf4bedbe376
1 <?php
3 declare(strict_types=1);
5 namespace PhpMyAdmin\Table;
7 use PhpMyAdmin\DatabaseInterface;
8 use PhpMyAdmin\Util;
9 use PhpMyAdmin\Utils\Gis;
11 use function count;
12 use function explode;
13 use function implode;
14 use function in_array;
15 use function is_array;
16 use function preg_match;
17 use function str_contains;
18 use function str_replace;
19 use function strncasecmp;
20 use function trim;
22 final class Search
24 public function __construct(private DatabaseInterface $dbi)
28 /**
29 * Builds the sql search query from the post parameters
31 * @return string the generated SQL query
33 public function buildSqlQuery(): string
35 $sqlQuery = 'SELECT ';
37 // If only distinct values are needed
38 $isDistinct = isset($_POST['distinct']) ? 'true' : 'false';
39 if ($isDistinct === 'true') {
40 $sqlQuery .= 'DISTINCT ';
43 // if all column names were selected to display, we do a 'SELECT *'
44 // (more efficient and this helps prevent a problem in IE
45 // if one of the rows is edited and we come back to the Select results)
46 if (isset($_POST['zoom_submit']) || ! empty($_POST['displayAllColumns'])) {
47 $sqlQuery .= '* ';
48 } else {
49 $columnsToDisplay = $_POST['columnsToDisplay'];
50 $quotedColumns = [];
51 foreach ($columnsToDisplay as $column) {
52 $quotedColumns[] = Util::backquote($column);
55 $sqlQuery .= implode(', ', $quotedColumns);
58 $sqlQuery .= ' FROM '
59 . Util::backquote($_POST['table']);
60 $whereClause = $this->generateWhereClause();
61 $sqlQuery .= $whereClause;
63 // if the search results are to be ordered
64 if (isset($_POST['orderByColumn']) && $_POST['orderByColumn'] !== '--nil--') {
65 $sqlQuery .= ' ORDER BY '
66 . Util::backquote($_POST['orderByColumn'])
67 . ' ' . $_POST['order'];
70 return $sqlQuery;
73 /**
74 * Generates the where clause for the SQL search query to be executed
76 * @return string the generated where clause
78 private function generateWhereClause(): string
80 if (isset($_POST['customWhereClause']) && trim($_POST['customWhereClause']) != '') {
81 return ' WHERE ' . $_POST['customWhereClause'];
84 if (
85 ! isset(
86 $_POST['criteriaColumnOperators'],
87 $_POST['criteriaValues'],
88 $_POST['criteriaColumnNames'],
89 $_POST['criteriaColumnTypes'],
91 ) {
92 return '';
95 // else continue to form the where clause from column criteria values
96 $fullWhereClause = [];
97 foreach ($_POST['criteriaColumnOperators'] as $columnIndex => $operator) {
98 $unaryFlag = $this->dbi->types->isUnaryOperator($operator);
99 $tmpGeomFunc = $_POST['geom_func'][$columnIndex] ?? null;
101 $whereClause = $this->getWhereClause(
102 $_POST['criteriaValues'][$columnIndex],
103 $_POST['criteriaColumnNames'][$columnIndex],
104 $_POST['criteriaColumnTypes'][$columnIndex],
105 $operator,
106 $unaryFlag,
107 $tmpGeomFunc,
110 if ($whereClause === '') {
111 continue;
114 $fullWhereClause[] = $whereClause;
117 if ($fullWhereClause !== []) {
118 return ' WHERE ' . implode(' AND ', $fullWhereClause);
121 return '';
125 * Return the where clause for query generation based on the inputs provided.
127 * @param mixed $criteriaValues Search criteria input
128 * @param string $names Name of the column on which search is submitted
129 * @param string $types Type of the field
130 * @param string $funcType Search function/operator
131 * @param bool $unaryFlag Whether operator unary or not
132 * @param string|null $geomFunc Whether geometry functions should be applied
134 * @return string generated where clause.
136 private function getWhereClause(
137 mixed $criteriaValues,
138 string $names,
139 string $types,
140 string $funcType,
141 bool $unaryFlag,
142 string|null $geomFunc = null,
143 ): string {
144 // If geometry function is set
145 if ($geomFunc !== null && $geomFunc !== '') {
146 return $this->getGeomWhereClause($criteriaValues, $names, $funcType, $types, $geomFunc);
149 $backquotedName = Util::backquote($names);
150 $where = '';
151 if ($unaryFlag) {
152 $where = $backquotedName . ' ' . $funcType;
153 } elseif (strncasecmp($types, 'enum', 4) == 0 && ! empty($criteriaValues)) {
154 $where = $backquotedName;
155 $where .= $this->getEnumWhereClause($criteriaValues, $funcType);
156 } elseif ($criteriaValues != '') {
157 // For these types we quote the value. Even if it's another type
158 // (like INT), for a LIKE we always quote the value. MySQL converts
159 // strings to numbers and numbers to strings as necessary
160 // during the comparison
161 $needsQuoting = preg_match('@char|binary|blob|text|set|date|time|year|uuid@i', $types)
162 || str_contains($funcType, 'LIKE');
164 // LIKE %...%
165 if ($funcType === 'LIKE %...%') {
166 $funcType = 'LIKE';
167 $criteriaValues = '%' . $criteriaValues . '%';
170 if ($funcType === 'NOT LIKE %...%') {
171 $funcType = 'NOT LIKE';
172 $criteriaValues = '%' . $criteriaValues . '%';
175 if ($funcType === 'REGEXP ^...$') {
176 $funcType = 'REGEXP';
177 $criteriaValues = '^' . $criteriaValues . '$';
180 if (
181 $funcType !== 'IN (...)'
182 && $funcType !== 'NOT IN (...)'
183 && $funcType !== 'BETWEEN'
184 && $funcType !== 'NOT BETWEEN'
186 return $backquotedName . ' ' . $funcType . ' '
187 . ($needsQuoting ? $this->dbi->quoteString($criteriaValues) : $criteriaValues);
190 $funcType = str_replace(' (...)', '', $funcType);
192 //Don't explode if this is already an array
193 //(Case for (NOT) IN/BETWEEN.)
194 $values = is_array($criteriaValues) ? $criteriaValues : explode(',', $criteriaValues);
196 // quote values one by one
197 $emptyKey = false;
198 foreach ($values as $key => $value) {
199 if ($value === '') {
200 $emptyKey = $key;
201 $values[$key] = 'NULL';
202 continue;
205 if (! $needsQuoting) {
206 continue;
209 $values[$key] = $this->dbi->quoteString(trim($value));
212 if ($funcType === 'BETWEEN' || $funcType === 'NOT BETWEEN') {
213 $where = $backquotedName . ' ' . $funcType . ' '
214 . ($values[0] ?? '')
215 . ' AND ' . ($values[1] ?? '');
216 } else { //[NOT] IN
217 if ($emptyKey !== false) {
218 unset($values[$emptyKey]);
221 $wheres = [];
222 if ($values !== []) {
223 $wheres[] = $backquotedName . ' ' . $funcType
224 . ' (' . implode(',', $values) . ')';
227 if ($emptyKey !== false) {
228 $wheres[] = $backquotedName . ' IS NULL';
231 $where = implode(' OR ', $wheres);
232 if (1 < count($wheres)) {
233 $where = '(' . $where . ')';
238 return $where;
242 * Return the where clause for a geometrical column.
244 * @param mixed $criteriaValues Search criteria input
245 * @param string $names Name of the column on which search is submitted
246 * @param string $funcType Search function/operator
247 * @param string $types Type of the field
248 * @param string|null $geomFunc Whether geometry functions should be applied
250 * @return string part of where clause.
252 private function getGeomWhereClause(
253 mixed $criteriaValues,
254 string $names,
255 string $funcType,
256 string $types,
257 string|null $geomFunc = null,
258 ): string {
259 $geomUnaryFunctions = ['IsEmpty' => 1, 'IsSimple' => 1, 'IsRing' => 1, 'IsClosed' => 1];
260 $where = '';
262 // Get details about the geometry functions
263 $geomFuncs = Gis::getFunctions($types);
265 // If the function takes multiple parameters
266 if (str_contains($funcType, 'IS NULL') || str_contains($funcType, 'IS NOT NULL')) {
267 return Util::backquote($names) . ' ' . $funcType;
270 if ($geomFuncs[$geomFunc]['params'] > 1) {
271 // create gis data from the criteria input
272 $gisData = Gis::createData($criteriaValues, $this->dbi->getVersion());
274 return $geomFunc . '(' . Util::backquote($names)
275 . ', ' . $gisData . ')';
278 // New output type is the output type of the function being applied
279 $type = $geomFuncs[$geomFunc]['type'];
280 $geomFunctionApplied = $geomFunc
281 . '(' . Util::backquote($names) . ')';
283 // If the where clause is something like 'IsEmpty(`spatial_col_name`)'
284 if (isset($geomUnaryFunctions[$geomFunc]) && trim($criteriaValues) == '') {
285 $where = $geomFunctionApplied;
286 } elseif (in_array($type, Gis::getDataTypes(), true) && ! empty($criteriaValues)) {
287 // create gis data from the criteria input
288 $gisData = Gis::createData($criteriaValues, $this->dbi->getVersion());
289 $where = $geomFunctionApplied . ' ' . $funcType . ' ' . $gisData;
290 } elseif ($criteriaValues != '') {
291 $where = $geomFunctionApplied . ' '
292 . $funcType . " '" . $criteriaValues . "'";
295 return $where;
299 * Return the where clause in case column's type is ENUM.
301 * @param mixed $criteriaValues Search criteria input
302 * @param string $funcType Search function/operator
304 * @return string part of where clause.
306 private function getEnumWhereClause(mixed $criteriaValues, string $funcType): string
308 if (! is_array($criteriaValues)) {
309 $criteriaValues = explode(',', $criteriaValues);
312 $enumSelectedCount = count($criteriaValues);
313 if ($funcType === '=' && $enumSelectedCount > 1) {
314 $funcType = 'IN';
315 $parensOpen = '(';
316 $parensClose = ')';
317 } elseif ($funcType === '!=' && $enumSelectedCount > 1) {
318 $funcType = 'NOT IN';
319 $parensOpen = '(';
320 $parensClose = ')';
321 } else {
322 $parensOpen = '';
323 $parensClose = '';
326 $enumWhere = $this->dbi->quoteString($criteriaValues[0]);
327 for ($e = 1; $e < $enumSelectedCount; $e++) {
328 $enumWhere .= ', ' . $this->dbi->quoteString($criteriaValues[$e]);
331 return ' ' . $funcType . ' ' . $parensOpen
332 . $enumWhere . $parensClose;