Author: jbellis
Date: Sat May 7 02:23:47 2011
New Revision: 1100432
URL: http://svn.apache.org/viewvc?rev=1100432&view=rev
Log:
simplify python cursor design and make fetchall actually work
patch by jbellis
Removed:
cassandra/branches/cassandra-0.8/drivers/py/cql/results.py
Modified:
cassandra/branches/cassandra-0.8/drivers/py/cql/cursor.py
cassandra/branches/cassandra-0.8/drivers/py/cql/decoders.py
cassandra/branches/cassandra-0.8/test/system/test_cql.py
Modified: cassandra/branches/cassandra-0.8/drivers/py/cql/cursor.py
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/drivers/py/cql/cursor.py?rev=1100432&r1=1100431&r2=1100432&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/drivers/py/cql/cursor.py (original)
+++ cassandra/branches/cassandra-0.8/drivers/py/cql/cursor.py Sat May 7
02:23:47 2011
@@ -21,7 +21,6 @@ import zlib
import cql
from cql.marshal import prepare
from cql.decoders import SchemaDecoder
-from cql.results import ResultSet
from cql.cassandra.ttypes import (
Compression,
CqlResultType,
@@ -31,6 +30,8 @@ from cql.cassandra.ttypes import (
TApplicationException,
SchemaDisagreementException)
+_COUNT_DESCRIPTION = (None, None, None, None, None, None, None)
+
class Cursor:
_keyspace_re = re.compile("USE (\w+);?", re.I | re.M)
@@ -41,7 +42,6 @@ class Cursor:
self.open_socket = True
self.parent_connection = parent_connection
- self.result = None # Populate on execute()
self.description = None # A list of 7-tuples:
# (column_name, type_code, none, none,
# none, none, nulls_ok=True)
@@ -145,20 +145,18 @@ class Cursor:
self.decoder.schema = self.__get_schema()
if response.type == CqlResultType.ROWS:
- self.result = ResultSet(response.rows,
- self._query_ks,
- self._query_cf,
- self.decoder)
+ self.result = response.rows
self.rs_idx = 0
self.rowcount = len(self.result)
- self.description = self.result.description
+ if self.result:
+ self.description =
self.decoder.decode_description(self._query_ks, self._query_cf, self.result[0])
if response.type == CqlResultType.INT:
self.result = [(response.num,)]
self.rs_idx = 0
self.rowcount = 1
# TODO: name could be the COUNT expression
- self.description = (None, None, None, None, None, None, None)
+ self.description = _COUNT_DESCRIPTION
# 'Return values are not defined.'
return True
@@ -178,27 +176,28 @@ class Cursor:
def fetchone(self):
self.__checksock()
- ret = self.result[self.rs_idx]
+ row = self.result[self.rs_idx]
self.rs_idx += 1
- self.description = getattr(self.result, 'description',
self.description)
- return ret
+ if self.description != _COUNT_DESCRIPTION:
+ self.description = self.decoder.decode_description(self._query_ks,
self._query_cf, row)
+ return self.decoder.decode_row(self._query_ks, self._query_cf, row)
+ else:
+ return row
def fetchmany(self, size=None):
self.__checksock()
if size is None:
size = self.arraysize
- end = self.rs_idx + size
- ret = self.result[self.rs_idx:end]
- self.rs_idx = end
- self.description = getattr(self.result, 'description',
self.description)
- return ret
+ # we avoid leveraging fetchone here to avoid calling
decode_description unnecessarily
+ L = []
+ while len(L) < size and self.rs_idx < len(self.result):
+ row = self.result[self.rs_idx]
+ self.rs_idx += 1
+ L.append(self.decoder.decode_row(self._query_ks, self._query_cf,
row))
+ return L
def fetchall(self):
- self.__checksock()
- ret = self.result[self.rs_idx:]
- self.rs_idx = len(self.result)
- self.description = self.result.description
- return ret
+ return self.fetchmany(len(self.result) - self.rs_idx)
###
# Iterator extension
Modified: cassandra/branches/cassandra-0.8/drivers/py/cql/decoders.py
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/drivers/py/cql/decoders.py?rev=1100432&r1=1100431&r2=1100432&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/drivers/py/cql/decoders.py (original)
+++ cassandra/branches/cassandra-0.8/drivers/py/cql/decoders.py Sat May 7
02:23:47 2011
@@ -50,20 +50,27 @@ class SchemaDecoder(object):
return cfam["key_validation_class"]
return None
- def decode_row(self, keyspace, column_family, row):
+ def decode_description(self, keyspace, column_family, row):
key_type = self.__keytype_for(keyspace, column_family)
- key = unmarshallers.get(key_type, unmarshal_noop)(row.key)
description = [(cql.ROW_KEY, key_type, None, None, None, None, None,
False)]
+ comparator = self.__comparator_for(keyspace, column_family)
+ unmarshal = unmarshallers.get(comparator, unmarshal_noop)
+ for column in row.columns:
+ description.append((unmarshal(column.name), comparator, None,
None, None, None, True))
+ return description
+
+ def decode_row(self, keyspace, column_family, row):
+ key_type = self.__keytype_for(keyspace, column_family)
+ key = unmarshallers.get(key_type, unmarshal_noop)(row.key)
comparator = self.__comparator_for(keyspace, column_family)
unmarshal = unmarshallers.get(comparator, unmarshal_noop)
values = [key]
for column in row.columns:
- description.append((unmarshal(column.name), comparator, None,
None, None, None, True))
validator = self.__validator_for(keyspace, column_family,
column.name)
if column.value is None:
values.append(None)
else:
values.append(unmarshallers.get(validator,
unmarshal_noop)(column.value))
- return description, values
+ return values
Modified: cassandra/branches/cassandra-0.8/test/system/test_cql.py
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/test/system/test_cql.py?rev=1100432&r1=1100431&r2=1100432&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/test/system/test_cql.py (original)
+++ cassandra/branches/cassandra-0.8/test/system/test_cql.py Sat May 7
02:23:47 2011
@@ -125,7 +125,7 @@ def init(keyspace="Keyspace1"):
class TestCql(ThriftTester):
def test_select_simple(self):
- "retrieve a column"
+ "single-row named column queries"
cursor = init()
cursor.execute("SELECT 'ca1' FROM StandardString1 WHERE KEY='ka'")
r = cursor.fetchone()
@@ -137,10 +137,8 @@ class TestCql(ThriftTester):
assert d[1][0] == 'ca1'
assert r[1] == 'va1'
- def test_select_columns(self):
- "retrieve multiple columns"
- cursor = init()
- # we deliberately request columns in non-comparator order
+ # retrieve multiple columns
+ # (we deliberately request columns in non-comparator order)
cursor.execute("""
SELECT ca1, col, cd1 FROM StandardString1 WHERE KEY = 'kd'
""")
@@ -157,12 +155,8 @@ class TestCql(ThriftTester):
cursor.execute("""
SELECT 4 FROM StandardLongA WHERE KEY > 'ad' AND KEY < 'ag';
""")
- keys = ['ad', 'ae', 'af']
- assert cursor.rowcount == 4
- for i in range(3):
- r = cursor.fetchone()
- assert len(r) == 2
- assert r[0] == keys[i]
+ rows = [row[0] for row in cursor.fetchall()]
+ assert ['ad', 'ae', 'af', 'ag'] == rows, rows
def test_select_row_range_with_limit(self):
"retrieve a limited range of rows with columns"
@@ -183,9 +177,18 @@ class TestCql(ThriftTester):
assert r[0] == "k%d" % (i+1)
def test_select_columns_slice(self):
- "range of columns (slice) by row"
+ "column slice tests"
cursor = init()
+ # all columns
+ cursor.execute("SELECT * FROM StandardString1 WHERE KEY = 'ka';")
+ r = cursor.fetchone()
+ assert len(r) == 3
+ cursor.execute("SELECT ''..'' FROM StandardString1 WHERE KEY = 'ka';")
+ r = cursor.fetchone()
+ assert len(r) == 3
+
+ # column subsets
cursor.execute("SELECT 1..3 FROM StandardLongA WHERE KEY = 'aa';")
assert cursor.rowcount == 1
r = cursor.fetchone()
@@ -193,7 +196,7 @@ class TestCql(ThriftTester):
assert r[1] == "1"
assert r[2] == "2"
assert r[3] == "3"
-
+
cursor.execute("SELECT 10..30 FROM StandardIntegerA WHERE KEY='k1'")
assert cursor.rowcount == 1
r = cursor.fetchone()
@@ -202,19 +205,7 @@ class TestCql(ThriftTester):
assert r[2] == "b"
assert r[3] == "c"
- def test_select_columns_slice_all(self):
- "slice all columns in a row"
- cursor = init()
- cursor.execute("SELECT * FROM StandardString1 WHERE KEY = 'ka';")
- r = cursor.fetchone()
- assert len(r) == 3
- cursor.execute("SELECT ''..'' FROM StandardString1 WHERE KEY = 'ka';")
- r = cursor.fetchone()
- assert len(r) == 3
-
- def test_select_columns_slice_with_limit(self):
- "range of columns (slice) by row with limit"
- cursor = init()
+ # range of columns (slice) by row with FIRST
cursor.execute("""
SELECT FIRST 1 1..3 FROM StandardLongA WHERE KEY = 'aa';
""")
@@ -224,6 +215,17 @@ class TestCql(ThriftTester):
assert r[0] == "aa"
assert r[1] == "1"
+ # range of columns (slice) by row reversed
+ cursor.execute("""
+ SELECT FIRST 2 REVERSED 3..1 FROM StandardLongA WHERE KEY = 'aa';
+ """)
+ assert cursor.rowcount == 1, "%d != 1" % cursor.rowcount
+ r = cursor.fetchone()
+ assert len(r) == 3
+ assert r[0] == 'aa'
+ assert r[1] == "3"
+ assert r[2] == "2"
+
def test_select_range_with_single_column_results(self):
"range should not fail when keys were not set"
cursor = init()
@@ -253,19 +255,6 @@ class TestCql(ThriftTester):
assert r[0] == "user3"
assert r[1] == None
- def test_select_columns_slice_reversed(self):
- "range of columns (slice) by row reversed"
- cursor= init()
- cursor.execute("""
- SELECT FIRST 2 REVERSED 3..1 FROM StandardLongA WHERE KEY = 'aa';
- """)
- assert cursor.rowcount == 1, "%d != 1" % cursor.rowcount
- r = cursor.fetchone()
- assert len(r) == 3
- assert r[0] == 'aa'
- assert r[1] == "3"
- assert r[2] == "2"
-
def test_error_on_multiple_key_by(self):
"ensure multiple key-bys in where clause excepts"
cursor = init()