5151
5252# pylint: disable=protected-access
5353@calculate_execution_time ()
54- @reflection .cache
5554def get_columns (self , connection , table_name , schema = None , ** kw ):
5655 """
5756 Return information about columns in `table_name`.
@@ -61,6 +60,10 @@ def get_columns(self, connection, table_name, schema=None, **kw):
6160
6261 overriding the default dialect method to include the
6362 distkey and sortkey info
63+
64+ Note: @reflection.cache removed to avoid unbounded memory growth
65+ across schemas (issue #20649). The underlying
66+ _get_schema_column_info already caches per-schema.
6467 """
6568 cols = self ._get_redshift_columns (connection , table_name , schema , ** kw )
6669 if not self ._domains :
@@ -121,22 +124,21 @@ def _get_column_info(self, *args, **kwargs):
121124
122125
123126@calculate_execution_time ()
124- @reflection .cache
125127def _get_schema_column_info (
126128 self , connection , schema = None , ** kw
127129): # pylint: disable=unused-argument
128130 """
129131 Get schema column info
130132
131- Args:
132- connection:
133- schema:
134- **kw:
135- Returns:
136-
137- This method is responsible for fetching all the column details like
138- name, type, constraints, distkey and sortkey etc.
133+ Uses a custom single-schema cache instead of @reflection.cache
134+ to prevent unbounded memory growth across schemas (issue #20649).
135+ Only the most recently requested schema's data is retained.
139136 """
137+ # Single-schema cache: invalidate when schema changes
138+ cached = getattr (self , "_schema_col_cache" , None )
139+ if cached is not None and cached [0 ] == schema :
140+ return cached [1 ]
141+
140142 schema_clause = f"AND schema = '{ schema if schema else '' } '"
141143 all_columns = defaultdict (list )
142144 result = connection .execute (
@@ -145,7 +147,10 @@ def _get_schema_column_info(
145147 for col in result :
146148 key = RelationKey (col .table_name , col .schema , connection )
147149 all_columns [key ].append (col )
148- return dict (all_columns )
150+ result .close ()
151+ data = dict (all_columns )
152+ self ._schema_col_cache = (schema , data )
153+ return data
149154
150155
151156def _handle_array_type (attype ):
@@ -377,10 +382,19 @@ def get_table_comment(
377382
378383
379384@calculate_execution_time ()
380- @reflection .cache
381385def _get_all_relation_info (self , connection , ** kw ): # pylint: disable=unused-argument
386+ """
387+ Uses a custom single-schema cache instead of @reflection.cache
388+ to prevent unbounded memory growth across schemas (issue #20649).
389+ """
382390 # pylint: disable=consider-using-f-string
383391 schema = kw .get ("schema" , None )
392+
393+ # Single-schema cache: invalidate when schema changes
394+ cached = getattr (self , "_relation_info_cache" , None )
395+ if cached is not None and cached [0 ] == schema :
396+ return cached [1 ]
397+
384398 schema_clause = "AND schema = '{schema}'" .format (schema = schema ) if schema else ""
385399
386400 table_name = kw .get ("table_name" , None )
@@ -399,6 +413,8 @@ def _get_all_relation_info(self, connection, **kw): # pylint: disable=unused-ar
399413 for rel in result :
400414 key = RelationKey (rel .relname , rel .schema , connection )
401415 relations [key ] = rel
416+ result .close ()
417+ self ._relation_info_cache = (schema , relations )
402418 return relations
403419
404420
0 commit comments