Skip to content

Commit db24010

Browse files

File tree

bigquery/google/cloud/bigquery/job.py

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
"""Define API Jobs."""
1616

17-
import copy
1817
import threading
1918

2019
import six
@@ -1291,12 +1290,12 @@ def query_plan(self):
12911290
See:
12921291
https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs#statistics.query.queryPlan
12931292
1294-
:rtype: list of dict
1293+
:rtype: list of :class:`QueryPlanEntry`
12951294
:returns: mappings describing the query plan, or an empty list
12961295
if the query has not yet completed.
12971296
"""
12981297
plan_entries = self._job_statistics().get('queryPlan', ())
1299-
return [copy.deepcopy(entry) for entry in plan_entries]
1298+
return [QueryPlanEntry.from_api_repr(entry) for entry in plan_entries]
13001299

13011300
def query_results(self):
13021301
"""Construct a QueryResults instance, bound to this job.
@@ -1324,3 +1323,149 @@ def result(self, timeout=None):
13241323
super(QueryJob, self).result(timeout=timeout)
13251324
# Return a QueryResults instance instead of returning the job.
13261325
return self.query_results()
1326+
1327+
1328+
class QueryPlanEntryStep(object):
1329+
"""Map a single step in a query plan entry.
1330+
1331+
:type kind: str
1332+
:param kind: step type
1333+
1334+
:type substeps:
1335+
:param substeps: names of substeps
1336+
"""
1337+
def __init__(self, kind, substeps):
1338+
self.kind = kind
1339+
self.substeps = list(substeps)
1340+
1341+
@classmethod
1342+
def from_api_repr(cls, resource):
1343+
"""Factory: construct instance from the JSON repr.
1344+
1345+
:type resource: dict
1346+
:param resource: JSON representation of the entry
1347+
1348+
:rtype: :class:`QueryPlanEntryStep`
1349+
:return: new instance built from the resource
1350+
"""
1351+
return cls(
1352+
kind=resource.get('kind'),
1353+
substeps=resource.get('substeps', ()),
1354+
)
1355+
1356+
def __eq__(self, other):
1357+
if not isinstance(other, self.__class__):
1358+
return NotImplemented
1359+
return self.kind == other.kind and self.substeps == other.substeps
1360+
1361+
1362+
class QueryPlanEntry(object):
1363+
"""Map a single entry in a query plan.
1364+
1365+
:type name: str
1366+
:param name: name of the entry
1367+
1368+
:type entry_id: int
1369+
:param entry_id: ID of the entry
1370+
1371+
:type wait_ratio_avg: float
1372+
:param wait_ratio_avg: average wait ratio
1373+
1374+
:type wait_ratio_max: float
1375+
:param wait_ratio_avg: maximum wait ratio
1376+
1377+
:type read_ratio_avg: float
1378+
:param read_ratio_avg: average read ratio
1379+
1380+
:type read_ratio_max: float
1381+
:param read_ratio_avg: maximum read ratio
1382+
1383+
:type copute_ratio_avg: float
1384+
:param copute_ratio_avg: average copute ratio
1385+
1386+
:type copute_ratio_max: float
1387+
:param copute_ratio_avg: maximum copute ratio
1388+
1389+
:type write_ratio_avg: float
1390+
:param write_ratio_avg: average write ratio
1391+
1392+
:type write_ratio_max: float
1393+
:param write_ratio_avg: maximum write ratio
1394+
1395+
:type records_read: int
1396+
:param records_read: number of records read
1397+
1398+
:type records_written: int
1399+
:param records_written: number of records written
1400+
1401+
:type status: str
1402+
:param status: entry status
1403+
1404+
:type steps: List(QueryPlanEntryStep)
1405+
:param steps: steps in the entry
1406+
"""
1407+
def __init__(self,
1408+
name,
1409+
entry_id,
1410+
wait_ratio_avg,
1411+
wait_ratio_max,
1412+
read_ratio_avg,
1413+
read_ratio_max,
1414+
compute_ratio_avg,
1415+
compute_ratio_max,
1416+
write_ratio_avg,
1417+
write_ratio_max,
1418+
records_read,
1419+
records_written,
1420+
status,
1421+
steps):
1422+
self.name = name
1423+
self.entry_id = entry_id
1424+
self.wait_ratio_avg = wait_ratio_avg
1425+
self.wait_ratio_max = wait_ratio_max
1426+
self.read_ratio_avg = read_ratio_avg
1427+
self.read_ratio_max = read_ratio_max
1428+
self.compute_ratio_avg = compute_ratio_avg
1429+
self.compute_ratio_max = compute_ratio_max
1430+
self.write_ratio_avg = write_ratio_avg
1431+
self.write_ratio_max = write_ratio_max
1432+
self.records_read = records_read
1433+
self.records_written = records_written
1434+
self.status = status
1435+
self.steps = steps
1436+
1437+
@classmethod
1438+
def from_api_repr(cls, resource):
1439+
"""Factory: construct instance from the JSON repr.
1440+
1441+
:type resource: dict
1442+
:param resource: JSON representation of the entry
1443+
1444+
:rtype: :class:`QueryPlanEntry`
1445+
:return: new instance built from the resource
1446+
"""
1447+
records_read = resource.get('recordsRead')
1448+
if records_read is not None:
1449+
records_read = int(records_read)
1450+
1451+
records_written = resource.get('recordsWritten')
1452+
if records_written is not None:
1453+
records_written = int(records_written)
1454+
1455+
return cls(
1456+
name=resource.get('name'),
1457+
entry_id=resource.get('id'),
1458+
wait_ratio_avg=resource.get('waitRatioAvg'),
1459+
wait_ratio_max=resource.get('waitRatioMax'),
1460+
read_ratio_avg=resource.get('readRatioAvg'),
1461+
read_ratio_max=resource.get('readRatioMax'),
1462+
compute_ratio_avg=resource.get('computeRatioAvg'),
1463+
compute_ratio_max=resource.get('computeRatioMax'),
1464+
write_ratio_avg=resource.get('writeRatioAvg'),
1465+
write_ratio_max=resource.get('writeRatioMax'),
1466+
records_read=records_read,
1467+
records_written=records_written,
1468+
status=resource.get('status'),
1469+
steps=[QueryPlanEntryStep.from_api_repr(step)
1470+
for step in resource.get('steps', ())],
1471+
)

0 commit comments

Comments
 (0)