mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 10:12:41 +01:00
Tighten Ferret query parsing of empty tokens and empty functions
Summary: Ref T13509. Certain query tokens like `title:=""` are currently accepted by the parser but discarded, and have no impact on the query. This isn't desirable. Instead, require that tokens making an assertion about field content must be nonempty. Test Plan: Added unit tests, made them pass. Maniphest Tasks: T13509 Differential Revision: https://secure.phabricator.com/D21106
This commit is contained in:
parent
471e89a8b7
commit
5c30a60e30
2 changed files with 73 additions and 31 deletions
|
@ -243,13 +243,9 @@ final class PhutilSearchQueryCompiler
|
||||||
'Query contains unmatched double quotes.'));
|
'Query contains unmatched double quotes.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mode == 'operator') {
|
// If the input query has trailing space, like "a b ", we may exit the
|
||||||
throw new PhutilSearchQueryCompilerSyntaxException(
|
// parser without a final token.
|
||||||
pht(
|
if ($current_function !== null || $current_operator || $current_token) {
|
||||||
'Query contains operator ("%s") with no search term.',
|
|
||||||
implode('', $current_operator)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$token = array(
|
$token = array(
|
||||||
'operator' => $current_operator,
|
'operator' => $current_operator,
|
||||||
'quoted' => false,
|
'quoted' => false,
|
||||||
|
@ -261,16 +257,12 @@ final class PhutilSearchQueryCompiler
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokens[] = $token;
|
$tokens[] = $token;
|
||||||
|
}
|
||||||
|
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ($tokens as $token) {
|
foreach ($tokens as $token) {
|
||||||
$value = implode('', $token['value']);
|
$value = implode('', $token['value']);
|
||||||
$operator_string = implode('', $token['operator']);
|
$operator_string = implode('', $token['operator']);
|
||||||
|
|
||||||
if (!strlen($value)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$is_quoted = $token['quoted'];
|
$is_quoted = $token['quoted'];
|
||||||
|
|
||||||
switch ($operator_string) {
|
switch ($operator_string) {
|
||||||
|
@ -304,6 +296,24 @@ final class PhutilSearchQueryCompiler
|
||||||
$operator_string));
|
$operator_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strlen($value)) {
|
||||||
|
$require_value = $is_quoted;
|
||||||
|
|
||||||
|
switch ($operator) {
|
||||||
|
default:
|
||||||
|
$require_value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($require_value) {
|
||||||
|
throw new PhutilSearchQueryCompilerSyntaxException(
|
||||||
|
pht(
|
||||||
|
'Query contains a token ("%s") with no search term. Query '.
|
||||||
|
'tokens specify text to search for.',
|
||||||
|
$this->getDisplayToken($token)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$result = array(
|
$result = array(
|
||||||
'operator' => $operator,
|
'operator' => $operator,
|
||||||
'quoted' => $is_quoted,
|
'quoted' => $is_quoted,
|
||||||
|
@ -371,4 +381,23 @@ final class PhutilSearchQueryCompiler
|
||||||
return $open_quote.$value.$close_quote;
|
return $open_quote.$value.$close_quote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getDisplayToken(array $token) {
|
||||||
|
if (isset($token['function'])) {
|
||||||
|
$function = $token['function'].':';
|
||||||
|
} else {
|
||||||
|
$function = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$operator_string = implode('', $token['operator']);
|
||||||
|
|
||||||
|
$value = implode('', $token['value']);
|
||||||
|
|
||||||
|
$is_quoted = $token['quoted'];
|
||||||
|
if ($is_quoted) {
|
||||||
|
$value = $this->quoteToken($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%s%s%s', $function, $operator_string, $value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,13 @@ final class PhutilSearchQueryCompilerTestCase
|
||||||
'"cat' => false,
|
'"cat' => false,
|
||||||
'A"' => false,
|
'A"' => false,
|
||||||
'A"B"' => '+"A" +"B"',
|
'A"B"' => '+"A" +"B"',
|
||||||
|
|
||||||
|
// Trailing whitespace should be discarded.
|
||||||
|
'a b ' => '+"a" +"b"',
|
||||||
|
|
||||||
|
// Functions must have search text.
|
||||||
|
'""' => false,
|
||||||
|
'-' => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertCompileQueries($tests);
|
$this->assertCompileQueries($tests);
|
||||||
|
@ -56,7 +63,6 @@ final class PhutilSearchQueryCompilerTestCase
|
||||||
'cat -dog' => '+[cat] -[dog]',
|
'cat -dog' => '+[cat] -[dog]',
|
||||||
);
|
);
|
||||||
$this->assertCompileQueries($quote_tests, '+ -><()~*:[]&|');
|
$this->assertCompileQueries($quote_tests, '+ -><()~*:[]&|');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCompileQueriesWithStemming() {
|
public function testCompileQueriesWithStemming() {
|
||||||
|
@ -133,6 +139,9 @@ final class PhutilSearchQueryCompilerTestCase
|
||||||
'"'.$mao.'"' => array(
|
'"'.$mao.'"' => array(
|
||||||
array(null, $op_and, $mao),
|
array(null, $op_and, $mao),
|
||||||
),
|
),
|
||||||
|
'title:' => false,
|
||||||
|
'title:+' => false,
|
||||||
|
'title:+""' => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertCompileFunctionQueries($function_tests);
|
$this->assertCompileFunctionQueries($function_tests);
|
||||||
|
@ -199,6 +208,7 @@ final class PhutilSearchQueryCompilerTestCase
|
||||||
$compiler = id(new PhutilSearchQueryCompiler())
|
$compiler = id(new PhutilSearchQueryCompiler())
|
||||||
->setEnableFunctions(true);
|
->setEnableFunctions(true);
|
||||||
|
|
||||||
|
try {
|
||||||
$tokens = $compiler->newTokens($input);
|
$tokens = $compiler->newTokens($input);
|
||||||
|
|
||||||
$result = array();
|
$result = array();
|
||||||
|
@ -209,6 +219,9 @@ final class PhutilSearchQueryCompilerTestCase
|
||||||
$token->getValue(),
|
$token->getValue(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch (PhutilSearchQueryCompilerSyntaxException $ex) {
|
||||||
|
$result = false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
$expect,
|
$expect,
|
||||||
|
|
Loading…
Reference in a new issue