@@ -77,6 +77,11 @@ def get_table_metadata(
77
77
return cached_table
78
78
79
79
table = bqclient .get_table (table_ref )
80
+ # local time will lag a little bit do to network latency
81
+ # make sure it is at least table creation time.
82
+ # This is relevant if the table was created immediately before loading it here.
83
+ if (table .created is not None ) and (table .created > bq_time ):
84
+ bq_time = table .created
80
85
81
86
cached_table = (bq_time , table )
82
87
cache [table_ref ] = cached_table
@@ -85,64 +90,66 @@ def get_table_metadata(
85
90
86
91
def validate_table (
87
92
bqclient : bigquery .Client ,
88
- table_ref : bigquery .table .TableReference ,
93
+ table : bigquery .table .Table ,
89
94
columns : Optional [Sequence [str ]],
90
95
snapshot_time : datetime .datetime ,
91
- table_type : str ,
92
96
filter_str : Optional [str ] = None ,
93
97
) -> bool :
94
98
"""Validates that the table can be read, returns True iff snapshot is supported."""
95
- # First run without snapshot to verify table can be read
96
- sql = bigframes .session ._io .bigquery .to_query (
97
- query_or_table = f"{ table_ref .project } .{ table_ref .dataset_id } .{ table_ref .table_id } " ,
98
- columns = columns or (),
99
- sql_predicate = filter_str ,
100
- )
101
- dry_run_config = bigquery .QueryJobConfig ()
102
- dry_run_config .dry_run = True
103
- try :
104
- bqclient .query_and_wait (sql , job_config = dry_run_config )
105
- except google .api_core .exceptions .Forbidden as ex :
106
- if "Drive credentials" in ex .message :
107
- ex .message += "\n Check https://cloud.google.com/bigquery/docs/query-drive-data#Google_Drive_permissions."
108
- raise
109
99
100
+ time_travel_not_found = False
110
101
# Anonymous dataset, does not support snapshot ever
111
- if table_ref .dataset_id .startswith ("_" ):
112
- return False
113
-
114
- # Materialized views,does not support snapshot
115
- if table_type == "MATERIALIZED_VIEW" :
116
- warnings .warn (
117
- "Materialized views do not support FOR SYSTEM_TIME AS OF queries. "
118
- "Attempting query without time travel. Be aware that as materialized views "
119
- "are updated periodically, modifications to the underlying data in the view may "
120
- "result in errors or unexpected behavior." ,
121
- category = bigframes .exceptions .TimeTravelDisabledWarning ,
102
+ if table .dataset_id .startswith ("_" ):
103
+ pass
104
+ # Only true tables support time travel
105
+ elif table .table_type != "TABLE" :
106
+ if table .table_type == "MATERIALIZED_VIEW" :
107
+ warnings .warn (
108
+ "Materialized views do not support FOR SYSTEM_TIME AS OF queries. "
109
+ "Attempting query without time travel. Be aware that as materialized views "
110
+ "are updated periodically, modifications to the underlying data in the view may "
111
+ "result in errors or unexpected behavior." ,
112
+ category = bigframes .exceptions .TimeTravelDisabledWarning ,
113
+ )
114
+ else :
115
+ # table might support time travel, lets do a dry-run query with time travel
116
+ snapshot_sql = bigframes .session ._io .bigquery .to_query (
117
+ query_or_table = f"{ table .reference .project } .{ table .reference .dataset_id } .{ table .reference .table_id } " ,
118
+ columns = columns or (),
119
+ sql_predicate = filter_str ,
120
+ time_travel_timestamp = snapshot_time ,
122
121
)
123
- return False
122
+ try :
123
+ # If this succeeds, we don't need to query without time travel, that would surely succeed
124
+ bqclient .query_and_wait (
125
+ snapshot_sql , job_config = bigquery .QueryJobConfig (dry_run = True )
126
+ )
127
+ return True
128
+ except google .api_core .exceptions .NotFound :
129
+ # note that a notfound caused by a simple typo will be
130
+ # caught above when the metadata is fetched, not here
131
+ time_travel_not_found = True
124
132
125
- # Second, try with snapshot to verify table supports this feature
133
+ # At this point, time travel is known to fail, but can we query without time travel?
126
134
snapshot_sql = bigframes .session ._io .bigquery .to_query (
127
- query_or_table = f"{ table_ref . project } .{ table_ref . dataset_id } .{ table_ref .table_id } " ,
135
+ query_or_table = f"{ table . reference . project } .{ table . reference . dataset_id } .{ table . reference .table_id } " ,
128
136
columns = columns or (),
129
137
sql_predicate = filter_str ,
130
- time_travel_timestamp = snapshot_time ,
138
+ time_travel_timestamp = None ,
131
139
)
132
- try :
133
- bqclient .query_and_wait (snapshot_sql , job_config = dry_run_config )
134
- return True
135
- except google .api_core .exceptions .NotFound :
136
- # note that a notfound caused by a simple typo will be
137
- # caught above when the metadata is fetched, not here
140
+ # Any erorrs here should just be raised to user
141
+ bqclient .query_and_wait (
142
+ snapshot_sql , job_config = bigquery .QueryJobConfig (dry_run = True )
143
+ )
144
+ if time_travel_not_found :
138
145
warnings .warn (
139
146
"NotFound error when reading table with time travel."
140
147
" Attempting query without time travel. Warning: Without"
141
148
" time travel, modifications to the underlying table may"
142
149
" result in errors or unexpected behavior." ,
143
150
category = bigframes .exceptions .TimeTravelDisabledWarning ,
144
151
)
145
- return False
152
+ return False
146
153
147
154
148
155
def are_index_cols_unique (
0 commit comments