From 87ab5bd49959823a89cc6f2b4b8b15e707a8a19e Mon Sep 17 00:00:00 2001 From: Kevin Deldycke Date: Thu, 24 Apr 2014 16:40:54 +0200 Subject: [PATCH] Check number of elements in collections. --- changelog | 4 ++++ cqlengine/columns.py | 12 +++++++++- .../tests/columns/test_container_columns.py | 24 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index 697b99f5..c2d7aa20 100644 --- a/changelog +++ b/changelog @@ -1,5 +1,9 @@ CHANGELOG +0.14.0-dev + +* Check number of elements in collections. + 0.13.0 * adding support for post batch callbacks (thanks Daniel Dotsenko github.com/dvdotsenko) * fixing sync table for tables with multiple keys (thanks Daniel Dotsenko github.com/dvdotsenko) diff --git a/cqlengine/columns.py b/cqlengine/columns.py index e78e9255..79cb3734 100644 --- a/cqlengine/columns.py +++ b/cqlengine/columns.py @@ -513,7 +513,9 @@ class Decimal(Column): class BaseContainerColumn(Column): """ - Base Container type + Base Container type for collection-like columns. + + https://cassandra.apache.org/doc/cql3/CQL.html#collections """ def __init__(self, value_type, **kwargs): @@ -537,6 +539,14 @@ class BaseContainerColumn(Column): super(BaseContainerColumn, self).__init__(**kwargs) + def validate(self, value): + value = super(BaseContainerColumn, self).validate(value) + # It is dangerous to let collections have more than 65535. + # See: https://issues.apache.org/jira/browse/CASSANDRA-5428 + if value is not None and len(value) > 65535: + raise ValidationError("Collection can't have more than 65535 elements.") + return value + def get_column_def(self): """ Returns a column definition for CQL table definition diff --git a/cqlengine/tests/columns/test_container_columns.py b/cqlengine/tests/columns/test_container_columns.py index 2f803913..63af8ab1 100644 --- a/cqlengine/tests/columns/test_container_columns.py +++ b/cqlengine/tests/columns/test_container_columns.py @@ -92,6 +92,14 @@ class TestSetColumn(BaseCassEngTestCase): with self.assertRaises(ValidationError): TestSetModel.create(int_set={'string', True}, text_set={1, 3.0}) + def test_element_count_validation(self): + """ + Tests that big collections are detected and raise an exception. + """ + TestSetModel.create(text_set={str(uuid4()) for i in range(65535)}) + with self.assertRaises(ValidationError): + TestSetModel.create(text_set={str(uuid4()) for i in range(65536)}) + def test_partial_updates(self): """ Tests that partial udpates work as expected """ m1 = TestSetModel.create(int_set={1, 2, 3, 4}) @@ -232,6 +240,14 @@ class TestListColumn(BaseCassEngTestCase): with self.assertRaises(ValidationError): TestListModel.create(int_list=['string', True], text_list=[1, 3.0]) + def test_element_count_validation(self): + """ + Tests that big collections are detected and raise an exception. + """ + TestListModel.create(text_list=[str(uuid4()) for i in range(65535)]) + with self.assertRaises(ValidationError): + TestListModel.create(text_list=[str(uuid4()) for i in range(65536)]) + def test_partial_updates(self): """ Tests that partial udpates work as expected """ final = range(10) @@ -423,6 +439,14 @@ class TestMapColumn(BaseCassEngTestCase): with self.assertRaises(ValidationError): TestMapModel.create(int_map={'key': 2, uuid4(): 'val'}, text_map={2: 5}) + def test_element_count_validation(self): + """ + Tests that big collections are detected and raise an exception. + """ + TestMapModel.create(text_map={str(uuid4()): i for i in range(65535)}) + with self.assertRaises(ValidationError): + TestMapModel.create(text_map={str(uuid4()): i for i in range(65536)}) + def test_partial_updates(self): """ Tests that partial udpates work as expected """ now = datetime.now()