def update(self): """ updates a row. This is a blind update call. All validation and cleaning needs to happen prior to calling this. """ if self.instance is None: raise CQLEngineException("DML Query intance attribute is None") assert type(self.instance) == self.model static_update_only = True statement = UpdateStatement(self.column_family_name, ttl=self._ttl, timestamp=self._timestamp, transactions=self._transaction) #get defined fields and their column names for name, col in self.model._columns.items(): if not col.is_primary_key: val = getattr(self.instance, name, None) val_mgr = self.instance._values[name] # don't update something that is null if val is None: continue # don't update something if it hasn't changed if not val_mgr.changed and not isinstance(col, Counter): continue static_update_only = (static_update_only and col.static) if isinstance(col, (BaseContainerColumn, Counter)): # get appropriate clause if isinstance(col, List): klass = ListUpdateClause elif isinstance(col, Map): klass = MapUpdateClause elif isinstance(col, Set): klass = SetUpdateClause elif isinstance(col, Counter): klass = CounterUpdateClause else: raise RuntimeError # do the stuff clause = klass(col.db_field_name, val, previous=val_mgr.previous_value, column=col) if clause.get_context_size() > 0: statement.add_assignment_clause(clause) else: statement.add_assignment_clause(AssignmentClause( col.db_field_name, col.to_database(val) )) if statement.get_context_size() > 0 or self.instance._has_counter: for name, col in self.model._primary_keys.items(): if static_update_only and (not col.partition_key): continue statement.add_where_clause(WhereClause( col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name)) )) self._execute(statement) self._delete_null_columns()
def filter(self, *args, **kwargs): """ Adds WHERE arguments to the queryset, returning a new queryset #TODO: show examples :rtype: AbstractQuerySet """ #add arguments to the where clause filters if len([x for x in kwargs.values() if x is None]): raise CQLEngineException("None values on filter are not allowed") clone = copy.deepcopy(self) for operator in args: if not isinstance(operator, WhereClause): raise QueryException('{} is not a valid query operator'.format(operator)) clone._where.append(operator) for arg, val in kwargs.items(): col_name, col_op = self._parse_filter_arg(arg) quote_field = True #resolve column and operator try: column = self.model._get_column(col_name) except KeyError: if col_name == 'pk__token': if not isinstance(val, Token): raise QueryException("Virtual column 'pk__token' may only be compared to Token() values") column = columns._PartitionKeysToken(self.model) quote_field = False else: raise QueryException("Can't resolve column name: '{}'".format(col_name)) if isinstance(val, Token): if col_name != 'pk__token': raise QueryException("Token() values may only be compared to the 'pk__token' virtual column") partition_columns = column.partition_columns if len(partition_columns) != len(val.value): raise QueryException( 'Token() received {} arguments but model has {} partition keys'.format( len(val.value), len(partition_columns))) val.set_columns(partition_columns) #get query operator, or use equals if not supplied operator_class = BaseWhereOperator.get_operator(col_op or 'EQ') operator = operator_class() if isinstance(operator, InOperator): if not isinstance(val, (list, tuple)): raise QueryException('IN queries must use a list/tuple value') query_val = [column.to_database(v) for v in val] elif isinstance(val, BaseQueryFunction): query_val = val else: query_val = column.to_database(val) clone._where.append(WhereClause(column.db_field_name, operator, query_val, quote_field=quote_field)) return clone
def test_count(self): ss = SelectStatement('table', count=True, limit=10, order_by='d') ss.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual( six.text_type(ss), 'SELECT COUNT(*) FROM table WHERE "a" = %(0)s LIMIT 10', six.text_type(ss)) self.assertIn('LIMIT', six.text_type(ss)) self.assertNotIn('ORDER', six.text_type(ss))
def test_context_update(self): ds = DeleteStatement('table', None) ds.add_field(MapDeleteClause('d', {1: 2}, {1: 2, 3: 4})) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) ds.update_context_id(7) self.assertEqual(six.text_type(ds), 'DELETE "d"[%(8)s] FROM table WHERE "a" = %(7)s') self.assertEqual(ds.get_context(), {'7': 'b', '8': 3})
def test_rendering(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertEqual( six.text_type(us), 'UPDATE table SET "a" = %(0)s, "c" = %(1)s WHERE "a" = %(2)s', six.text_type(us))
def test_context_id_update(self): """ tests that the right things happen the the context id """ ss = SelectStatement('table') ss.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(ss.get_context(), {'0': 'b'}) self.assertEqual(str(ss), 'SELECT * FROM table WHERE "a" = %(0)s') ss.update_context_id(5) self.assertEqual(ss.get_context(), {'5': 'b'}) self.assertEqual(str(ss), 'SELECT * FROM table WHERE "a" = %(5)s')
def test_context_update(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) us.update_context_id(3) self.assertEqual( six.text_type(us), 'UPDATE table SET "a" = %(4)s, "c" = %(5)s WHERE "a" = %(3)s') self.assertEqual(us.get_context(), {'4': 'b', '5': 'd', '3': 'x'})
def test_mintimeuuid_function(self): """ Tests that queries with helper functions are generated properly """ now = datetime.now() where = WhereClause('time', EqualsOperator(), functions.MinTimeUUID(now)) where.set_context_id(5) self.assertEqual(str(where), '"time" = MinTimeUUID(%(5)s)') ctx = {} where.update_context(ctx) self.assertEqual(ctx, {'5': DateTime().to_database(now)})
def delete(self): """ Deletes one instance """ if self.instance is None: raise CQLEngineException("DML Query instance attribute is None") ds = DeleteStatement(self.column_family_name, timestamp=self._timestamp) for name, col in self.model._primary_keys.items(): ds.add_where_clause( WhereClause(col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name)))) self._execute(ds)
def _delete_null_columns(self): """ executes a delete query to remove columns that have changed to null """ ds = DeleteStatement(self.column_family_name) deleted_fields = False for _, v in self.instance._values.items(): col = v.column if v.deleted: ds.add_field(col.db_field_name) deleted_fields = True elif isinstance(col, Map): uc = MapDeleteClause(col.db_field_name, v.value, v.previous_value) if uc.get_context_size() > 0: ds.add_field(uc) deleted_fields = True if deleted_fields: for name, col in self.model._primary_keys.items(): ds.add_where_clause( WhereClause(col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name)))) self._execute(ds)
def __eq__(self, other): return WhereClause(six.text_type(self), EqualsOperator(), self._to_database(other))
def test_additional_rendering(self): us = UpdateStatement('table', ttl=60) us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertIn('USING TTL 60', six.text_type(us))
def test_context(self): ds = DeleteStatement('table', None) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(ds.get_context(), {'0': 'b'})
def test_equality_method(self): """ tests that 2 identical where clauses evaluate as == """ wc1 = WhereClause('a', EqualsOperator(), 'c') wc2 = WhereClause('a', EqualsOperator(), 'c') assert wc1 == wc2
def __gt__(self, other): return WhereClause(six.text_type(self), GreaterThanOperator(), self._to_database(other))
def test_context(self): ss = SelectStatement('table') ss.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(ss.get_context(), {'0': 'b'})
def test_where_clause_rendering(self): ss = SelectStatement('table') ss.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(six.text_type(ss), 'SELECT * FROM table WHERE "a" = %(0)s', six.text_type(ss))
def __le__(self, other): return WhereClause(six.text_type(self), LessThanOrEqualOperator(), self._to_database(other))
def test_context(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertEqual(us.get_context(), {'0': 'b', '1': 'd', '2': 'x'})
def test_operator_check(self): """ tests that creating a where statement with a non BaseWhereOperator object fails """ with self.assertRaises(StatementException): WhereClause('a', 'b', 'c')
def test_where_clause_rendering(self): ds = DeleteStatement('table', None) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s', six.text_type(ds))