@@ -110,6 +110,24 @@ def _verify_table_definitions(self, job, config):
110110self .assertIsNotNone (expected_ec )
111111self .assertEqual (found_ec .to_api_repr (),expected_ec )
112112
113+ def _verify_dml_stats_resource_properties (self ,job ,resource ):
114+ query_stats = resource .get ("statistics" , {}).get ("query" , {})
115+
116+ if "dmlStats" in query_stats :
117+ resource_dml_stats = query_stats ["dmlStats" ]
118+ job_dml_stats = job .dml_stats
119+ assert str (job_dml_stats .inserted_row_count )== resource_dml_stats .get (
120+ "insertedRowCount" ,"0"
121+ )
122+ assert str (job_dml_stats .updated_row_count )== resource_dml_stats .get (
123+ "updatedRowCount" ,"0"
124+ )
125+ assert str (job_dml_stats .deleted_row_count )== resource_dml_stats .get (
126+ "deletedRowCount" ,"0"
127+ )
128+ else :
129+ assert job .dml_stats is None
130+
113131def _verify_configuration_properties (self ,job ,configuration ):
114132if "dryRun" in configuration :
115133self .assertEqual (job .dry_run ,configuration ["dryRun" ])
@@ -118,6 +136,7 @@ def _verify_configuration_properties(self, job, configuration):
118136
119137def _verifyResourceProperties (self ,job ,resource ):
120138self ._verifyReadonlyResourceProperties (job ,resource )
139+ self ._verify_dml_stats_resource_properties (job ,resource )
121140
122141configuration = resource .get ("configuration" , {})
123142self ._verify_configuration_properties (job ,configuration )
@@ -130,16 +149,19 @@ def _verifyResourceProperties(self, job, resource):
130149self ._verify_table_definitions (job ,query_config )
131150
132151self .assertEqual (job .query ,query_config ["query" ])
152+
133153if "createDisposition" in query_config :
134154self .assertEqual (job .create_disposition ,query_config ["createDisposition" ])
135155else :
136156self .assertIsNone (job .create_disposition )
157+
137158if "defaultDataset" in query_config :
138159ds_ref = job .default_dataset
139160ds_ref = {"projectId" :ds_ref .project ,"datasetId" :ds_ref .dataset_id }
140161self .assertEqual (ds_ref ,query_config ["defaultDataset" ])
141162else :
142163self .assertIsNone (job .default_dataset )
164+
143165if "destinationTable" in query_config :
144166table = job .destination
145167tb_ref = {
@@ -150,14 +172,17 @@ def _verifyResourceProperties(self, job, resource):
150172self .assertEqual (tb_ref ,query_config ["destinationTable" ])
151173else :
152174self .assertIsNone (job .destination )
175+
153176if "priority" in query_config :
154177self .assertEqual (job .priority ,query_config ["priority" ])
155178else :
156179self .assertIsNone (job .priority )
180+
157181if "writeDisposition" in query_config :
158182self .assertEqual (job .write_disposition ,query_config ["writeDisposition" ])
159183else :
160184self .assertIsNone (job .write_disposition )
185+
161186if "destinationEncryptionConfiguration" in query_config :
162187self .assertIsNotNone (job .destination_encryption_configuration )
163188self .assertEqual (
@@ -166,6 +191,7 @@ def _verifyResourceProperties(self, job, resource):
166191 )
167192else :
168193self .assertIsNone (job .destination_encryption_configuration )
194+
169195if "schemaUpdateOptions" in query_config :
170196self .assertEqual (
171197job .schema_update_options ,query_config ["schemaUpdateOptions" ]
@@ -190,6 +216,7 @@ def test_ctor_defaults(self):
190216self .assertIsNone (job .create_disposition )
191217self .assertIsNone (job .default_dataset )
192218self .assertIsNone (job .destination )
219+ self .assertIsNone (job .dml_stats )
193220self .assertIsNone (job .flatten_results )
194221self .assertIsNone (job .priority )
195222self .assertIsNone (job .use_query_cache )
@@ -278,6 +305,26 @@ def test_from_api_repr_with_encryption(self):
278305self .assertIs (job ._client ,client )
279306self ._verifyResourceProperties (job ,RESOURCE )
280307
308+ def test_from_api_repr_with_dml_stats (self ):
309+ self ._setUpConstants ()
310+ client = _make_client (project = self .PROJECT )
311+ RESOURCE = {
312+ "id" :self .JOB_ID ,
313+ "jobReference" : {"projectId" :self .PROJECT ,"jobId" :self .JOB_ID },
314+ "configuration" : {"query" : {"query" :self .QUERY }},
315+ "statistics" : {
316+ "query" : {
317+ "dmlStats" : {"insertedRowCount" :"15" ,"updatedRowCount" :"2" },
318+ },
319+ },
320+ }
321+ klass = self ._get_target_class ()
322+
323+ job = klass .from_api_repr (RESOURCE ,client = client )
324+
325+ self .assertIs (job ._client ,client )
326+ self ._verifyResourceProperties (job ,RESOURCE )
327+
281328def test_from_api_repr_w_properties (self ):
282329from google .cloud .bigquery .job import CreateDisposition
283330from google .cloud .bigquery .job import SchemaUpdateOption
@@ -815,6 +862,23 @@ def test_estimated_bytes_processed(self):
815862query_stats ["estimatedBytesProcessed" ]= str (est_bytes )
816863self .assertEqual (job .estimated_bytes_processed ,est_bytes )
817864
865+ def test_dml_stats (self ):
866+ from google .cloud .bigquery .job .query import DmlStats
867+
868+ client = _make_client (project = self .PROJECT )
869+ job = self ._make_one (self .JOB_ID ,self .QUERY ,client )
870+ assert job .dml_stats is None
871+
872+ statistics = job ._properties ["statistics" ]= {}
873+ assert job .dml_stats is None
874+
875+ query_stats = statistics ["query" ]= {}
876+ assert job .dml_stats is None
877+
878+ query_stats ["dmlStats" ]= {"insertedRowCount" :"35" }
879+ assert isinstance (job .dml_stats ,DmlStats )
880+ assert job .dml_stats .inserted_row_count == 35
881+
818882def test_result (self ):
819883from google .cloud .bigquery .table import RowIterator
820884