You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ca...@codespot.com on 2012/02/09 23:03:02 UTC
[cassandra-dbapi2] 4 new revisions pushed by pcannon@gmail.com on
2012-02-09 22:01 GMT
4 new revisions:
Revision: 81f460c98a20
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 11:09:39 2012
Log: add marshalling capability
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=81f460c98a20
Revision: ff3c1d6228df
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 11:11:08 2012
Log: get TApplicationException from thrift, not ttypes...
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=ff3c1d6228df
Revision: f6611bc3ca30
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 11:18:45 2012
Log: add tests for marshallers
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=f6611bc3ca30
Revision: b71be13e3a41
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 13:52:06 2012
Log: support for prepared statements
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=b71be13e3a41
==============================================================================
Revision: 81f460c98a20
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 11:09:39 2012
Log: add marshalling capability
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=81f460c98a20
Modified:
/cql/marshal.py
=======================================
--- /cql/marshal.py Tue Jan 24 09:46:37 2012
+++ /cql/marshal.py Thu Feb 9 11:09:39 2012
@@ -105,68 +105,122 @@
def unmarshal_noop(bytestr):
return bytestr
+marshal_noop = unmarshal_noop
+
def unmarshal_bool(bytestr):
if not bytestr:
return None
return bool(ord(bytestr[0]))
+def marshal_bool(truth):
+ if truth is None:
+ return ''
+ return chr(bool(truth))
+
def unmarshal_utf8(bytestr):
return bytestr.decode("utf8")
+def marshal_utf8(ustr):
+ if ustr is None:
+ return ''
+ return ustr.encode('utf8')
+
if _have_struct:
def unmarshal_int32(bytestr):
if not bytestr:
return None
return _int32_packer.unpack(bytestr)[0]
+ def marshal_int32(i):
+ if i is None:
+ return ''
+ return _int32_packer.pack(i)
else:
def unmarshal_int32(bytestr):
if not bytestr:
return None
return struct.unpack(">i", bytestr)[0]
+ def marshal_int32(i):
+ if i is None:
+ return ''
+ return struct.pack('>i', i)
def unmarshal_int(bytestr):
if not bytestr:
return None
return decode_bigint(bytestr)
+def marshal_int(bigint):
+ if bigint is None:
+ return ''
+ return encode_bigint(bigint)
+
if _have_struct:
def unmarshal_long(bytestr):
if not bytestr:
return None
return _long_packer.unpack(bytestr)[0]
+ def marshal_long(longint):
+ if longint is None:
+ return ''
+ return _long_packer.pack(longint)
else:
def unmarshal_long(bytestr):
if not bytestr:
return None
return struct.unpack(">q", bytestr)[0]
+ def marshal_long(longint):
+ if longint is None:
+ return ''
+ return struct.pack(">q", longint)
if _have_struct:
def unmarshal_float(bytestr):
if not bytestr:
return None
return _float_packer.unpack(bytestr)[0]
+ def marshal_float(f):
+ if f is None:
+ return ''
+ return _float_packer.pack(f)
else:
def unmarshal_float(bytestr):
if not bytestr:
return None
return struct.unpack(">f", bytestr)[0]
+ def marshal_float(f):
+ if f is None:
+ return ''
+ return struct.pack('>f', f)
if _have_struct:
def unmarshal_double(bytestr):
if not bytestr:
return None
return _double_packer.unpack(bytestr)[0]
+ def marshal_double(d):
+ if d is None:
+ return ''
+ return _double_packer.pack(d)
else:
def unmarshal_double(bytestr):
if not bytestr:
return None
return struct.unpack(">d", bytestr)[0]
+ def marshal_double(d):
+ if d is None:
+ return ''
+ return struct.pack('>d', d)
def unmarshal_date(bytestr):
if not bytestr:
return None
return unmarshal_long(bytestr) / 1000.0
+def marshal_date(date):
+ if date is None:
+ return ''
+ return marshal_long(date * 1000)
+
def unmarshal_decimal(bytestr):
if not bytestr:
return None
@@ -174,11 +228,27 @@
unscaled = decode_bigint(bytestr[4:])
return Decimal('%de%d' % (unscaled, -scale))
+def marshal_decimal(dec):
+ if dec is None:
+ return ''
+ sign, digits, exponent = dec.as_tuple()
+ unscaled = int(''.join([str(digit) for digit in digits]))
+ if sign:
+ unscaled *= -1
+ scale = marshal_int32(-exponent)
+ unscaled = encode_bigint(unscaled)
+ return scale + unscaled
+
def unmarshal_uuid(bytestr):
if not bytestr:
return None
return UUID(bytes=bytestr)
+def marshal_uuid(uuid):
+ if uuid is None:
+ return ''
+ return uuid.bytes
+
unmarshallers = {BYTES_TYPE: unmarshal_noop,
ASCII_TYPE: unmarshal_noop,
BOOLEAN_TYPE: unmarshal_bool,
@@ -198,12 +268,53 @@
short_name = name.split('.')[-1]
unmarshallers[short_name] = typ
+marshallers = {BYTES_TYPE: marshal_noop,
+ ASCII_TYPE: marshal_noop,
+ BOOLEAN_TYPE: marshal_bool,
+ DATE_TYPE: marshal_date,
+ DECIMAL_TYPE: marshal_decimal,
+ UTF8_TYPE: marshal_utf8,
+ INT32_TYPE: marshal_int32,
+ INTEGER_TYPE: marshal_int,
+ LONG_TYPE: marshal_long,
+ FLOAT_TYPE: marshal_float,
+ DOUBLE_TYPE: marshal_double,
+ UUID_TYPE: marshal_uuid,
+ LEXICAL_UUID_TYPE: marshal_uuid,
+ TIME_UUID_TYPE: marshal_uuid,
+ COUNTER_COLUMN_TYPE: marshal_long}
+for name, typ in marshallers.items():
+ short_name = name.split('.')[-1]
+ marshallers[short_name] = typ
+
def decode_bigint(term):
val = int(term.encode('hex'), 16)
if (ord(term[0]) & 128) != 0:
val = val - (1 << (len(term) * 8))
return val
+def bitlength(n):
+ bitlen = 0
+ while n > 0:
+ n >>= 1
+ bitlen += 1
+ return bitlen
+
+def encode_bigint(big):
+ pos = True
+ if big < 0:
+ bytelength = bitlength(abs(big) - 1) / 8 + 1
+ big = (1 << bytelength * 8) + big
+ pos = False
+ revbytes = []
+ while big > 0:
+ revbytes.append(chr(big & 0xff))
+ big >>= 8
+ if pos and ord(revbytes[-1]) & 0x80:
+ revbytes.append('\x00')
+ revbytes.reverse()
+ return ''.join(revbytes)
+
def __escape_quotes(term):
assert isinstance(term, basestring)
return term.replace("'", "''")
==============================================================================
Revision: ff3c1d6228df
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 11:11:08 2012
Log: get TApplicationException from thrift, not ttypes
since it doesn't show up in ttypes anymore for all Thrift versions, and
eventually it's going to disappear. not a great idea to rely on another
module's imports, anyway.
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=ff3c1d6228df
Modified:
/cql/cursor.py
=======================================
--- /cql/cursor.py Fri Oct 7 16:06:33 2011
+++ /cql/cursor.py Thu Feb 9 11:11:08 2012
@@ -27,8 +27,8 @@
InvalidRequestException,
UnavailableException,
TimedOutException,
- TApplicationException,
SchemaDisagreementException)
+from thrift.Thrift import TApplicationException
_COUNT_DESCRIPTION = (None, None, None, None, None, None, None)
_VOID_DESCRIPTION = (None)
==============================================================================
Revision: f6611bc3ca30
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 11:18:45 2012
Log: add tests for marshallers
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=f6611bc3ca30
Added:
/test/test_marshalling.py
Deleted:
/test/test_unmarshalling.py
=======================================
--- /dev/null
+++ /test/test_marshalling.py Thu Feb 9 11:18:45 2012
@@ -0,0 +1,88 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from decimal import Decimal
+from uuid import UUID
+import cql
+from cql.marshal import unmarshallers, marshallers, unmarshal_noop,
marshal_noop
+
+marshalled_value_pairs = (
+ ('lorem ipsum dolor sit amet', 'AsciiType', 'lorem ipsum dolor sit
amet'),
+ ('', 'AsciiType', ''),
+ ('\x01', 'BooleanType', True),
+ ('\x00', 'BooleanType', False),
+ ('', 'BooleanType', None),
+ ('\xff\xfe\xfd\xfc\xfb', 'BytesType', '\xff\xfe\xfd\xfc\xfb'),
+ ('', 'BytesType', ''),
+ ('\x7f\xff\xff\xff\xff\xff\xff\xff', 'CounterColumnType',
9223372036854775807),
+ ('\x80\x00\x00\x00\x00\x00\x00\x00', 'CounterColumnType',
-9223372036854775808),
+ ('', 'CounterColumnType', None),
+ ('\x00\x00\x013\x7fb\xeey', 'DateType', 1320692149.881),
+ ('', 'DateType', None),
+ ('\x00\x00\x00\r\nJ\x04"^\x91\x04\x8a\xb1\x18\xfe', 'DecimalType',
Decimal('1243878957943.1234124191998')),
+ ('\x00\x00\x00\x06\xe5\xde]\x98Y', 'DecimalType',
Decimal('-112233.441191')),
+ ('\x00\x00\x00\x14\x00\xfa\xce', 'DecimalType',
Decimal('0.00000000000000064206')),
+ ('\x00\x00\x00\x14\xff\x052', 'DecimalType',
Decimal('-0.00000000000000064206')),
+ ('\xff\xff\xff\x9c\x00\xfa\xce', 'DecimalType', Decimal('64206e100')),
+ ('', 'DecimalType', None),
+ ('@\xd2\xfa\x08\x00\x00\x00\x00', 'DoubleType', 19432.125),
+ ('\xc0\xd2\xfa\x08\x00\x00\x00\x00', 'DoubleType', -19432.125),
+ ('\x7f\xef\x00\x00\x00\x00\x00\x00', 'DoubleType',
1.7415152243978685e+308),
+ ('', 'DoubleType', None),
+ ('F\x97\xd0@', 'FloatType', 19432.125),
+ ('\xc6\x97\xd0@', 'FloatType', -19432.125),
+ ('\xc6\x97\xd0@', 'FloatType', -19432.125),
+ ('\x7f\x7f\x00\x00', 'FloatType',
338953138925153547590470800371487866880.0),
+ ('', 'FloatType', None),
+ ('\x7f\x50\x00\x00', 'Int32Type', 2135949312),
+ ('\xff\xfd\xcb\x91', 'Int32Type', -144495),
+ ('', 'Int32Type', None),
+ ('f\x1e\xfd\xf2\xe3\xb1\x9f|\x04_\x15', 'IntegerType',
123456789123456789123456789),
+ ('', 'IntegerType', None),
+ ('\x7f\xff\xff\xff\xff\xff\xff\xff', 'LongType', 9223372036854775807),
+ ('\x80\x00\x00\x00\x00\x00\x00\x00', 'LongType', -9223372036854775808),
+ ('', 'LongType', None),
+ ('\xe3\x81\xbe\xe3\x81\x97\xe3\x81\xa6', 'UTF8Type',
u'\u307e\u3057\u3066'),
+ ('\xe3\x81\xbe\xe3\x81\x97\xe3\x81\xa6' * 1000, 'UTF8Type',
u'\u307e\u3057\u3066' * 1000),
+ ('', 'UTF8Type', u''),
+ ('\xff' * 16, 'UUIDType',
UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')),
+ ('I\x15~\xfc\xef<\x9d\xe3\x16\x98\xaf\x80\x1f\xb4\x0b*', 'UUIDType',
UUID('49157efc-ef3c-9de3-1698-af801fb40b2a')),
+ ('', 'UUIDType', None),
+)
+
+class TestUnmarshal(unittest.TestCase):
+ def test_unmarshalling(self):
+ for serializedval, valtype, nativeval in marshalled_value_pairs:
+ unmarshaller = unmarshallers.get(valtype, unmarshal_noop)
+ whatwegot = unmarshaller(serializedval)
+ self.assertEqual(whatwegot, nativeval,
+ msg='Unmarshaller for %s (%s) failed:
unmarshal(%r) got %r instead of %r'
+ % (valtype, unmarshaller, serializedval,
whatwegot, nativeval))
+ self.assertEqual(type(whatwegot), type(nativeval),
+ msg='Unmarshaller for %s (%s) gave wrong type
(%s instead of %s)'
+ % (valtype, unmarshaller,
type(whatwegot), type(nativeval)))
+
+ def test_marshalling(self):
+ for serializedval, valtype, nativeval in marshalled_value_pairs:
+ marshaller = marshallers.get(valtype, marshal_noop)
+ whatwegot = marshaller(nativeval)
+ self.assertEqual(whatwegot, serializedval,
+ msg='Marshaller for %s (%s) failed:
marshal(%r) got %r instead of %r'
+ % (valtype, marshaller, nativeval,
whatwegot, serializedval))
+ self.assertEqual(type(whatwegot), type(serializedval),
+ msg='Marshaller for %s (%s) gave wrong type
(%s instead of %s)'
+ % (valtype, marshaller, type(whatwegot),
type(serializedval)))
=======================================
--- /test/test_unmarshalling.py Mon Nov 7 12:32:57 2011
+++ /dev/null
@@ -1,77 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import unittest
-from decimal import Decimal
-from uuid import UUID
-import cql
-from cql.marshal import unmarshallers, unmarshal_noop
-
-demarshal_me = (
- ('lorem ipsum dolor sit amet', 'AsciiType', 'lorem ipsum dolor sit
amet'),
- ('', 'AsciiType', ''),
- ('\x01', 'BooleanType', True),
- ('\x00', 'BooleanType', False),
- ('', 'BooleanType', None),
- ('\xff\xfe\xfd\xfc\xfb', 'BytesType', '\xff\xfe\xfd\xfc\xfb'),
- ('', 'BytesType', ''),
- ('\x7f\xff\xff\xff\xff\xff\xff\xff', 'CounterColumnType',
9223372036854775807),
- ('\x80\x00\x00\x00\x00\x00\x00\x00', 'CounterColumnType',
-9223372036854775808),
- ('', 'CounterColumnType', None),
- ('\x00\x00\x013\x7fb\xeey', 'DateType', 1320692149.881),
- ('', 'DateType', None),
- ('\x00\x00\x00\r\nJ\x04"^\x91\x04\x8a\xb1\x18\xfe', 'DecimalType',
Decimal('1243878957943.1234124191998')),
- ('\x00\x00\x00\x06\xe5\xde]\x98Y', 'DecimalType',
Decimal('-112233.441191')),
- ('\x00\x00\x00\x14\x00\xfa\xce', 'DecimalType',
Decimal('0.00000000000000064206')),
- ('\x00\x00\x00\x14\xff\x052', 'DecimalType',
Decimal('-0.00000000000000064206')),
- ('\xff\xff\xff\x9c\x00\xfa\xce', 'DecimalType', Decimal('64206e100')),
- ('', 'DecimalType', None),
- ('@\xd2\xfa\x08\x00\x00\x00\x00', 'DoubleType', 19432.125),
- ('\xc0\xd2\xfa\x08\x00\x00\x00\x00', 'DoubleType', -19432.125),
- ('\x7f\xef\x00\x00\x00\x00\x00\x00', 'DoubleType',
1.7415152243978685e+308),
- ('', 'DoubleType', None),
- ('F\x97\xd0@', 'FloatType', 19432.125),
- ('\xc6\x97\xd0@', 'FloatType', -19432.125),
- ('\xc6\x97\xd0@', 'FloatType', -19432.125),
- ('\x7f\x7f\x00\x00', 'FloatType',
338953138925153547590470800371487866880.0),
- ('', 'FloatType', None),
- ('\x7f\x50\x00\x00', 'Int32Type', 2135949312),
- ('\xff\xfd\xcb\x91', 'Int32Type', -144495),
- ('', 'Int32Type', None),
- ('f\x1e\xfd\xf2\xe3\xb1\x9f|\x04_\x15', 'IntegerType',
123456789123456789123456789),
- ('', 'IntegerType', None),
- ('\x7f\xff\xff\xff\xff\xff\xff\xff', 'LongType', 9223372036854775807),
- ('\x80\x00\x00\x00\x00\x00\x00\x00', 'LongType', -9223372036854775808),
- ('', 'LongType', None),
- ('\xe3\x81\xbe\xe3\x81\x97\xe3\x81\xa6', 'UTF8Type',
u'\u307e\u3057\u3066'),
- ('\xe3\x81\xbe\xe3\x81\x97\xe3\x81\xa6' * 1000, 'UTF8Type',
u'\u307e\u3057\u3066' * 1000),
- ('', 'UTF8Type', u''),
- ('\xff' * 16, 'UUIDType',
UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')),
- ('I\x15~\xfc\xef<\x9d\xe3\x16\x98\xaf\x80\x1f\xb4\x0b*', 'UUIDType',
UUID('49157efc-ef3c-9de3-1698-af801fb40b2a')),
- ('', 'UUIDType', None),
-)
-
-class TestUnmarshal(unittest.TestCase):
- def test_unmarshalling(self):
- for serializedval, valtype, marshaledval in demarshal_me:
- unmarshaller = unmarshallers.get(valtype, unmarshal_noop)
- whatwegot = unmarshaller(serializedval)
- self.assertEqual(whatwegot, marshaledval,
- msg='Unmarshaller for %s (%s) failed:
unmarshal(%r) got %r instead of %r'
- % (valtype, unmarshaller, serializedval,
whatwegot, marshaledval))
- self.assertEqual(type(whatwegot), type(marshaledval),
- msg='Unmarshaller for %s (%s) gave wrong type
(%s instead of %s)'
- % (valtype, unmarshaller,
type(whatwegot), type(marshaledval)))
==============================================================================
Revision: b71be13e3a41
Author: paul cannon <pa...@datastax.com>
Date: Thu Feb 9 13:52:06 2012
Log: support for prepared statements
http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/source/detail?r=b71be13e3a41
Modified:
/cql/connection.py
/cql/cursor.py
/cql/marshal.py
=======================================
--- /cql/connection.py Wed Jan 25 04:36:19 2012
+++ /cql/connection.py Thu Feb 9 13:52:06 2012
@@ -49,6 +49,8 @@
credentials = {"username": user, "password": password}
self.client.login(AuthenticationRequest(credentials=credentials))
+ self.remote_thrift_version = tuple(map(int,
self.client.describe_version().split('.')))
+
if cql_version:
self.client.set_cql_version(cql_version)
=======================================
--- /cql/cursor.py Thu Feb 9 11:11:08 2012
+++ /cql/cursor.py Thu Feb 9 13:52:06 2012
@@ -19,7 +19,7 @@
import zlib
import cql
-from cql.marshal import prepare
+from cql.marshal import prepare_inline, prepare_query, PreparedQuery
from cql.decoders import SchemaDecoder
from cql.cassandra.ttypes import (
Compression,
@@ -33,6 +33,8 @@
_COUNT_DESCRIPTION = (None, None, None, None, None, None, None)
_VOID_DESCRIPTION = (None)
+MIN_THRIFT_FOR_PREPARED_QUERIES = (19, 27, 0)
+
class Cursor:
_keyspace_re = re.compile("USE (\w+);?",
re.IGNORECASE | re.MULTILINE)
@@ -40,6 +42,7 @@
re.IGNORECASE | re.MULTILINE | re.DOTALL)
_ddl_re = re.compile("\s*(CREATE|ALTER|DROP)\s+",
re.IGNORECASE | re.MULTILINE)
+ supports_prepared_queries = False
def __init__(self, parent_connection):
self.open_socket = True
@@ -55,6 +58,10 @@
self.compression = 'GZIP'
self.decoder = None
+ if hasattr(parent_connection.client, 'execute_prepared_cql_query')
\
+ and parent_connection.remote_thrift_version >=
MIN_THRIFT_FOR_PREPARED_QUERIES:
+ self.supports_prepared_queries = True
+
###
# Cursor API
###
@@ -62,29 +69,60 @@
def close(self):
self.open_socket = False
- def prepare(self, query, params):
- return prepare(query, params)
-
- def execute(self, cql_query, params={}, decoder=None):
+ def compress_query_text(self, querytext):
+ if self.compression == 'GZIP':
+ compressed_q = zlib.compress(querytext)
+ else:
+ compressed_q = querytext
+ req_compression = getattr(Compression, self.compression)
+ return compressed_q, req_compression
+
+ def prepare_inline(self, query, params):
+ try:
+ prepared_q_text = prepare_inline(query, params)
+ except KeyError, e:
+ raise cql.ProgrammingError("Unmatched named substitution: " +
+ "%s not given for %r" % (e, query))
+ return self.compress_query_text(prepared_q_text)
+
+ def prepare_query(self, query, paramtypes=None):
+ prepared_q_text, paramnames = prepare_query(query)
+ compressed_q, compression =
self.compress_query_text(prepared_q_text)
+ presult = self._connection.client.prepare_cql_query(compressed_q,
compression)
+ assert presult.count == len(paramnames)
+ if presult.variable_types is None and presult.count > 0:
+ raise cql.ProgrammingError("Cassandra did not provide types
for bound"
+ " parameters. Prepared statements
are only"
+ " supported with cql3.")
+ return PreparedQuery(query, presult.itemId,
presult.variable_types, paramnames)
+
+ def pre_execution_setup(self):
self.__checksock()
self.rs_idx = 0
self.rowcount = 0
self.description = None
- try:
- prepared_q = self.prepare(cql_query, params)
- except KeyError, e:
- raise cql.ProgrammingError("Unmatched named substitution: " +
- "%s not given for %s" % (e,
cql_query))
-
- if self.compression == 'GZIP':
- compressed_q = zlib.compress(prepared_q)
- else:
- compressed_q = prepared_q
- request_compression = getattr(Compression, self.compression)
-
+
+ def execute(self, cql_query, params={}, decoder=None):
+ self.pre_execution_setup()
+
+ prepared_q, compress = self.prepare_inline(cql_query, params)
+ doquery = self._connection.client.execute_cql_query
+ response = self.handle_cql_execution_errors(doquery, prepared_q,
compress)
+
+ return self.process_execution_results(response, decoder=decoder)
+
+ def execute_prepared(self, prepared_query, params={}, decoder=None):
+ self.pre_execution_setup()
+
+ doquery = self._connection.client.execute_prepared_cql_query
+ paramvals = prepared_query.encode_params()
+ response = self.handle_cql_execution_errors(doquery,
prepared_query.itemid, paramvals)
+
+ return self.process_execution_results(response, decoder=decoder)
+
+ def handle_cql_execution_errors(self, executor, *args, **kwargs):
try:
- client = self._connection.client
- response = client.execute_cql_query(compressed_q,
request_compression)
+ return executor(*args, **kwargs)
except InvalidRequestException, ire:
raise cql.ProgrammingError("Bad Request: %s" % ire.why)
except SchemaDisagreementException, sde:
@@ -97,6 +135,7 @@
except TApplicationException, tapp:
raise cql.InternalError("Internal application error")
+ def process_execution_results(self, response, decoder=None):
if response.type == CqlResultType.ROWS:
self.decoder = (decoder or SchemaDecoder)(response.schema)
self.result = response.rows
=======================================
--- /cql/marshal.py Thu Feb 9 11:09:39 2012
+++ /cql/marshal.py Thu Feb 9 13:52:06 2012
@@ -21,7 +21,8 @@
import cql
-__all__ = ['prepare', 'cql_quote', 'unmarshal_noop', 'unmarshallers']
+__all__ =
['prepare_inline', 'prepare_query', 'cql_quote', 'unmarshal_noop', 'unmarshallers',
+ 'cql_marshal', 'PreparedQuery']
if hasattr(struct, 'Struct'): # new in Python 2.5
_have_struct = True
@@ -73,6 +74,19 @@
TIME_UUID_TYPE = "org.apache.cassandra.db.marshal.TimeUUIDType"
COUNTER_COLUMN_TYPE = "org.apache.cassandra.db.marshal.CounterColumnType"
+class PreparedQuery(object):
+ def __init__(self, querytext, itemid, vartypes, paramnames):
+ self.querytext = querytext
+ self.itemid = itemid
+ self.vartypes = vartypes
+ self.paramnames = paramnames
+ if len(self.vartypes) != len(self.paramnames):
+ raise cql.ProgrammingError("Length of variable types list is
not the same"
+ " length as the list of parameter
names")
+
+ def encode_params(self, params):
+ return [cql_marshal(params[n], t) for (n, t) in
zip(self.paramnames, self.vartypes)]
+
def blank_comments(query):
def teh_blanker(match):
m = match.group(0)
@@ -81,7 +95,7 @@
return ' ' * len(m)
return _comment_re.sub(teh_blanker, query)
-def prepare(query, params):
+def prepare_inline(query, params):
"""
For every match of the form ":param_name", call cql_quote
on kwargs['param_name'] and replace that section of the query
@@ -94,6 +108,18 @@
query = blank_comments(query)
return _param_re.sub(lambda m: m.group(1) +
cql_quote(params[m.group(2)]), query)
+def prepare_query(querytext):
+ querytext = blank_comments(querytext)
+ paramnames = []
+ def found_param(match):
+ pre_param_text = match.group(1)
+ paramname = match.group(2)
+ paramnames.append(paramname)
+ return pre_param_text + '?'
+ transformed_query = _param_re.sub(found_param, querytext)
+ return transformed_query, paramnames
+
+
def cql_quote(term):
if isinstance(term, unicode):
return "'%s'" % __escape_quotes(term.encode('utf8'))