diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp
index cf2eef82347..24b7d83f14e 100644
--- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp
+++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp
@@ -63,9 +63,9 @@ void ComplexKeyCacheDictionary::setAttributeValue(Attribute & attribute, const s
const auto str_size = string.size();
if (str_size != 0)
{
- auto string_ptr = string_arena->alloc(str_size + 1);
- std::copy(string.data(), string.data() + str_size + 1, string_ptr);
- string_ref = StringRef{string_ptr, str_size};
+ auto str_ptr = string_arena->alloc(str_size);
+ std::copy(string.data(), string.data() + str_size, str_ptr);
+ string_ref = StringRef{str_ptr, str_size};
}
else
string_ref = {};
diff --git a/dbms/tests/integration/test_cached_dictionary_string/__init__.py b/dbms/tests/integration/test_cached_dictionary_string/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/dbms/tests/integration/test_cached_dictionary_string/configs/config.xml b/dbms/tests/integration/test_cached_dictionary_string/configs/config.xml
new file mode 100644
index 00000000000..a1518083be3
--- /dev/null
+++ b/dbms/tests/integration/test_cached_dictionary_string/configs/config.xml
@@ -0,0 +1,30 @@
+
+
+
+ trace
+ /var/log/clickhouse-server/clickhouse-server.log
+ /var/log/clickhouse-server/clickhouse-server.err.log
+ 1000M
+ 10
+
+
+ 9000
+ 127.0.0.1
+
+
+
+ true
+ none
+
+ AcceptCertificateHandler
+
+
+
+
+ 500
+ 5368709120
+ ./clickhouse/
+ users.xml
+
+ /etc/clickhouse-server/config.d/*.xml
+
diff --git a/dbms/tests/integration/test_cached_dictionary_string/configs/dictionaries/complex_key_cache_string.xml b/dbms/tests/integration/test_cached_dictionary_string/configs/dictionaries/complex_key_cache_string.xml
new file mode 100644
index 00000000000..0c3ba112215
--- /dev/null
+++ b/dbms/tests/integration/test_cached_dictionary_string/configs/dictionaries/complex_key_cache_string.xml
@@ -0,0 +1,45 @@
+
+
+ radars
+
+
+
+
+ radar_id
+ String
+ False
+ False
+
+
+
+ radar_ip
+ String
+
+ False
+ True
+
+
+ client_id
+ String
+
+ False
+ True
+
+
+
+
+ 20
+
+
+ 1
+
+
diff --git a/dbms/tests/integration/test_cached_dictionary_string/configs/users.xml b/dbms/tests/integration/test_cached_dictionary_string/configs/users.xml
new file mode 100644
index 00000000000..6061af8e33d
--- /dev/null
+++ b/dbms/tests/integration/test_cached_dictionary_string/configs/users.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+ ::/0
+
+ default
+ default
+
+
+
+
+
+
+
+
diff --git a/dbms/tests/integration/test_cached_dictionary_string/test.py b/dbms/tests/integration/test_cached_dictionary_string/test.py
new file mode 100644
index 00000000000..f3381581121
--- /dev/null
+++ b/dbms/tests/integration/test_cached_dictionary_string/test.py
@@ -0,0 +1,51 @@
+import pytest
+import os
+import time
+from helpers.cluster import ClickHouseCluster
+import random
+
+SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
+cluster = ClickHouseCluster(__file__, base_configs_dir=os.path.join(SCRIPT_DIR, 'configs'))
+
+node = cluster.add_instance('node', main_configs=['configs/dictionaries/complex_key_cache_string.xml'])
+
+@pytest.fixture(scope="module")
+def started_cluster():
+ try:
+ cluster.start()
+ node.query("create table radars_table (radar_id String, radar_ip String, client_id String) engine=MergeTree() order by radar_id")
+
+ yield cluster
+ finally:
+ cluster.shutdown()
+
+
+def test_memory_consumption(started_cluster):
+ node.query("insert into radars_table select toString(rand() % 5000), '{0}', '{0}' from numbers(1000)".format('w' * 8))
+ node.query("insert into radars_table select toString(rand() % 5000), '{0}', '{0}' from numbers(1000)".format('x' * 16))
+ node.query("insert into radars_table select toString(rand() % 5000), '{0}', '{0}' from numbers(1000)".format('y' * 32))
+ node.query("insert into radars_table select toString(rand() % 5000), '{0}', '{0}' from numbers(1000)".format('z' * 64))
+
+ # Fill dictionary
+ node.query("select dictGetString('radars', 'client_id', tuple(toString(number))) from numbers(0, 5000)")
+
+ allocated_first = int(node.query("select bytes_allocated from system.dictionaries where name = 'radars'").strip())
+
+ alloc_array = []
+ for i in xrange(5):
+ node.query("select dictGetString('radars', 'client_id', tuple(toString(number))) from numbers(0, 5000)")
+
+ allocated = int(node.query("select bytes_allocated from system.dictionaries where name = 'radars'").strip())
+ alloc_array.append(allocated)
+
+ # size doesn't grow
+ assert all(allocated_first >= a for a in alloc_array)
+
+ for i in xrange(5):
+ node.query("select dictGetString('radars', 'client_id', tuple(toString(number))) from numbers(0, 5000)")
+
+ allocated = int(node.query("select bytes_allocated from system.dictionaries where name = 'radars'").strip())
+ alloc_array.append(allocated)
+
+ # size doesn't grow
+ assert all(allocated_first >= a for a in alloc_array)