@@ -891,9 +891,9 @@ def read_type(cls, f, user_type_map):
891891 num_fields = read_short (f )
892892 names , types = zip (* ((read_string (f ), cls .read_type (f , user_type_map ))
893893 for _ in range (num_fields )))
894- specialized_type = typeclass . make_udt_class (ks , udt_name , names , types )
895- specialized_type . mapped_class = user_type_map . get ( ks , {}). get ( udt_name )
896- typeclass = specialized_type
894+ mapped_class = user_type_map . get (ks , {}). get ( udt_name )
895+ typeclass = typeclass . make_udt_class (
896+ ks , udt_name , names , types , mapped_class = mapped_class )
897897 elif typeclass == CUSTOM_TYPE :
898898 classname = read_string (f )
899899 typeclass = lookup_casstype (classname )
@@ -1100,6 +1100,110 @@ def send_body(self, f, protocol_version, protocol_features=None):
11001100 "Continuous paging backpressure is not supported." )
11011101
11021102
1103+ _PREPARED_METADATA_RESULT_ATTRIBUTES = (
1104+ 'opcode' ,
1105+ 'name' ,
1106+ 'kind' ,
1107+ 'results' ,
1108+ 'paging_state' ,
1109+ '_FLAGS_GLOBAL_TABLES_SPEC' ,
1110+ '_HAS_MORE_PAGES_FLAG' ,
1111+ '_NO_METADATA_FLAG' ,
1112+ '_CONTINUOUS_PAGING_FLAG' ,
1113+ '_CONTINUOUS_PAGING_LAST_FLAG' ,
1114+ '_METADATA_ID_FLAG' ,
1115+ 'column_names' ,
1116+ 'column_types' ,
1117+ 'parsed_rows' ,
1118+ 'continuous_paging_seq' ,
1119+ 'continuous_paging_last' ,
1120+ 'new_keyspace' ,
1121+ 'column_metadata' ,
1122+ 'query_id' ,
1123+ 'bind_metadata' ,
1124+ 'pk_indexes' ,
1125+ 'schema_change_event' ,
1126+ 'is_lwt' ,
1127+ 'tracing' ,
1128+ 'custom_payload' ,
1129+ 'warnings' ,
1130+ '__init__' ,
1131+ 'recv' ,
1132+ 'recv_body' ,
1133+ 'recv_results_rows' ,
1134+ 'recv_results_prepared' ,
1135+ 'recv_results_metadata' ,
1136+ 'recv_prepared_metadata' ,
1137+ 'recv_results_schema_change' ,
1138+ 'read_type' ,
1139+ 'recv_row' ,
1140+ )
1141+
1142+
1143+ def _class_attribute (cls , name ):
1144+ for base in cls .__mro__ :
1145+ if name in vars (base ):
1146+ return vars (base )[name ]
1147+ return None
1148+
1149+
1150+ _PREPARED_METADATA_CONFIG_UNSET = object ()
1151+
1152+
1153+ def _prepared_metadata_cache_config (
1154+ handler , result_message_type ,
1155+ message_types = _PREPARED_METADATA_CONFIG_UNSET ,
1156+ type_codes = _PREPARED_METADATA_CONFIG_UNSET ,
1157+ code_to_type = _PREPARED_METADATA_CONFIG_UNSET ,
1158+ decode_message = _PREPARED_METADATA_CONFIG_UNSET ,
1159+ result_attributes = _PREPARED_METADATA_CONFIG_UNSET ,
1160+ column_encryption_policy = _PREPARED_METADATA_CONFIG_UNSET ):
1161+ """
1162+ Capture the canonical driver-owned result-decoder configuration.
1163+
1164+ Cluster-side snapshots compare against this exact configuration. Installing
1165+ an application ResultMessage or mutating a decoding attribute therefore
1166+ makes the handler ineligible for skip-metadata instead of attempting to
1167+ freeze arbitrary application class state.
1168+ """
1169+ message_types = (
1170+ handler .message_types_by_opcode
1171+ if message_types is _PREPARED_METADATA_CONFIG_UNSET
1172+ else message_types )
1173+ type_codes = (
1174+ result_message_type .type_codes
1175+ if type_codes is _PREPARED_METADATA_CONFIG_UNSET
1176+ else type_codes )
1177+ code_to_type = (
1178+ getattr (result_message_type , 'code_to_type' , {})
1179+ if code_to_type is _PREPARED_METADATA_CONFIG_UNSET
1180+ else code_to_type )
1181+ decode_message = (
1182+ _class_attribute (handler , 'decode_message' )
1183+ if decode_message is _PREPARED_METADATA_CONFIG_UNSET
1184+ else decode_message )
1185+ result_attributes = (
1186+ tuple (
1187+ (name , _class_attribute (result_message_type , name ))
1188+ for name in _PREPARED_METADATA_RESULT_ATTRIBUTES
1189+ )
1190+ if result_attributes is _PREPARED_METADATA_CONFIG_UNSET
1191+ else result_attributes )
1192+ column_encryption_policy = (
1193+ handler .column_encryption_policy
1194+ if column_encryption_policy is _PREPARED_METADATA_CONFIG_UNSET
1195+ else column_encryption_policy )
1196+ return (
1197+ tuple (message_types .items ()),
1198+ result_message_type ,
1199+ tuple (type_codes .items ()),
1200+ tuple (code_to_type .items ()),
1201+ decode_message ,
1202+ result_attributes ,
1203+ column_encryption_policy ,
1204+ )
1205+
1206+
11031207class _ProtocolHandler (object ):
11041208 """
11051209 _ProtocolHander handles encoding and decoding messages.
@@ -1121,6 +1225,11 @@ class _ProtocolHandler(object):
11211225 column_encryption_policy = None
11221226 """Instance of :class:`cassandra.policies.ColumnEncryptionPolicy` in use by this handler"""
11231227
1228+ # Marks handlers whose result-decoder configuration the driver may copy
1229+ # into an immutable per-request snapshot. Subclasses do not inherit
1230+ # eligibility because their opcode and type maps are application-owned.
1231+ _prepared_metadata_cache_token = object ()
1232+
11241233 @classmethod
11251234 def encode_message (cls , msg , stream_id , protocol_version , compressor , allow_beta_protocol_version ,
11261235 protocol_features ):
@@ -1245,6 +1354,10 @@ def decode_message(cls, protocol_version, protocol_features, user_type_map, stre
12451354 return msg
12461355
12471356
1357+ _ProtocolHandler ._prepared_metadata_cache_config = \
1358+ _prepared_metadata_cache_config (_ProtocolHandler , ResultMessage )
1359+
1360+
12481361def cython_protocol_handler (colparser ):
12491362 """
12501363 Given a column parser to deserialize ResultMessages, return a suitable
@@ -1284,7 +1397,11 @@ class CythonProtocolHandler(_ProtocolHandler):
12841397 message_types_by_opcode = my_opcodes
12851398
12861399 col_parser = colparser
1400+ _prepared_metadata_cache_token = object ()
12871401
1402+ CythonProtocolHandler ._prepared_metadata_cache_config = \
1403+ _prepared_metadata_cache_config (
1404+ CythonProtocolHandler , FastResultMessage )
12881405 return CythonProtocolHandler
12891406
12901407
0 commit comments