From cc143105b884bfcaa188c6e9f93babec5958ea87 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 12 Dec 2014 22:12:19 +0100
Subject: [PATCH] sticker/Match: add operator "EQUALS"

Mapped to "=" in the MPD protocol.  This is the first operator,
initially supporting value matches in the MPD protocol.
---
 NEWS                            |  1 +
 doc/protocol.xml                | 19 +++++++++++++++++++
 src/command/StickerCommands.cxx | 18 +++++++++++++++++-
 src/sticker/Match.hxx           |  6 ++++++
 src/sticker/StickerDatabase.cxx | 11 +++++++++--
 5 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index 6d8bb10c8..586d11eaf 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ ver 0.20 (not yet released)
   - "commands" returns playlist commands only if playlist_directory configured
   - "search"/"find" have a "window" parameter
   - report song duration with milliseconds precision
+  - "sticker find" can match sticker values
 * tags
   - ape, ogg: drop support for non-standard tag "album artist"
     affected filetypes: vorbis, flac, opus & all files with ape2 tags
diff --git a/doc/protocol.xml b/doc/protocol.xml
index 2d5979a5f..e5567c91f 100644
--- a/doc/protocol.xml
+++ b/doc/protocol.xml
@@ -2138,6 +2138,25 @@ OK
             </para>
           </listitem>
         </varlistentry>
+
+        <varlistentry id="command_sticker_find_equals">
+          <term>
+            <cmdsynopsis>
+              <command>sticker</command>
+              <arg choice="plain">find</arg>
+              <arg choice="req"><replaceable>TYPE</replaceable></arg>
+              <arg choice="req"><replaceable>URI</replaceable></arg>
+              <arg choice="req"><replaceable>NAME</replaceable></arg>
+              <arg choice="plain">=</arg>
+              <arg choice="req"><replaceable>VALUE</replaceable></arg>
+            </cmdsynopsis>
+          </term>
+          <listitem>
+            <para>
+              Searches for stickers with the given value.
+            </para>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </section>
 
diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx
index b8eee55c5..07bed032e 100644
--- a/src/command/StickerCommands.cxx
+++ b/src/command/StickerCommands.cxx
@@ -138,7 +138,8 @@ handle_sticker_song(Client &client, ConstBuffer<const char *> args)
 
 		return CommandResult::OK;
 	/* find song dir key */
-	} else if (args.size == 4 && strcmp(cmd, "find") == 0) {
+	} else if ((args.size == 4 || args.size == 6) &&
+		   strcmp(cmd, "find") == 0) {
 		/* "sticker find song a/directory name" */
 
 		const char *const base_uri = args[2];
@@ -146,6 +147,21 @@ handle_sticker_song(Client &client, ConstBuffer<const char *> args)
 		StickerOperator op = StickerOperator::EXISTS;
 		const char *value = nullptr;
 
+		if (args.size == 6) {
+			/* match the value */
+
+			const char *op_s = args[4];
+			value = args[5];
+
+			if (strcmp(op_s, "=") == 0)
+				op = StickerOperator::EQUALS;
+			else {
+				command_error(client, ACK_ERROR_ARG,
+					      "bad operator");
+				return CommandResult::ERROR;
+			}
+		}
+
 		bool success;
 		struct sticker_song_find_data data = {
 			client,
diff --git a/src/sticker/Match.hxx b/src/sticker/Match.hxx
index f91e70b40..6165ffb84 100644
--- a/src/sticker/Match.hxx
+++ b/src/sticker/Match.hxx
@@ -26,6 +26,12 @@ enum class StickerOperator {
 	 * "value" parameter is ignored (must be nullptr).
 	 */
 	EXISTS,
+
+	/**
+	 * Matches if a sticker with the specified name and value
+	 * exists.
+	 */
+	EQUALS,
 };
 
 #endif
diff --git a/src/sticker/StickerDatabase.cxx b/src/sticker/StickerDatabase.cxx
index 3c1245a6e..bd809c1d3 100644
--- a/src/sticker/StickerDatabase.cxx
+++ b/src/sticker/StickerDatabase.cxx
@@ -43,6 +43,7 @@ enum sticker_sql {
 	STICKER_SQL_DELETE,
 	STICKER_SQL_DELETE_VALUE,
 	STICKER_SQL_FIND,
+	STICKER_SQL_FIND_VALUE,
 };
 
 static const char *const sticker_sql[] = {
@@ -60,6 +61,9 @@ static const char *const sticker_sql[] = {
 	"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
 	//[STICKER_SQL_FIND] =
 	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
+
+	//[STICKER_SQL_FIND_VALUE] =
+	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",
 };
 
 static const char sticker_sql_create[] =
@@ -383,9 +387,12 @@ BindFind(const char *type, const char *base_uri, const char *name,
 	case StickerOperator::EXISTS:
 		return BindAllOrNull(error, sticker_stmt[STICKER_SQL_FIND],
 				     type, base_uri, name);
-	}
 
-	(void)value;
+	case StickerOperator::EQUALS:
+		return BindAllOrNull(error,
+				     sticker_stmt[STICKER_SQL_FIND_VALUE],
+				     type, base_uri, name, value);
+	}
 
 	assert(false);
 	gcc_unreachable();