song/Filter: operator "==" never searches substrings in filter expressions

The protocol documentation says that the difference between `find` and
`search` is that `search` is case insensitive, but that's only half
the truth: `search` also searches for sub strings instead of matching
the whole string.  This part is undocumented and unfortunate, but at
this point, we can't change it.

However leaking this surprising behavior to the new filter expressions
was a bad idea; the "==" operator should never match substrings.  For
people who need that, we should add a new operator.
This commit is contained in:
Max Kellermann 2018-11-04 13:57:34 +01:00
parent 6fe43ed969
commit ac0852b4e3
5 changed files with 22 additions and 8 deletions

1
NEWS
View File

@ -1,6 +1,7 @@
ver 0.21.1 (not yet released) ver 0.21.1 (not yet released)
* protocol * protocol
- allow escaping quotes in filter expressions - allow escaping quotes in filter expressions
- operator "==" never searches substrings in filter expressions
* decoder * decoder
- ffmpeg: fix build failure with non-standard FFmpeg installation path - ffmpeg: fix build failure with non-standard FFmpeg installation path
- flac: fix linker failure when building without FLAC support - flac: fix linker failure when building without FLAC support

View File

@ -91,8 +91,11 @@ locate_parse_type(const char *str) noexcept
SongFilter::SongFilter(TagType tag, const char *value, bool fold_case) SongFilter::SongFilter(TagType tag, const char *value, bool fold_case)
{ {
/* for compatibility with MPD 0.20 and older, "fold_case" also
switches on "substring" */
and_filter.AddItem(std::make_unique<TagSongFilter>(tag, value, and_filter.AddItem(std::make_unique<TagSongFilter>(tag, value,
fold_case, false)); fold_case, fold_case,
false));
} }
SongFilter::~SongFilter() SongFilter::~SongFilter()
@ -296,11 +299,13 @@ SongFilter::ParseExpression(const char *&s, bool fold_case)
if (type == LOCATE_TAG_FILE_TYPE) if (type == LOCATE_TAG_FILE_TYPE)
return std::make_unique<UriSongFilter>(std::move(value), return std::make_unique<UriSongFilter>(std::move(value),
fold_case, fold_case,
false,
negated); negated);
return std::make_unique<TagSongFilter>(TagType(type), return std::make_unique<TagSongFilter>(TagType(type),
std::move(value), std::move(value),
fold_case, negated); fold_case, false,
negated);
} }
} }
@ -325,7 +330,10 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
break; break;
case LOCATE_TAG_FILE_TYPE: case LOCATE_TAG_FILE_TYPE:
/* for compatibility with MPD 0.20 and older,
"fold_case" also switches on "substring" */
and_filter.AddItem(std::make_unique<UriSongFilter>(value, and_filter.AddItem(std::make_unique<UriSongFilter>(value,
fold_case,
fold_case, fold_case,
false)); false));
break; break;
@ -334,9 +342,12 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
if (tag == LOCATE_TAG_ANY_TYPE) if (tag == LOCATE_TAG_ANY_TYPE)
tag = TAG_NUM_OF_ITEM_TYPES; tag = TAG_NUM_OF_ITEM_TYPES;
/* for compatibility with MPD 0.20 and older,
"fold_case" also switches on "substring" */
and_filter.AddItem(std::make_unique<TagSongFilter>(TagType(tag), and_filter.AddItem(std::make_unique<TagSongFilter>(TagType(tag),
value, value,
fold_case, fold_case,
fold_case,
false)); false));
break; break;
} }

View File

@ -40,12 +40,12 @@ class StringFilter {
public: public:
template<typename V> template<typename V>
StringFilter(V &&_value, bool _fold_case) StringFilter(V &&_value, bool _fold_case, bool _substring)
:value(std::forward<V>(_value)), :value(std::forward<V>(_value)),
fold_case(_fold_case fold_case(_fold_case
? IcuCompare(value.c_str()) ? IcuCompare(value.c_str())
: IcuCompare()), : IcuCompare()),
substring(_fold_case) {} substring(_substring) {}
bool empty() const noexcept { bool empty() const noexcept {
return value.empty(); return value.empty();

View File

@ -42,9 +42,10 @@ class TagSongFilter final : public ISongFilter {
public: public:
template<typename V> template<typename V>
TagSongFilter(TagType _type, V &&_value, bool fold_case, bool _negated) TagSongFilter(TagType _type, V &&_value, bool fold_case, bool substring,
bool _negated)
:type(_type), negated(_negated), :type(_type), negated(_negated),
filter(std::forward<V>(_value), fold_case) {} filter(std::forward<V>(_value), fold_case, substring) {}
TagType GetTagType() const { TagType GetTagType() const {
return type; return type;

View File

@ -32,8 +32,9 @@ class UriSongFilter final : public ISongFilter {
public: public:
template<typename V> template<typename V>
UriSongFilter(V &&_value, bool fold_case, bool _negated) UriSongFilter(V &&_value, bool fold_case, bool substring,
:filter(std::forward<V>(_value), fold_case), bool _negated)
:filter(std::forward<V>(_value), fold_case, substring),
negated(_negated) {} negated(_negated) {}
const auto &GetValue() const noexcept { const auto &GetValue() const noexcept {