From 4bca34d8a4d6e307ea9ee1d19c0aaf1e54df8837 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 8 Aug 2017 15:40:11 +0400 Subject: [PATCH] MDEV-12789 JSON_KEYS returns duplicate keys twice. Check for duplicating keys added. --- mysql-test/r/func_json.result | 6 +++++ mysql-test/t/func_json.test | 5 ++++ sql/item_jsonfunc.cc | 50 ++++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index d004ebde35b..aa8a2850a58 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -356,6 +356,12 @@ json_keys('foo') NULL Warnings: Warning 4038 Syntax error in JSON text in argument 1 to function 'json_keys' at position 1 +select json_keys('{"a":{"c":1, "d":2}, "b":2, "c":1, "a":3, "b":1, "c":2}'); +json_keys('{"a":{"c":1, "d":2}, "b":2, "c":1, "a":3, "b":1, "c":2}') +["a", "b", "c"] +select json_keys('{"c1": "value 1", "c1": "value 2"}'); +json_keys('{"c1": "value 1", "c1": "value 2"}') +["c1"] SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; select json_search(@j, 'one', 'abc'); json_search(@j, 'one', 'abc') diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 3bcdc6a8709..3bbda0de409 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -138,6 +138,11 @@ select json_keys('{"a":{"c":1, "d":2}, "b":2}'); select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.a"); select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.b"); select json_keys('foo'); +# +# mdev-12789 JSON_KEYS returns duplicate keys twice +# +select json_keys('{"a":{"c":1, "d":2}, "b":2, "c":1, "a":3, "b":1, "c":2}'); +select json_keys('{"c1": "value 1", "c1": "value 2"}'); SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; select json_search(@j, 'one', 'abc'); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 315443fdcd2..d871175a3ba 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -2779,6 +2779,41 @@ void Item_func_json_keys::fix_length_and_dec() } +/* + That function is for Item_func_json_keys::val_str exclusively. + It utilizes the fact the resulting string is in specific format: + ["key1", "key2"...] +*/ +static int check_key_in_list(String *res, + const uchar *key, int key_len) +{ + const uchar *c= (const uchar *) res->ptr() + 2; /* beginning '["' */ + const uchar *end= (const uchar *) res->end() - 1; /* ending '"' */ + + while (c < end) + { + int n_char; + for (n_char=0; c[n_char] != '"' && n_char < key_len; n_char++) + { + if (c[n_char] != key[n_char]) + break; + } + if (c[n_char] == '"') + { + if (n_char == key_len) + return 1; + } + else + { + while (c[n_char] != '"') + n_char++; + } + c+= n_char + 4; /* skip ', "' */ + } + return 0; +} + + String *Item_func_json_keys::val_str(String *str) { json_engine_t je; @@ -2835,6 +2870,7 @@ skip_search: while (json_scan_next(&je) == 0 && je.state != JST_OBJ_END) { const uchar *key_start, *key_end; + int key_len; switch (je.state) { @@ -2844,13 +2880,19 @@ skip_search: { key_end= je.s.c_str; } while (json_read_keyname_chr(&je) == 0); - if (je.s.error || - (n_keys > 0 && str->append(", ", 2)) || + if (je.s.error) + goto err_return; + key_len= key_end - key_start; + + if (!check_key_in_list(str, key_start, key_len)) + { + if ((n_keys > 0 && str->append(", ", 2)) || str->append("\"", 1) || - append_simple(str, key_start, key_end - key_start) || + append_simple(str, key_start, key_len) || str->append("\"", 1)) goto err_return; - n_keys++; + n_keys++; + } break; case JST_OBJ_START: case JST_ARRAY_START: