Skip to content

Commit 77b24f0

Browse files
committed
feat(bigquery): Add script statistics to job resource.
1 parent 3b612b1 commit 77b24f0

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

bigquery/google/cloud/bigquery/job.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,15 @@ def parent_job_id(self):
344344
"""
345345
return _helpers._get_sub_prop(self._properties, ["statistics", "parentJobId"])
346346

347+
@property
348+
def script_statistics(self):
349+
resource = _helpers._get_sub_prop(
350+
self._properties, ["statistics", "scriptStatistics"]
351+
)
352+
if resource is None:
353+
return None
354+
return ScriptStatistics(resource)
355+
347356
@property
348357
def num_child_jobs(self):
349358
"""The number of child jobs executed.
@@ -3456,3 +3465,82 @@ def from_api_repr(cls, resource, client):
34563465
resource["jobReference"] = job_ref_properties
34573466
job._properties = resource
34583467
return job
3468+
3469+
3470+
class ScriptStackFrame(object):
3471+
"""Stack frame showing the line/column/procedure name where the current
3472+
evaluation happened.
3473+
3474+
Args:
3475+
resource (Map[str, Any]):
3476+
JSON representation of object.
3477+
"""
3478+
3479+
def __init__(self, resource):
3480+
self._properties = resource
3481+
3482+
@property
3483+
def procedure_id(self):
3484+
"""str: Name of the active procedure, empty if in a top-level
3485+
script.
3486+
"""
3487+
return self._properties.get("procedureId")
3488+
3489+
@property
3490+
def text(self):
3491+
"""str: Text of the current statement/expression."""
3492+
return self._properties.get("text")
3493+
3494+
@property
3495+
def start_line(self):
3496+
"""int: One-based start line."""
3497+
return _helpers._int_or_none(self._properties.get("startLine"))
3498+
3499+
@property
3500+
def start_column(self):
3501+
"""int: One-based start column."""
3502+
return _helpers._int_or_none(self._properties.get("startColumn"))
3503+
3504+
@property
3505+
def end_line(self):
3506+
"""int: One-based end line."""
3507+
return _helpers._int_or_none(self._properties.get("endLine"))
3508+
3509+
@property
3510+
def end_column(self):
3511+
"""int: One-based end column."""
3512+
return _helpers._int_or_none(self._properties.get("endColumn"))
3513+
3514+
3515+
class ScriptStatistics(object):
3516+
"""Statistics for a child job of a script.
3517+
3518+
Args:
3519+
resource (Map[str, Any]):
3520+
JSON representation of object.
3521+
"""
3522+
3523+
def __init__(self, resource):
3524+
self._properties = resource
3525+
3526+
@property
3527+
def stack_frames(self):
3528+
"""List[ScriptStackFrame]: Stack trace where the current evaluation
3529+
happened.
3530+
3531+
Shows line/column/procedure name of each frame on the stack at the
3532+
point where the current evaluation happened.
3533+
3534+
The leaf frame is first, the primary script is last.
3535+
"""
3536+
return [
3537+
ScriptStackFrame(frame) for frame in self._properties.get("stackFrames", [])
3538+
]
3539+
3540+
@property
3541+
def evaluation_kind(self):
3542+
"""str: Indicates the type of child job.
3543+
3544+
Possible values include ``STATEMENT`` and ``EXPRESSION``.
3545+
"""
3546+
return self._properties.get("evaluationKind")

bigquery/tests/unit/test_job.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,46 @@ def test_parent_job_id(self):
276276
job._properties["statistics"] = {"parentJobId": "parent-job-123"}
277277
self.assertEqual(job.parent_job_id, "parent-job-123")
278278

279+
def test_script_statistics(self):
280+
client = _make_client(project=self.PROJECT)
281+
job = self._make_one(self.JOB_ID, client)
282+
283+
self.assertIsNone(job.script_statistics)
284+
job._properties["statistics"] = {
285+
"scriptStatistics": {
286+
"evaluationKind": "EXPRESSION",
287+
"stackFrames": [
288+
{
289+
"procedureId": "some-procedure",
290+
"startLine": 5,
291+
"startColumn": 29,
292+
"endLine": 9,
293+
"endColumn": 14,
294+
"text": "QUERY TEXT",
295+
},
296+
{},
297+
],
298+
}
299+
}
300+
script_stats = job.script_statistics
301+
self.assertEqual(script_stats.evaluation_kind, "EXPRESSION")
302+
stack_frames = script_stats.stack_frames
303+
self.assertEqual(len(stack_frames), 2)
304+
stack_frame = stack_frames[0]
305+
self.assertEqual(stack_frame.procedure_id, "some-procedure")
306+
self.assertEqual(stack_frame.start_line, 5)
307+
self.assertEqual(stack_frame.start_column, 29)
308+
self.assertEqual(stack_frame.end_line, 9)
309+
self.assertEqual(stack_frame.end_column, 14)
310+
self.assertEqual(stack_frame.text, "QUERY TEXT")
311+
stack_frame = stack_frames[1]
312+
self.assertIsNone(stack_frame.procedure_id)
313+
self.assertIsNone(stack_frame.start_line)
314+
self.assertIsNone(stack_frame.start_column)
315+
self.assertIsNone(stack_frame.end_line)
316+
self.assertIsNone(stack_frame.end_column)
317+
self.assertIsNone(stack_frame.text)
318+
279319
def test_num_child_jobs(self):
280320
client = _make_client(project=self.PROJECT)
281321
job = self._make_one(self.JOB_ID, client)

0 commit comments

Comments
 (0)