Skip to content

Commit 55df6a2

Browse files
authored
BigQuery: Add Client.insert_rows, deprecate Client.create_rows (#4657)
* BigQuery: Add Client.insert_rows, deprecate Client.create_rows `insert_rows` aligns better with API request (Tabledata.insertAll). Feedback from BQ GA review.
1 parent f4e029a commit 55df6a2

File tree

5 files changed

+81
-40
lines changed

5 files changed

+81
-40
lines changed

bigquery/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
## Interface changes / breaking changes
2626

27+
- Add `Client.insert_rows()` and `Client.insert_rows_json()`, deprecate
28+
`Client.create_rows()` and `Client.create_rows_json()`.
29+
([#4657](https://github.com/GoogleCloudPlatform/google-cloud-python/pull/4657))
2730
- Add `Client.list_tables`, deprecate `Client.list_dataset_tables`.
2831
([#4653](https://github.com/GoogleCloudPlatform/google-cloud-python/pull/4653))
2932
- `Client.list_tables` returns an iterators of `TableListItem`. The API

bigquery/google/cloud/bigquery/client.py

+26-5
Original file line numberDiff line numberDiff line change
@@ -1007,8 +1007,8 @@ def query(self, query, job_config=None, job_id=None, job_id_prefix=None,
10071007
job._begin(retry=retry)
10081008
return job
10091009

1010-
def create_rows(self, table, rows, selected_fields=None, **kwargs):
1011-
"""API call: insert table data via a POST request
1010+
def insert_rows(self, table, rows, selected_fields=None, **kwargs):
1011+
"""Insert rows into a table via the streaming API.
10121012
10131013
See
10141014
https://cloud.google.com/bigquery/docs/reference/rest/v2/tabledata/insertAll
@@ -1073,12 +1073,12 @@ def create_rows(self, table, rows, selected_fields=None, **kwargs):
10731073

10741074
json_rows.append(json_row)
10751075

1076-
return self.create_rows_json(table, json_rows, **kwargs)
1076+
return self.insert_rows_json(table, json_rows, **kwargs)
10771077

1078-
def create_rows_json(self, table, json_rows, row_ids=None,
1078+
def insert_rows_json(self, table, json_rows, row_ids=None,
10791079
skip_invalid_rows=None, ignore_unknown_values=None,
10801080
template_suffix=None, retry=DEFAULT_RETRY):
1081-
"""API call: insert table data via a POST request
1081+
"""Insert rows into a table without applying local type conversions.
10821082
10831083
See
10841084
https://cloud.google.com/bigquery/docs/reference/rest/v2/tabledata/insertAll
@@ -1162,6 +1162,27 @@ def create_rows_json(self, table, json_rows, row_ids=None,
11621162

11631163
return errors
11641164

1165+
def create_rows(self, *args, **kwargs):
1166+
"""DEPRECATED: Insert rows into a table via the streaming API.
1167+
1168+
Use :func:`~google.cloud.bigquery.client.Client.insert_rows` instead.
1169+
"""
1170+
warnings.warn(
1171+
'create_rows is deprecated, use insert_rows instead.',
1172+
DeprecationWarning)
1173+
return self.insert_rows(*args, **kwargs)
1174+
1175+
def create_rows_json(self, *args, **kwargs):
1176+
"""DEPRECATED: Insert rows into a table without type conversions.
1177+
1178+
Use :func:`~google.cloud.bigquery.client.Client.insert_rows_json`
1179+
instead.
1180+
"""
1181+
warnings.warn(
1182+
'create_rows_json is deprecated, use insert_rows_json instead.',
1183+
DeprecationWarning)
1184+
return self.insert_rows_json(*args, **kwargs)
1185+
11651186
def list_rows(self, table, selected_fields=None, max_results=None,
11661187
page_token=None, start_index=None, retry=DEFAULT_RETRY):
11671188
"""List the rows of the table.

bigquery/tests/system.py

+24-19
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ def _still_in_use(bad_request):
124124
for doomed in self.to_delete:
125125
if isinstance(doomed, Bucket):
126126
retry_409(doomed.delete)(force=True)
127-
elif isinstance(doomed, Dataset):
127+
elif isinstance(doomed, (Dataset, bigquery.DatasetReference)):
128128
retry_in_use(Config.CLIENT.delete_dataset)(doomed)
129-
elif isinstance(doomed, Table):
129+
elif isinstance(doomed, (Table, bigquery.TableReference)):
130130
retry_in_use(Config.CLIENT.delete_table)(doomed)
131131
else:
132132
doomed.delete()
@@ -327,7 +327,7 @@ def _fetch_single_page(table, selected_fields=None):
327327
page = six.next(iterator.pages)
328328
return list(page)
329329

330-
def test_create_rows_then_dump_table(self):
330+
def test_insert_rows_then_dump_table(self):
331331
NOW_SECONDS = 1448911495.484366
332332
NOW = datetime.datetime.utcfromtimestamp(
333333
NOW_SECONDS).replace(tzinfo=UTC)
@@ -339,7 +339,7 @@ def test_create_rows_then_dump_table(self):
339339
]
340340
ROW_IDS = range(len(ROWS))
341341

342-
dataset = self.temp_dataset(_make_dataset_id('create_rows_then_dump'))
342+
dataset = self.temp_dataset(_make_dataset_id('insert_rows_then_dump'))
343343
TABLE_ID = 'test_table'
344344
schema = [
345345
bigquery.SchemaField('full_name', 'STRING', mode='REQUIRED'),
@@ -352,7 +352,7 @@ def test_create_rows_then_dump_table(self):
352352
self.to_delete.insert(0, table)
353353
self.assertTrue(_table_exists(table))
354354

355-
errors = Config.CLIENT.create_rows(table, ROWS, row_ids=ROW_IDS)
355+
errors = Config.CLIENT.insert_rows(table, ROWS, row_ids=ROW_IDS)
356356
self.assertEqual(len(errors), 0)
357357

358358
rows = ()
@@ -1315,7 +1315,7 @@ def test_query_external_table(self):
13151315
self.assertEqual(sorted(row_tuples, key=by_age),
13161316
sorted(ROWS, key=by_age))
13171317

1318-
def test_create_rows_nested_nested(self):
1318+
def test_insert_rows_nested_nested(self):
13191319
# See #2951
13201320
SF = bigquery.SchemaField
13211321
schema = [
@@ -1342,14 +1342,14 @@ def test_create_rows_nested_nested(self):
13421342
table = retry_403(Config.CLIENT.create_table)(table_arg)
13431343
self.to_delete.insert(0, table)
13441344

1345-
Config.CLIENT.create_rows(table, to_insert)
1345+
Config.CLIENT.insert_rows(table, to_insert)
13461346

13471347
retry = RetryResult(_has_rows, max_tries=8)
13481348
rows = retry(self._fetch_single_page)(table)
13491349
row_tuples = [r.values() for r in rows]
13501350
self.assertEqual(row_tuples, to_insert)
13511351

1352-
def test_create_rows_nested_nested_dictionary(self):
1352+
def test_insert_rows_nested_nested_dictionary(self):
13531353
# See #2951
13541354
SF = bigquery.SchemaField
13551355
schema = [
@@ -1376,7 +1376,7 @@ def test_create_rows_nested_nested_dictionary(self):
13761376
table = retry_403(Config.CLIENT.create_table)(table_arg)
13771377
self.to_delete.insert(0, table)
13781378

1379-
Config.CLIENT.create_rows(table, to_insert)
1379+
Config.CLIENT.insert_rows(table, to_insert)
13801380

13811381
retry = RetryResult(_has_rows, max_tries=8)
13821382
rows = retry(self._fetch_single_page)(table)
@@ -1402,7 +1402,7 @@ def test_create_table_rows_fetch_nested_schema(self):
14021402
for line in rows_file:
14031403
to_insert.append(json.loads(line))
14041404

1405-
errors = Config.CLIENT.create_rows_json(table, to_insert)
1405+
errors = Config.CLIENT.insert_rows_json(table, to_insert)
14061406
self.assertEqual(len(errors), 0)
14071407

14081408
retry = RetryResult(_has_rows, max_tries=8)
@@ -1467,19 +1467,24 @@ def test_nested_table_to_dataframe(self):
14671467
'nested_record': {'nested_nested_string': 'some deep insight'},
14681468
}
14691469
to_insert = [
1470-
('Some value', record)
1470+
{'string_col': 'Some value', 'record_col': record},
14711471
]
1472+
rows = [json.dumps(row) for row in to_insert]
1473+
body = six.StringIO('{}\n'.format('\n'.join(rows)))
14721474
table_id = 'test_table'
14731475
dataset = self.temp_dataset(_make_dataset_id('nested_df'))
1474-
table_arg = Table(dataset.table(table_id), schema=schema)
1475-
table = retry_403(Config.CLIENT.create_table)(table_arg)
1476+
table = dataset.table(table_id)
14761477
self.to_delete.insert(0, table)
1477-
Config.CLIENT.create_rows(table, to_insert)
1478-
QUERY = 'SELECT * from `{}.{}.{}`'.format(
1479-
Config.CLIENT.project, dataset.dataset_id, table_id)
1480-
1481-
retry = RetryResult(_has_rows, max_tries=8)
1482-
df = retry(self._fetch_dataframe)(QUERY)
1478+
job_config = bigquery.LoadJobConfig()
1479+
job_config.write_disposition = 'WRITE_TRUNCATE'
1480+
job_config.source_format = 'NEWLINE_DELIMITED_JSON'
1481+
job_config.schema = schema
1482+
# Load a table using a local JSON file from memory.
1483+
Config.CLIENT.load_table_from_file(
1484+
body, table, job_config=job_config).result()
1485+
1486+
df = Config.CLIENT.list_rows(
1487+
table, selected_fields=schema).to_dataframe()
14831488

14841489
self.assertIsInstance(df, pandas.DataFrame)
14851490
self.assertEqual(len(df), 1) # verify the number of rows

docs/bigquery/snippets.py

+26-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
need to be deleted during teardown.
2424
"""
2525

26+
import json
2627
import time
2728

2829
import pytest
@@ -64,9 +65,9 @@ def to_delete(client):
6465
doomed = []
6566
yield doomed
6667
for item in doomed:
67-
if isinstance(item, bigquery.Dataset):
68+
if isinstance(item, (bigquery.Dataset, bigquery.DatasetReference)):
6869
client.delete_dataset(item)
69-
elif isinstance(item, bigquery.Table):
70+
elif isinstance(item, (bigquery.Table, bigquery.TableReference)):
7071
client.delete_table(item)
7172
else:
7273
item.delete()
@@ -414,28 +415,28 @@ def test_update_table_multiple_properties(client, to_delete):
414415
# [END update_table_multiple_properties]
415416

416417

417-
def test_table_create_rows(client, to_delete):
418+
def test_table_insert_rows(client, to_delete):
418419
"""Insert / fetch table data."""
419-
DATASET_ID = 'table_create_rows_dataset_{}'.format(_millis())
420-
TABLE_ID = 'table_create_rows_table_{}'.format(_millis())
421-
dataset = bigquery.Dataset(client.dataset(DATASET_ID))
420+
dataset_id = 'table_insert_rows_dataset_{}'.format(_millis())
421+
table_id = 'table_insert_rows_table_{}'.format(_millis())
422+
dataset = bigquery.Dataset(client.dataset(dataset_id))
422423
dataset = client.create_dataset(dataset)
423424
to_delete.append(dataset)
424425

425-
table = bigquery.Table(dataset.table(TABLE_ID), schema=SCHEMA)
426+
table = bigquery.Table(dataset.table(table_id), schema=SCHEMA)
426427
table = client.create_table(table)
427428
to_delete.insert(0, table)
428429

429-
# [START table_create_rows]
430-
ROWS_TO_INSERT = [
430+
# [START table_insert_rows]
431+
rows_to_insert = [
431432
(u'Phred Phlyntstone', 32),
432433
(u'Wylma Phlyntstone', 29),
433434
]
434435

435-
errors = client.create_rows(table, ROWS_TO_INSERT) # API request
436+
errors = client.insert_rows(table, rows_to_insert) # API request
436437

437438
assert errors == []
438-
# [END table_create_rows]
439+
# [END table_insert_rows]
439440

440441

441442
def test_load_table_from_file(client, to_delete):
@@ -600,9 +601,20 @@ def test_extract_table(client, to_delete):
600601
to_delete.append(dataset)
601602

602603
table_ref = dataset.table('person_ages')
603-
table = client.create_table(bigquery.Table(table_ref, schema=SCHEMA))
604-
to_delete.insert(0, table)
605-
client.create_rows(table, ROWS)
604+
to_insert = [
605+
{'full_name': name, 'age': age}
606+
for name, age in ROWS
607+
]
608+
rows = [json.dumps(row) for row in to_insert]
609+
body = six.StringIO('{}\n'.format('\n'.join(rows)))
610+
job_config = bigquery.LoadJobConfig()
611+
job_config.write_disposition = 'WRITE_TRUNCATE'
612+
job_config.source_format = 'NEWLINE_DELIMITED_JSON'
613+
job_config.schema = SCHEMA
614+
to_delete.insert(0, table_ref)
615+
# Load a table using a local JSON file from memory.
616+
client.load_table_from_file(
617+
body, table_ref, job_config=job_config).result()
606618

607619
bucket_name = 'extract_person_ages_job_{}'.format(_millis())
608620
# [START extract_table]

docs/bigquery/usage.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ Utilize iterator properties returned with row data:
176176
Insert rows into a table's data:
177177

178178
.. literalinclude:: snippets.py
179-
:start-after: [START table_create_rows]
180-
:end-before: [END table_create_rows]
179+
:start-after: [START table_insert_rows]
180+
:end-before: [END table_insert_rows]
181181

182182
Upload table data from a file:
183183

0 commit comments

Comments
 (0)