@@ -229,7 +229,14 @@ def __init__(
229229 ] = None , # Use Any because it's recursive.
230230 ) -> None :
231231 super ().__init__ ()
232- self .analyzer = analyzer
232+ # With multi-threading support, each thread has its own analyzer which can be
233+ # accessed through session object. Therefore, we need to store the session in
234+ # the Selectable object and use the session to access the appropriate analyzer
235+ # for current thread.
236+ self ._session = analyzer .session
237+ # We create this internal object to be used for setting query generator during
238+ # the optimization stage
239+ self ._analyzer = None
233240 self .pre_actions : Optional [List ["Query" ]] = None
234241 self .post_actions : Optional [List ["Query" ]] = None
235242 self .flatten_disabled : bool = False
@@ -243,6 +250,23 @@ def __init__(
243250 self ._cumulative_node_complexity : Optional [Dict [PlanNodeCategory , int ]] = None
244251 self ._encoded_node_id_with_query : Optional [str ] = None
245252
253+ @property
254+ def analyzer (self ) -> "Analyzer" :
255+ """Get the analyzer for used for the current thread"""
256+ return self ._analyzer or self ._session ._analyzer
257+
258+ @analyzer .setter
259+ def analyzer (self , value : "Analyzer" ) -> None :
260+ """For query optimization stage, we need to replace the analyzer with a query generator which
261+ is aware of schema for the final plan and can compile WithQueryBlocks. Therefore we update the
262+ setter to allow the analyzer to be set externally."""
263+ if not self ._is_valid_for_replacement :
264+ raise ValueError (
265+ "Cannot set analyzer for a Selectable that is not valid for replacement"
266+ )
267+
268+ self ._analyzer = value
269+
246270 @property
247271 @abstractmethod
248272 def sql_query (self ) -> str :
@@ -258,7 +282,7 @@ def encoded_node_id_with_query(self) -> str:
258282 two selectable node with same queries. This is currently used by repeated subquery
259283 elimination to detect two nodes with same query, please use it with careful.
260284 """
261- with self .analyzer . session ._plan_lock :
285+ with self ._session ._plan_lock :
262286 if self ._encoded_node_id_with_query is None :
263287 self ._encoded_node_id_with_query = encode_node_id_with_query (self )
264288 return self ._encoded_node_id_with_query
@@ -310,7 +334,7 @@ def get_snowflake_plan(self, skip_schema_query) -> SnowflakePlan:
310334 queries ,
311335 schema_query ,
312336 post_actions = self .post_actions ,
313- session = self .analyzer . session ,
337+ session = self ._session ,
314338 expr_to_alias = self .expr_to_alias ,
315339 df_aliased_col_name_to_real_col_name = self .df_aliased_col_name_to_real_col_name ,
316340 source_plan = self ,
@@ -328,7 +352,7 @@ def plan_state(self) -> Dict[PlanState, Any]:
328352
329353 @property
330354 def cumulative_node_complexity (self ) -> Dict [PlanNodeCategory , int ]:
331- with self .analyzer . session ._plan_lock :
355+ with self ._session ._plan_lock :
332356 if self ._cumulative_node_complexity is None :
333357 self ._cumulative_node_complexity = sum_node_complexities (
334358 self .individual_node_complexity ,
@@ -361,7 +385,7 @@ def column_states(self) -> ColumnStateDict:
361385 Refer to class ColumnStateDict.
362386 """
363387 if self ._column_states is None :
364- if self .analyzer . session .reduce_describe_query_enabled :
388+ if self ._session .reduce_describe_query_enabled :
365389 # data types are not needed in SQL simplifier, so we
366390 # just create dummy data types here.
367391 column_attrs = [
@@ -512,7 +536,7 @@ def __init__(
512536 self .pre_actions [0 ].query_id_place_holder
513537 )
514538 self ._schema_query = analyzer_utils .schema_value_statement (
515- analyze_attributes (sql , self .analyzer . session )
539+ analyze_attributes (sql , self ._session )
516540 ) # Change to subqueryable schema query so downstream query plan can describe the SQL
517541 self ._query_param = None
518542 else :
@@ -1165,7 +1189,7 @@ def filter(self, col: Expression) -> "SelectStatement":
11651189 new = SelectStatement (
11661190 from_ = self .to_subqueryable (), where = col , analyzer = self .analyzer
11671191 )
1168- if self .analyzer . session .reduce_describe_query_enabled :
1192+ if self ._session .reduce_describe_query_enabled :
11691193 new ._attributes = self ._attributes
11701194
11711195 return new
@@ -1200,7 +1224,7 @@ def sort(self, cols: List[Expression]) -> "SelectStatement":
12001224 order_by = cols ,
12011225 analyzer = self .analyzer ,
12021226 )
1203- if self .analyzer . session .reduce_describe_query_enabled :
1227+ if self ._session .reduce_describe_query_enabled :
12041228 new ._attributes = self ._attributes
12051229
12061230 return new
@@ -1284,7 +1308,7 @@ def limit(self, n: int, *, offset: int = 0) -> "SelectStatement":
12841308 new .pre_actions = new .from_ .pre_actions
12851309 new .post_actions = new .from_ .post_actions
12861310 new ._merge_projection_complexity_with_subquery = False
1287- if self .analyzer . session .reduce_describe_query_enabled :
1311+ if self ._session .reduce_describe_query_enabled :
12881312 new ._attributes = self ._attributes
12891313
12901314 return new
@@ -1604,7 +1628,7 @@ def can_select_projection_complexity_be_merged(
16041628 on top of subquery.
16051629 subquery: the subquery where the current select is performed on top of
16061630 """
1607- if not subquery .analyzer . session ._large_query_breakdown_enabled :
1631+ if not subquery ._session ._large_query_breakdown_enabled :
16081632 return False
16091633
16101634 # only merge of nested select statement is supported, and subquery must be
0 commit comments