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'))