22import os
33import socket
44import typing
5- from collections import deque
5+ from collections import deque , OrderedDict
66from copy import deepcopy
77from datetime import datetime as Datetime
88from datetime import timedelta as Timedelta
@@ -1826,11 +1826,14 @@ def execute(self: "Connection", cursor: Cursor, operation: str, vals) -> None:
18261826 # transforms user provided bind parameters to server friendly bind parameters
18271827 params : typing .Tuple [typing .Optional [typing .Tuple [int , int , typing .Callable ]], ...] = ()
18281828 has_bind_parameters : bool = False if vals is None else True
1829- # multi dimensional dictionary to store the data
1830- # cache = self._caches[cursor.paramstyle][pid]
1831- # cache = {'statement': {}, 'ps': {}}
1832- # statement stores the data of the statement, ps store the data of the prepared statement
1833- # statement = {operation(query): tuple from 'convert_paramstyle'(statement, make_args)}
1829+ statements_to_close = []
1830+ # Cache structure for prepared statements:
1831+ # self._caches[paramstyle][pid] contains:
1832+ # - 'statement': stores SQL statements and their parameter processors
1833+ # - 'ps': stores prepared statement metadata
1834+ # - 'statement_dict': OrderedDict tracking most recently used prepared statements
1835+ # (used when max_prepared_statements is set)
1836+ # Each statement entry contains (processed_statement, parameter_binding_function)
18341837 try :
18351838 cache = self ._caches [cursor .paramstyle ][pid ]
18361839 except KeyError :
@@ -1842,7 +1845,11 @@ def execute(self: "Connection", cursor: Cursor, operation: str, vals) -> None:
18421845 try :
18431846 cache = param_cache [pid ]
18441847 except KeyError :
1845- cache = param_cache [pid ] = {"statement" : {}, "ps" : {}}
1848+ cache = param_cache [pid ] = {
1849+ "statement" : {},
1850+ "ps" : {},
1851+ "statement_dict" : OrderedDict () if self .max_prepared_statements > 0 else None
1852+ }
18461853
18471854 try :
18481855 statement , make_args = cache ["statement" ][operation ]
@@ -1863,6 +1870,9 @@ def execute(self: "Connection", cursor: Cursor, operation: str, vals) -> None:
18631870
18641871 try :
18651872 ps = cache ["ps" ][key ]
1873+ # If statement exists, move it to end of ordered dict (most recently used)
1874+ if self .max_prepared_statements > 0 and 'statement_dict' in cache and key in cache ["statement_dict" ]:
1875+ cache ["statement_dict" ].move_to_end (key )
18661876 _logger .debug ("Using cached prepared statement" )
18671877 cursor .ps = ps
18681878 except KeyError :
@@ -1978,12 +1988,21 @@ def execute(self: "Connection", cursor: Cursor, operation: str, vals) -> None:
19781988
19791989 ps ["bind_2" ] = h_pack (len (output_fc )) + pack ("!" + "h" * len (output_fc ), * output_fc )
19801990
1981- if len (cache ["ps" ]) >= self .max_prepared_statements :
1982- for p in cache ["ps" ].values ():
1983- self .close_prepared_statement (p ["statement_name_bin" ])
1984- cache ["ps" ].clear ()
19851991 if self .max_prepared_statements > 0 :
1992+ # Ensure consistency between ps and statement_dict
1993+ if len (cache ["ps" ]) != len (cache ["statement_dict" ]):
1994+ for existing_key in cache ["ps" ]:
1995+ cache ["statement_dict" ][existing_key ] = None
1996+
1997+ # If cache is full, remove oldest statement
1998+ if len (cache ["ps" ]) >= self .max_prepared_statements :
1999+ oldest_key , _ = cache ["statement_dict" ].popitem (last = False )
2000+ statements_to_close .append (cache ["ps" ][oldest_key ]["statement_name_bin" ])
2001+ del cache ["ps" ][oldest_key ]
2002+
2003+ # Add new statement to cache and queue
19862004 cache ["ps" ][key ] = ps
2005+ cache ["statement_dict" ][key ] = None
19872006
19882007 cursor ._cached_rows .clear ()
19892008 cursor ._row_count = - 1
@@ -2031,6 +2050,10 @@ def execute(self: "Connection", cursor: Cursor, operation: str, vals) -> None:
20312050 else :
20322051 self .handle_messages (cursor )
20332052
2053+ # Clean up prepared statements after query execution and results are returned
2054+ for stmt in statements_to_close :
2055+ self .close_prepared_statement (stmt )
2056+
20342057 def _send_message (self : "Connection" , code : bytes , data : bytes ) -> None :
20352058 _logger .debug ("Sending message with code %s to BE" , code )
20362059 try :
0 commit comments