2626import bigframes .session ._io .bigquery as bq_io
2727from bigframes .core import bq_data , compile , nodes
2828from bigframes .session import executor , semi_executor , execution_spec
29+ from bigframes .core .compile .configs import CompileRequest , CompileResult
2930from bigframes import exceptions as bfe
3031import bigframes .core .schema as schemata
32+ import google .cloud .bigquery_storage_v1
3133
3234import google .api_core .exceptions
3335
@@ -43,11 +45,13 @@ class DirectGbqExecutor(semi_executor.SemiExecutor):
4345 def __init__ (
4446 self ,
4547 bqclient : bigquery .Client ,
46- compiler : Literal ["ibis" , "sqlglot" ]
47- | Callable [[compile .CompileRequest ], executor .CompileResult ] = "ibis" ,
48+ bqstoragereadclient : google .cloud .bigquery_storage_v1 .BigQueryReadClient ,
4849 * ,
50+ compiler : Literal ["ibis" , "sqlglot" ]
51+ | Callable [[CompileRequest ], CompileResult ] = "ibis" ,
4952 metrics : Optional [bigframes .session .metrics .ExecutionMetrics ] = None ,
5053 publisher : Optional [bigframes .core .events .Publisher ] = None ,
54+ labels : Mapping [str , str ] = {},
5155 ):
5256 self .bqclient = bqclient
5357 if isinstance (compiler , str ):
@@ -58,8 +62,10 @@ def __init__(
5862 )
5963 else :
6064 self ._compile_fn = compiler
65+ self ._bqstoragereadclient = bqstoragereadclient
6166 self ._publisher = publisher
6267 self ._metrics = metrics
68+ self ._labels = labels
6369
6470 def execute (
6571 self ,
@@ -69,36 +75,43 @@ def execute(
6975 """Just execute whatever plan as is, without further caching or decomposition."""
7076
7177 og_schema = plan .schema
72- compile_request = compile . CompileRequest (
73- plan ,
74- sort_rows = spec .ordered ,
75- peek_count = spec .peek ,
76- )
78+ compile_request = CompileRequest (
79+ plan ,
80+ sort_rows = spec .ordered ,
81+ peek_count = spec .peek ,
82+ )
7783
7884 compiled = self ._compile_fn (compile_request )
7985 # might have more columns than og schema, for hidden ordering columns
8086 compiled_schema = compiled .sql_schema
8187
8288 job_config = bigquery .QueryJobConfig ()
83- if isinstance (spec .destination_spec , execution_spec .TableOutputSpec ):
84- job_config .destination = spec .destination_spec .table
85- job_config .write_disposition = _WRITE_DISPOSITIONS [spec .destination_spec .if_exists ]
86- job_config .clustering_fields = spec .destination_spec .cluster_cols
87- elif isinstance (spec .destination_spec , execution_spec .TempTableSpec ) and spec .destination_spec .lifetime == "ephemeral" :
88- pass
89- elif spec .destination_spec is not None :
90- raise ValueError (f"Direct GBQ Executor does not support destination: { spec .destination_spec } " )
89+ dest_spec = spec .destination_spec
90+ cluster_cols = ()
91+ if isinstance (dest_spec , execution_spec .TableOutputSpec ):
92+ job_config .destination = dest_spec .table
93+ job_config .write_disposition = _WRITE_DISPOSITIONS [dest_spec .if_exists ]
94+ cluster_cols = dest_spec .cluster_cols
95+ job_config .clustering_fields = dest_spec .cluster_cols
96+ elif (
97+ isinstance (dest_spec , execution_spec .TempTableSpec )
98+ and dest_spec .lifetime == "ephemeral"
99+ ):
100+ cluster_cols = dest_spec .cluster_cols
101+ job_config .clustering_fields = dest_spec .cluster_cols
102+ elif dest_spec is not None :
103+ raise ValueError (
104+ f"Direct GBQ Executor does not support destination: { dest_spec } "
105+ )
91106
92107 job_config .labels ["bigframes-dtypes" ] = compiled .encoded_type_refs
93- can_skip_job = spec . destination_spec is None and spec .promise_under_10gb
108+ can_skip_job = dest_spec is None and spec .promise_under_10gb
94109 iterator , query_job = self ._run_execute_query (
95110 sql = compiled .sql ,
96111 job_config = job_config ,
97112 query_with_job = (not can_skip_job ),
98113 session = plan .session ,
99114 )
100-
101- cluster_cols = spec .destination_spec .cluster_cols if spec .desination_spec else ()
102115 result_bq_data = None
103116 if query_job and query_job .destination :
104117 # we might add extra sql columns in compilation, esp if caching w ordering, infer a bigframes type for them
@@ -128,7 +141,7 @@ def execute(
128141 return executor .BQTableExecuteResult (
129142 data = result_bq_data ,
130143 project_id = self .bqclient .project ,
131- storage_client = self .bqstoragereadclient ,
144+ storage_client = self ._bqstoragereadclient ,
132145 execution_metadata = execution_metadata ,
133146 selected_fields = tuple ((col , col ) for col in og_schema .names ),
134147 )
@@ -159,34 +172,15 @@ def _run_execute_query(
159172 job_config .labels .update (self ._labels )
160173
161174 try :
162- # Trick the type checker into thinking we got a literal.
163- if query_with_job :
164- return bq_io .start_query_with_client (
165- self .bqclient ,
166- sql ,
167- job_config = job_config ,
168- metrics = self ._metrics ,
169- project = None ,
170- location = None ,
171- timeout = None ,
172- query_with_job = True ,
173- publisher = self ._publisher ,
174- session = session ,
175- )
176- else :
177- return bq_io .start_query_with_client (
178- self .bqclient ,
179- sql ,
180- job_config = job_config ,
181- metrics = self ._metrics ,
182- project = None ,
183- location = None ,
184- timeout = None ,
185- query_with_job = False ,
186- publisher = self ._publisher ,
187- session = session ,
188- )
189-
175+ return bq_io .start_query_with_client (
176+ self .bqclient ,
177+ sql ,
178+ job_config = job_config ,
179+ metrics = self ._metrics ,
180+ query_with_job = query_with_job ,
181+ publisher = self ._publisher ,
182+ session = session ,
183+ )
190184 except google .api_core .exceptions .BadRequest as e :
191185 # Unfortunately, this error type does not have a separate error code or exception type
192186 if "Resources exceeded during query execution" in e .message :
@@ -195,6 +189,7 @@ def _run_execute_query(
195189 else :
196190 raise
197191
192+
198193def _result_schema (
199194 logical_schema : schemata .ArraySchema , sql_schema : list [bigquery .SchemaField ]
200195) -> schemata .ArraySchema :
0 commit comments