diff --git a/src/Search.php b/src/Search.php index c4a85238740..e378039ef4e 100644 --- a/src/Search.php +++ b/src/Search.php @@ -4744,8 +4744,52 @@ public static function addWhere($link, $nott, $itemtype, $ID, $searchtype, $val, switch ($searchtype) { case "notcontains": - $nott = !$nott; - //negated, use contains case + // FIXME + // `field LIKE '%test%'` condition is not supposed to be relevant, and can sometimes result in SQL performances issues/warnings/errors, + // or at least to unexpected results, when following datatype are used: + // - integer + // - number + // - decimal + // - count + // - mio + // - percentage + // - timestamp + // - datetime + // - date_delay + // - mac + // - color + // - language + // Values should be filtered to accept only valid pattern according to given datatype. + + if (isset($searchopt[$ID]["datatype"]) && ($searchopt[$ID]["datatype"] === 'decimal')) { + $matches = []; + if (preg_match('/^(\d+.?\d?)/', $val, $matches)) { + $val = $matches[1]; + if (!str_contains($val, '.')) { + $val .= '.'; + } + } + } + + // To search for '&' in rich text + if ( + (($searchopt[$ID]['datatype'] ?? null) === 'text') + && (($searchopt[$ID]['htmltext'] ?? null) === true) + ) { + $val = str_replace('&', '38;amp;', $val); + } + if ($should_use_subquery) { + // Subquery will be needed to get accurate results + $use_subquery_on_text_search = true; + + // Potential negation will be handled by the subquery operator + $SEARCH = self::makeTextSearch($val, false); + $subquery_operator = $nott ? "IN" : "NOT IN"; + } else { + $SEARCH = self::makeTextSearch($val, $nott); + } + break; + case "contains": // FIXME // `field LIKE '%test%'` condition is not supposed to be relevant, and can sometimes result in SQL performances issues/warnings/errors, diff --git a/tests/functional/Search.php b/tests/functional/Search.php new file mode 100644 index 00000000000..48ad7fcd1f4 --- /dev/null +++ b/tests/functional/Search.php @@ -0,0 +1,103 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace tests\units; + +use DbTestCase; + +/* Test for inc/search.class.php */ + +class Search extends DbTestCase +{ + public function testAddWhere() + { + $itemtype = \Computer::class; + $search_option_id = 2; + $output = \Search::addWhere( + '', + 0, + $itemtype, + $search_option_id, + 'equals', + 42, + 0 + ); + // Ignore leading and trailing spaces not relevant) + $output = trim($output); + $this->string($output)->isEqualTo("(`glpi_computers`.`id` = 42)"); + + $output = \Search::addWhere( + '', + 0, + $itemtype, + $search_option_id, + 'notequals', + 42, + 0 + ); + // Ignore leading and trailing spaces not relevant) + $output = trim($output); + $this->string($output)->isEqualTo("(`glpi_computers`.`id` <> 42)"); + + // This Search option needs to be a number without min or max to be handled with contains or notcontains operators + $itemtype = \Printer::class; + $search_option_id = 12; + $output = \Search::addWhere( + '', + 0, + $itemtype, + $search_option_id, + 'contains', + 42, + 0 + ); + // Ignore leading and trailing spaces not relevant) + $output = trim($output); + $this->string($output)->isEqualTo("(`glpi_printers`.`last_pages_counter` = 42)"); + + $output = \Search::addWhere( + '', + 0, + $itemtype, + $search_option_id, + 'notcontains', + 42, + 0 + ); + // Ignore leading and trailing spaces not relevant) + $output = trim($output); + $this->string($output)->isEqualTo("(`glpi_printers`.`last_pages_counter` <> 42)"); + } +}