@@ -135,6 +135,10 @@ def __init__(self, connection: "Connection", timeout: int = 0) -> None:
135135
136136 self ._cached_column_map = None
137137 self ._cached_converter_map = None
138+ self ._uuid_str_indices = None # Pre-computed UUID column indices for str conversion
139+ # Cache the effective native_uuid setting for this cursor's connection.
140+ # Resolution order: connection._native_uuid (if not None) → module-level setting.
141+ self ._conn_native_uuid = getattr (self .connection , "_native_uuid" , None )
138142 self ._next_row_index = 0 # internal: index of the next row the driver will return (0-based)
139143 self ._has_result_set = False # Track if we have an active result set
140144 self ._skip_increment_for_next_fetch = (
@@ -1009,6 +1013,32 @@ def _build_converter_map(self):
10091013
10101014 return converter_map
10111015
1016+ def _compute_uuid_str_indices (self ):
1017+ """
1018+ Compute the tuple of column indices whose uuid.UUID values should be
1019+ stringified (as uppercase), based on the effective native_uuid setting.
1020+
1021+ Resolution order: connection-level (if set) → module-level (fallback).
1022+
1023+ Returns:
1024+ tuple of int or None: Column indices to stringify, or None when
1025+ native_uuid is True — meaning zero per-row overhead.
1026+ """
1027+ if not self .description :
1028+ return None
1029+
1030+ effective_native_uuid = (
1031+ self ._conn_native_uuid
1032+ if self ._conn_native_uuid is not None
1033+ else get_settings ().native_uuid
1034+ )
1035+ if not effective_native_uuid :
1036+ indices = tuple (
1037+ i for i , desc in enumerate (self .description ) if desc and desc [1 ] is uuid .UUID
1038+ )
1039+ return indices if indices else None
1040+ return None
1041+
10121042 def _get_column_and_converter_maps (self ):
10131043 """
10141044 Get column map and converter map for Row construction (thread-safe).
@@ -1429,20 +1459,13 @@ def execute( # pylint: disable=too-many-locals,too-many-branches,too-many-state
14291459 col_desc [0 ]: i for i , col_desc in enumerate (self .description )
14301460 }
14311461 self ._cached_converter_map = self ._build_converter_map ()
1462+ self ._uuid_str_indices = self ._compute_uuid_str_indices ()
14321463 else :
14331464 self .rowcount = ddbc_bindings .DDBCSQLRowCount (self .hstmt )
14341465 self ._clear_rownumber ()
14351466 self ._cached_column_map = None
14361467 self ._cached_converter_map = None
1437-
1438- # After successful execution, initialize description if there are results
1439- column_metadata = []
1440- try :
1441- ddbc_bindings .DDBCSQLDescribeCol (self .hstmt , column_metadata )
1442- self ._initialize_description (column_metadata )
1443- except Exception as e :
1444- # If describe fails, it's likely there are no results (e.g., for INSERT)
1445- self .description = None
1468+ self ._uuid_str_indices = None
14461469
14471470 self ._reset_inputsizes () # Reset input sizes after execution
14481471 # Return self for method chaining
@@ -2278,9 +2301,11 @@ def executemany( # pylint: disable=too-many-locals,too-many-branches,too-many-s
22782301 if self .description :
22792302 self .rowcount = - 1
22802303 self ._reset_rownumber ()
2304+ self ._uuid_str_indices = self ._compute_uuid_str_indices ()
22812305 else :
22822306 self .rowcount = ddbc_bindings .DDBCSQLRowCount (self .hstmt )
22832307 self ._clear_rownumber ()
2308+ self ._uuid_str_indices = None
22842309 finally :
22852310 # Reset input sizes after execution
22862311 self ._reset_inputsizes ()
@@ -2328,7 +2353,13 @@ def fetchone(self) -> Union[None, Row]:
23282353
23292354 # Get column and converter maps
23302355 column_map , converter_map = self ._get_column_and_converter_maps ()
2331- return Row (row_data , column_map , cursor = self , converter_map = converter_map )
2356+ return Row (
2357+ row_data ,
2358+ column_map ,
2359+ cursor = self ,
2360+ converter_map = converter_map ,
2361+ uuid_str_indices = self ._uuid_str_indices ,
2362+ )
23322363 except Exception as e :
23332364 # On error, don't increment rownumber - rethrow the error
23342365 raise e
@@ -2386,8 +2417,15 @@ def fetchmany(self, size: Optional[int] = None) -> List[Row]:
23862417 column_map , converter_map = self ._get_column_and_converter_maps ()
23872418
23882419 # Convert raw data to Row objects
2420+ uuid_idx = self ._uuid_str_indices
23892421 return [
2390- Row (row_data , column_map , cursor = self , converter_map = converter_map )
2422+ Row (
2423+ row_data ,
2424+ column_map ,
2425+ cursor = self ,
2426+ converter_map = converter_map ,
2427+ uuid_str_indices = uuid_idx ,
2428+ )
23912429 for row_data in rows_data
23922430 ]
23932431 except Exception as e :
@@ -2439,8 +2477,15 @@ def fetchall(self) -> List[Row]:
24392477 column_map , converter_map = self ._get_column_and_converter_maps ()
24402478
24412479 # Convert raw data to Row objects
2480+ uuid_idx = self ._uuid_str_indices
24422481 return [
2443- Row (row_data , column_map , cursor = self , converter_map = converter_map )
2482+ Row (
2483+ row_data ,
2484+ column_map ,
2485+ cursor = self ,
2486+ converter_map = converter_map ,
2487+ uuid_str_indices = uuid_idx ,
2488+ )
24442489 for row_data in rows_data
24452490 ]
24462491 except Exception as e :
@@ -2466,6 +2511,7 @@ def nextset(self) -> Union[bool, None]:
24662511 # Clear cached column and converter maps for the new result set
24672512 self ._cached_column_map = None
24682513 self ._cached_converter_map = None
2514+ self ._uuid_str_indices = None
24692515
24702516 # Skip to the next result set
24712517 ret = ddbc_bindings .DDBCSQLMoreResults (self .hstmt )
@@ -2491,6 +2537,7 @@ def nextset(self) -> Union[bool, None]:
24912537 col_desc [0 ]: i for i , col_desc in enumerate (self .description )
24922538 }
24932539 self ._cached_converter_map = self ._build_converter_map ()
2540+ self ._uuid_str_indices = self ._compute_uuid_str_indices ()
24942541 except Exception as e : # pylint: disable=broad-exception-caught
24952542 # If describe fails, there might be no results in this result set
24962543 self .description = None
0 commit comments