@@ -81,7 +81,7 @@ def __init__(self, connection, timeout: int = 0) -> None:
8181 self .description = None
8282 self .rowcount = - 1
8383 self .arraysize = (
84- 1 # Default number of rows to fetch at a time is 1, user can change it
84+ 1000 # Increased default for better performance - user can still change it
8585 )
8686 self .buffer_length = 1024 # Default buffer length for string data
8787 self .closed = False
@@ -98,6 +98,10 @@ def __init__(self, connection, timeout: int = 0) -> None:
9898
9999 # rownumber attribute
100100 self ._rownumber = - 1 # DB-API extension: last returned row index, -1 before first
101+
102+ # Performance optimizations
103+ self ._fast_mode = False # When enabled, returns tuples instead of Row objects
104+ self ._cached_column_map = None # Cache column mapping for performance
101105 self ._next_row_index = 0 # internal: index of the next row the driver will return (0-based)
102106 self ._has_result_set = False # Track if we have an active result set
103107 self ._skip_increment_for_next_fetch = False # Track if we need to skip incrementing the row index
@@ -1010,9 +1014,12 @@ def execute(
10101014 if self .description : # If we have column descriptions, it's likely a SELECT
10111015 self .rowcount = - 1
10121016 self ._reset_rownumber ()
1017+ # Performance optimization: pre-build column map after execution
1018+ self ._cached_column_map = {col_desc [0 ]: i for i , col_desc in enumerate (self .description )}
10131019 else :
10141020 self .rowcount = ddbc_bindings .DDBCSQLRowCount (self .hstmt )
10151021 self ._clear_rownumber ()
1022+ self ._cached_column_map = None # Clear cache for non-SELECT statements
10161023
10171024 # After successful execution, initialize description if there are results
10181025 column_metadata = []
@@ -1775,8 +1782,16 @@ def fetchmany(self, size: int = None) -> List[Row]:
17751782 else :
17761783 self .rowcount = self ._next_row_index
17771784
1785+ # Performance optimization: return tuples in fast mode
1786+ if self ._fast_mode :
1787+ return [tuple (row_data ) for row_data in rows_data ]
1788+
1789+ # Build column map once and cache it for better performance
1790+ if self ._cached_column_map is None and self .description :
1791+ self ._cached_column_map = {col_desc [0 ]: i for i , col_desc in enumerate (self .description )}
1792+
17781793 # Convert raw data to Row objects
1779- column_map = getattr (self , '_column_name_map' , None )
1794+ column_map = self . _cached_column_map or getattr (self , '_column_name_map' , None )
17801795 return [Row (self , self .description , row_data , column_map ) for row_data in rows_data ]
17811796 except Exception as e :
17821797 # On error, don't increment rownumber - rethrow the error
@@ -1787,7 +1802,7 @@ def fetchall(self) -> List[Row]:
17871802 Fetch all (remaining) rows of a query result.
17881803
17891804 Returns:
1790- List of Row objects.
1805+ List of Row objects or tuples (if fast mode is enabled) .
17911806 """
17921807 self ._check_closed () # Check if the cursor is closed
17931808 if not self ._has_result_set and self .description :
@@ -1813,8 +1828,15 @@ def fetchall(self) -> List[Row]:
18131828 else :
18141829 self .rowcount = self ._next_row_index
18151830
1816- # Convert raw data to Row objects
1817- column_map = getattr (self , '_column_name_map' , None )
1831+ # Performance optimization: return tuples in fast mode
1832+ if self ._fast_mode :
1833+ return [tuple (row_data ) for row_data in rows_data ]
1834+
1835+ # Build column map once and cache it for better performance
1836+ if self ._cached_column_map is None and self .description :
1837+ self ._cached_column_map = {col_desc [0 ]: i for i , col_desc in enumerate (self .description )}
1838+
1839+ column_map = self ._cached_column_map or getattr (self , '_column_name_map' , None )
18181840 return [Row (self , self .description , row_data , column_map ) for row_data in rows_data ]
18191841 except Exception as e :
18201842 # On error, don't increment rownumber - rethrow the error
@@ -2167,4 +2189,48 @@ def tables(self, table=None, catalog=None, schema=None, tableType=None):
21672189 except Exception as e :
21682190 # Log the error and re-raise
21692191 log ('error' , f"Error executing tables query: { e } " )
2170- raise
2192+ raise
2193+
2194+ # Performance optimization methods
2195+ def enable_fast_mode (self ):
2196+ """
2197+ Enable fast mode for better performance.
2198+ In fast mode, fetch methods return tuples instead of Row objects,
2199+ significantly reducing memory usage and object creation overhead.
2200+
2201+ Note: This breaks DB-API compatibility but provides better performance
2202+ for applications that don't need Row object features.
2203+ """
2204+ self ._fast_mode = True
2205+
2206+ def disable_fast_mode (self ):
2207+ """
2208+ Disable fast mode and return to standard Row objects.
2209+ """
2210+ self ._fast_mode = False
2211+
2212+ def is_fast_mode_enabled (self ):
2213+ """
2214+ Check if fast mode is currently enabled.
2215+
2216+ Returns:
2217+ bool: True if fast mode is enabled, False otherwise.
2218+ """
2219+ return self ._fast_mode
2220+
2221+
2222+
2223+ def optimize_for_performance (self ):
2224+ """
2225+ Apply all available performance optimizations to this cursor.
2226+ This includes:
2227+ - Increasing arraysize for better batching (sweet spot between speed and memory)
2228+ - Pre-building column maps for faster row creation
2229+ - Optimizing internal data structures
2230+ """
2231+ # Set arraysize to optimal value based on testing results
2232+ self .arraysize = 5000 # Sweet spot - more aggressive than default but not excessive
2233+
2234+ # Pre-build column map if we have description
2235+ if self .description and self ._cached_column_map is None :
2236+ self ._cached_column_map = {col_desc [0 ]: i for i , col_desc in enumerate (self .description )}
0 commit comments