2828import bigframes .core
2929import bigframes .core .events
3030import bigframes .core .guid
31- import bigframes .core .identifiers
3231import bigframes .core .ordering
3332import bigframes .core .nodes as nodes
3433import bigframes .core .schema as schemata
4039import bigframes .session .metrics
4140import bigframes .session .planner
4241import bigframes .session .temporary_storage
43- from bigframes .core import bq_data , compile , guid , local_data , rewrite
42+ from bigframes .core import bq_data , compile , guid , identifiers , local_data , rewrite
4443from bigframes .core .compile .sqlglot import sql as sg_sql
4544from bigframes .core .compile .sqlglot import sqlglot_ir
4645from bigframes .session import (
@@ -176,23 +175,14 @@ def _execute_bigquery(
176175 ) -> executor .ExecuteResult :
177176 dest_spec = execution_spec .destination_spec
178177 # Recursive handlers for different cases, maybe extract to explicit interface.
179- if (
180- isinstance (dest_spec , ex_spec .EphemeralTableSpec )
181- and not execution_spec .promise_under_10gb
182- ):
183- # Results over 10GB need to explicitly allocate a table.
184- execution_spec = dataclasses .replace (
185- execution_spec , destination_spec = ex_spec .SessionTableSpec ()
186- )
187- return self ._execute_bigquery (array_value , execution_spec )
188178 if isinstance (dest_spec , ex_spec .GcsOutputSpec ):
189179 execution_spec = dataclasses .replace (
190180 execution_spec , destination_spec = ex_spec .EphemeralTableSpec ()
191181 )
192182 results = self ._execute_bigquery (array_value , execution_spec )
193183 self ._export_result_gcs (results , dest_spec )
194184 return results
195- if isinstance (dest_spec , ex_spec .TableOutputSpec ) and dest_spec .permit_dml :
185+ elif isinstance (dest_spec , ex_spec .TableOutputSpec ) and dest_spec .permit_dml :
196186 # Special DML path - maybe this should be configurable, dml vs query destination has tradeoffs
197187 existing_table = self ._maybe_find_existing_table (dest_spec )
198188 if (existing_table is not None ) and _is_schema_match (
@@ -204,18 +194,18 @@ def _execute_bigquery(
204194 results = self ._execute_bigquery (array_value , execution_spec )
205195 self ._export_gbq_with_dml (results , dest_spec )
206196 return results
207- if isinstance (dest_spec , ex_spec .SessionTableSpec ):
197+ elif isinstance (dest_spec , ex_spec .SessionTableSpec ):
208198 # "ephemeral" temp tables created in the course of exeuction, don't need to be allocated
209199 # materialized ordering only really makes sense for internal temp tables used by caching
210200 cluster_cols = dest_spec .cluster_cols
211201 # Rewrite plan to materialize ordering as extra columns
212202 plan = array_value .node
213203 if dest_spec .ordering == "offsets_col" :
214204 order_col_id = guid .generate_guid ()
215- plan = nodes .PromoteOffsetsNode (plan , order_col_id )
205+ plan = nodes .PromoteOffsetsNode (plan , identifiers . ColumnId ( order_col_id ) )
216206 cluster_cols = [order_col_id ]
217207 elif dest_spec .ordering == "order_key" :
218- plan , _ = rewrite .pull_out_order (plan )
208+ plan , ordering = rewrite .pull_out_order (plan )
219209 destination_table = self .storage_manager .create_temp_table (
220210 plan .schema .to_bigquery (), cluster_cols
221211 )
@@ -230,7 +220,16 @@ def _execute_bigquery(
230220 permit_dml = False ,
231221 ),
232222 )
233- return self ._execute_bigquery (arr_value , execution_spec )
223+ result = self ._execute_bigquery (arr_value , execution_spec )
224+ result ._data = dataclasses .replace (result ._data , ordering = ordering )
225+ return result
226+ # Force table creation if result might be large (and user explicitly allowed large results)
227+ elif isinstance (dest_spec , ex_spec .EphemeralTableSpec ) or dest_spec is None :
228+ if not execution_spec .promise_under_10gb :
229+ execution_spec = dataclasses .replace (
230+ execution_spec , destination_spec = ex_spec .SessionTableSpec ()
231+ )
232+ return self ._execute_bigquery (array_value , execution_spec )
234233
235234 # At this point, dst should be unspecified, a specific bq table, or an ephemeral temp table
236235 # Also, ordering mode will either be none or row-sorted
@@ -315,6 +314,7 @@ def _export_gbq_with_dml(
315314 job_config = bigquery .QueryJobConfig (),
316315 metrics = self .metrics ,
317316 publisher = self ._publisher ,
317+ query_with_job = True ,
318318 )
319319
320320 def dry_run (
@@ -407,13 +407,14 @@ def _cache_with_cluster_cols(
407407 ]
408408 cluster_cols = cluster_cols [:_MAX_CLUSTER_COLUMNS ]
409409 execution_spec = ex_spec .ExecutionSpec (
410- destination_spec = ex_spec .SessionTableSpec (cluster_cols = tuple (cluster_cols ))
410+ destination_spec = ex_spec .SessionTableSpec (cluster_cols = tuple (cluster_cols ), ordering = "order_key" )
411411 )
412412 result = self .execute (
413413 array_value ,
414414 execution_spec = execution_spec ,
415415 )
416416 assert isinstance (result , executor .BQTableExecuteResult )
417+ assert result ._data .ordering is not None
417418 self .cache .cache_results_table (array_value .node , result ._data )
418419
419420 def _cache_with_offsets (self , array_value : bigframes .core .ArrayValue ):
@@ -428,6 +429,7 @@ def _cache_with_offsets(self, array_value: bigframes.core.ArrayValue):
428429 execution_spec = execution_spec ,
429430 )
430431 assert isinstance (result , executor .BQTableExecuteResult )
432+ assert result ._data .ordering is not None
431433 self .cache .cache_results_table (array_value .node , result ._data )
432434
433435 def _cache_with_session_awareness (
0 commit comments