|
25 | 25 | import requests |
26 | 26 |
|
27 | 27 | from google.cloud.bigquery.client import _LIST_ROWS_FROM_QUERY_RESULTS_FIELDS |
| 28 | +import google.cloud.bigquery._job_helpers |
28 | 29 | import google.cloud.bigquery.query |
29 | 30 | from google.cloud.bigquery.table import _EmptyRowIterator |
30 | 31 |
|
@@ -1081,6 +1082,114 @@ def test_result_with_done_job_calls_get_query_results(self): |
1081 | 1082 | timeout=None, |
1082 | 1083 | ) |
1083 | 1084 | conn.api_request.assert_has_calls([query_results_call, query_results_page_call]) |
| 1085 | + assert conn.api_request.call_count == 2 |
| 1086 | + |
| 1087 | + def test_result_with_done_jobs_query_response_doesnt_call_get_query_results(self): |
| 1088 | + """With a done result from jobs.query, we don't need to call |
| 1089 | + jobs.getQueryResults to wait for the query to finish. |
| 1090 | +
|
| 1091 | + jobs.get is still called because there is an assumption that after |
| 1092 | + QueryJob.result(), all job metadata is available locally. |
| 1093 | + """ |
| 1094 | + job_resource = self._make_resource(started=True, ended=True, location="EU") |
| 1095 | + conn = make_connection(job_resource) |
| 1096 | + client = _make_client(self.PROJECT, connection=conn) |
| 1097 | + query_resource_done = { |
| 1098 | + "jobComplete": True, |
| 1099 | + "jobReference": {"projectId": self.PROJECT, "jobId": self.JOB_ID}, |
| 1100 | + "schema": {"fields": [{"name": "col1", "type": "STRING"}]}, |
| 1101 | + "rows": [{"f": [{"v": "abc"}]}], |
| 1102 | + "totalRows": "1", |
| 1103 | + } |
| 1104 | + job = google.cloud.bigquery._job_helpers._to_query_job( |
| 1105 | + client, |
| 1106 | + "SELECT 'abc' AS col1", |
| 1107 | + request_config=None, |
| 1108 | + query_response=query_resource_done, |
| 1109 | + ) |
| 1110 | + assert job.state == "DONE" |
| 1111 | + |
| 1112 | + result = job.result() |
| 1113 | + |
| 1114 | + rows = list(result) |
| 1115 | + self.assertEqual(len(rows), 1) |
| 1116 | + self.assertEqual(rows[0].col1, "abc") |
| 1117 | + job_path = f"/projects/{self.PROJECT}/jobs/{self.JOB_ID}" |
| 1118 | + conn.api_request.assert_called_once_with( |
| 1119 | + method="GET", |
| 1120 | + path=job_path, |
| 1121 | + query_params={}, |
| 1122 | + timeout=None, |
| 1123 | + ) |
| 1124 | + |
| 1125 | + def test_result_with_done_jobs_query_response_and_page_size_invalidates_cache(self): |
| 1126 | + """We don't call jobs.query with a page size, so if the user explicitly |
| 1127 | + requests a certain size, invalidate the cache. |
| 1128 | + """ |
| 1129 | + # Arrange |
| 1130 | + job_resource = self._make_resource( |
| 1131 | + started=True, ended=True, location="asia-northeast1" |
| 1132 | + ) |
| 1133 | + query_resource_done = { |
| 1134 | + "jobComplete": True, |
| 1135 | + "jobReference": {"projectId": self.PROJECT, "jobId": self.JOB_ID}, |
| 1136 | + "schema": {"fields": [{"name": "col1", "type": "STRING"}]}, |
| 1137 | + "rows": [{"f": [{"v": "abc"}]}], |
| 1138 | + "pageToken": "initial-page-token-shouldnt-be-used", |
| 1139 | + "totalRows": "4", |
| 1140 | + } |
| 1141 | + query_page_resource = { |
| 1142 | + "totalRows": 4, |
| 1143 | + "pageToken": "some-page-token", |
| 1144 | + "rows": [ |
| 1145 | + {"f": [{"v": "row1"}]}, |
| 1146 | + {"f": [{"v": "row2"}]}, |
| 1147 | + {"f": [{"v": "row3"}]}, |
| 1148 | + ], |
| 1149 | + } |
| 1150 | + query_page_resource_2 = {"totalRows": 4, "rows": [{"f": [{"v": "row4"}]}]} |
| 1151 | + conn = make_connection(job_resource, query_page_resource, query_page_resource_2) |
| 1152 | + client = _make_client(self.PROJECT, connection=conn) |
| 1153 | + job = google.cloud.bigquery._job_helpers._to_query_job( |
| 1154 | + client, |
| 1155 | + "SELECT col1 FROM table", |
| 1156 | + request_config=None, |
| 1157 | + query_response=query_resource_done, |
| 1158 | + ) |
| 1159 | + assert job.state == "DONE" |
| 1160 | + |
| 1161 | + # Act |
| 1162 | + result = job.result(page_size=3) |
| 1163 | + |
| 1164 | + # Assert |
| 1165 | + actual_rows = list(result) |
| 1166 | + self.assertEqual(len(actual_rows), 4) |
| 1167 | + |
| 1168 | + query_results_path = f"/projects/{self.PROJECT}/queries/{self.JOB_ID}" |
| 1169 | + query_page_1_call = mock.call( |
| 1170 | + method="GET", |
| 1171 | + path=query_results_path, |
| 1172 | + query_params={ |
| 1173 | + "maxResults": 3, |
| 1174 | + "fields": _LIST_ROWS_FROM_QUERY_RESULTS_FIELDS, |
| 1175 | + "location": "asia-northeast1", |
| 1176 | + "formatOptions.useInt64Timestamp": True, |
| 1177 | + }, |
| 1178 | + timeout=None, |
| 1179 | + ) |
| 1180 | + query_page_2_call = mock.call( |
| 1181 | + method="GET", |
| 1182 | + path=query_results_path, |
| 1183 | + query_params={ |
| 1184 | + "pageToken": "some-page-token", |
| 1185 | + "maxResults": 3, |
| 1186 | + "fields": _LIST_ROWS_FROM_QUERY_RESULTS_FIELDS, |
| 1187 | + "location": "asia-northeast1", |
| 1188 | + "formatOptions.useInt64Timestamp": True, |
| 1189 | + }, |
| 1190 | + timeout=None, |
| 1191 | + ) |
| 1192 | + conn.api_request.assert_has_calls([query_page_1_call, query_page_2_call]) |
1084 | 1193 |
|
1085 | 1194 | def test_result_with_max_results(self): |
1086 | 1195 | from google.cloud.bigquery.table import RowIterator |
|
0 commit comments