|
33 | 33 | from cassandra.protocol import _UNSET_VALUE |
34 | 34 | from cassandra.util import OrderedDict, _sanitize_identifiers |
35 | 35 |
|
| 36 | +try: |
| 37 | + from cassandra.serializers import make_serializers as _cython_make_serializers |
| 38 | + _HAVE_CYTHON_SERIALIZERS = True |
| 39 | +except ImportError: |
| 40 | + _HAVE_CYTHON_SERIALIZERS = False |
| 41 | + |
36 | 42 | import logging |
37 | 43 | log = logging.getLogger(__name__) |
38 | 44 |
|
@@ -474,6 +480,24 @@ def __init__(self, column_metadata, query_id, routing_key_indexes, query, |
474 | 480 | self.is_idempotent = False |
475 | 481 | self._is_lwt = is_lwt |
476 | 482 |
|
| 483 | + @property |
| 484 | + def _serializers(self): |
| 485 | + """Lazily create and cache Cython serializers for column types. |
| 486 | +
|
| 487 | + Returns a list of Serializer objects if Cython serializers are available |
| 488 | + and there is no column encryption policy, otherwise returns None. |
| 489 | + """ |
| 490 | + try: |
| 491 | + return self.__serializers |
| 492 | + except AttributeError: |
| 493 | + pass |
| 494 | + if _HAVE_CYTHON_SERIALIZERS and not self.column_encryption_policy and self.column_metadata: |
| 495 | + self.__serializers = _cython_make_serializers( |
| 496 | + [col.type for col in self.column_metadata]) |
| 497 | + else: |
| 498 | + self.__serializers = None |
| 499 | + return self.__serializers |
| 500 | + |
477 | 501 | @classmethod |
478 | 502 | def from_message(cls, query_id, column_metadata, pk_indexes, cluster_metadata, |
479 | 503 | query, prepared_keyspace, protocol_version, result_metadata, |
@@ -636,28 +660,71 @@ def bind(self, values): |
636 | 660 |
|
637 | 661 | self.raw_values = values |
638 | 662 | self.values = [] |
639 | | - for value, col_spec in zip(values, col_meta): |
640 | | - if value is None: |
641 | | - self.values.append(None) |
642 | | - elif value is UNSET_VALUE: |
643 | | - if proto_version >= 4: |
644 | | - self._append_unset_value() |
| 663 | + if ce_policy: |
| 664 | + # Column encryption path: check each column for CE policy |
| 665 | + for value, col_spec in zip(values, col_meta): |
| 666 | + if value is None: |
| 667 | + self.values.append(None) |
| 668 | + elif value is UNSET_VALUE: |
| 669 | + if proto_version >= 4: |
| 670 | + self._append_unset_value() |
| 671 | + else: |
| 672 | + raise ValueError("Attempt to bind UNSET_VALUE while using unsuitable protocol version (%d < 4)" % proto_version) |
645 | 673 | else: |
646 | | - raise ValueError("Attempt to bind UNSET_VALUE while using unsuitable protocol version (%d < 4)" % proto_version) |
| 674 | + try: |
| 675 | + col_desc = ColDesc(col_spec.keyspace_name, col_spec.table_name, col_spec.name) |
| 676 | + uses_ce = ce_policy.contains_column(col_desc) |
| 677 | + if uses_ce: |
| 678 | + col_type = ce_policy.column_type(col_desc) |
| 679 | + col_bytes = col_type.serialize(value, proto_version) |
| 680 | + col_bytes = ce_policy.encrypt(col_desc, col_bytes) |
| 681 | + else: |
| 682 | + col_bytes = col_spec.type.serialize(value, proto_version) |
| 683 | + self.values.append(col_bytes) |
| 684 | + except (TypeError, struct.error) as exc: |
| 685 | + actual_type = type(value) |
| 686 | + message = ('Received an argument of invalid type for column "%s". ' |
| 687 | + 'Expected: %s, Got: %s; (%s)' % (col_spec.name, col_spec.type, actual_type, exc)) |
| 688 | + raise TypeError(message) |
| 689 | + else: |
| 690 | + # Fast path: no column encryption, use Cython serializers if available |
| 691 | + serializers = self.prepared_statement._serializers |
| 692 | + if serializers is not None: |
| 693 | + for ser, value, col_spec in zip(serializers, values, col_meta): |
| 694 | + if value is None: |
| 695 | + self.values.append(None) |
| 696 | + elif value is UNSET_VALUE: |
| 697 | + if proto_version >= 4: |
| 698 | + self._append_unset_value() |
| 699 | + else: |
| 700 | + raise ValueError("Attempt to bind UNSET_VALUE while using unsuitable protocol version (%d < 4)" % proto_version) |
| 701 | + else: |
| 702 | + try: |
| 703 | + col_bytes = ser.serialize(value, proto_version) |
| 704 | + self.values.append(col_bytes) |
| 705 | + except (TypeError, struct.error) as exc: |
| 706 | + actual_type = type(value) |
| 707 | + message = ('Received an argument of invalid type for column "%s". ' |
| 708 | + 'Expected: %s, Got: %s; (%s)' % (col_spec.name, col_spec.type, actual_type, exc)) |
| 709 | + raise TypeError(message) |
647 | 710 | else: |
648 | | - try: |
649 | | - col_desc = ColDesc(col_spec.keyspace_name, col_spec.table_name, col_spec.name) |
650 | | - uses_ce = ce_policy and ce_policy.contains_column(col_desc) |
651 | | - col_type = ce_policy.column_type(col_desc) if uses_ce else col_spec.type |
652 | | - col_bytes = col_type.serialize(value, proto_version) |
653 | | - if uses_ce: |
654 | | - col_bytes = ce_policy.encrypt(col_desc, col_bytes) |
655 | | - self.values.append(col_bytes) |
656 | | - except (TypeError, struct.error) as exc: |
657 | | - actual_type = type(value) |
658 | | - message = ('Received an argument of invalid type for column "%s". ' |
659 | | - 'Expected: %s, Got: %s; (%s)' % (col_spec.name, col_spec.type, actual_type, exc)) |
660 | | - raise TypeError(message) |
| 711 | + for value, col_spec in zip(values, col_meta): |
| 712 | + if value is None: |
| 713 | + self.values.append(None) |
| 714 | + elif value is UNSET_VALUE: |
| 715 | + if proto_version >= 4: |
| 716 | + self._append_unset_value() |
| 717 | + else: |
| 718 | + raise ValueError("Attempt to bind UNSET_VALUE while using unsuitable protocol version (%d < 4)" % proto_version) |
| 719 | + else: |
| 720 | + try: |
| 721 | + col_bytes = col_spec.type.serialize(value, proto_version) |
| 722 | + self.values.append(col_bytes) |
| 723 | + except (TypeError, struct.error) as exc: |
| 724 | + actual_type = type(value) |
| 725 | + message = ('Received an argument of invalid type for column "%s". ' |
| 726 | + 'Expected: %s, Got: %s; (%s)' % (col_spec.name, col_spec.type, actual_type, exc)) |
| 727 | + raise TypeError(message) |
661 | 728 |
|
662 | 729 | if proto_version >= 4: |
663 | 730 | diff = col_meta_len - len(self.values) |
|
0 commit comments