Skip to content

Commit d214bb0

Browse files
authored
Merge pull request googleapis#16 from tswast/patch-1
Add support for STRUCT and ARRAY types.
2 parents b895b67 + e5a07b9 commit d214bb0

File tree

5 files changed

+47
-14
lines changed

5 files changed

+47
-14
lines changed

README.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,3 @@ Set up an environment and run tests::
7070
source .env/bin/activate
7171
pip install -r dev_requirements.txt
7272
pytest
73-
74-
TODO
75-
====
76-
77-
- Support for Record column type

pybigquery/sqlalchemy_bigquery.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,8 @@ def format_label(self, *args, **kwargs):
5252
'DATETIME': types.DATETIME,
5353
'DATE': types.DATE,
5454
'BYTES': types.BINARY,
55-
'TIME': types.TIME
56-
# TODO
57-
# 'RECORD'
55+
'TIME': types.TIME,
56+
'RECORD': types.JSON,
5857
}
5958

6059

@@ -116,6 +115,14 @@ def create_connect_args(self, url):
116115
client = bigquery.Client(url.host)
117116
return ([client], {})
118117

118+
def _json_deserializer(self, row):
119+
"""JSON deserializer for RECORD types.
120+
121+
The DB-API layer already deserializes JSON to a dictionary, so this
122+
just returns the input.
123+
"""
124+
return row
125+
119126
def _split_table_name(self, full_table_name):
120127
# Split full_table_name to get project, dataset and table name
121128
dataset = None
@@ -165,8 +172,8 @@ def get_columns(self, connection, table_name, schema=None, **kw):
165172

166173
result.append({
167174
'name': col.name,
168-
'type': coltype,
169-
'nullable': True if col.mode == 'NULLABLE' else False,
175+
'type': types.ARRAY(coltype) if col.mode == 'REPEATED' else coltype,
176+
'nullable': col.mode == 'NULLABLE' or col.mode == 'REPEATED',
170177
'default': None,
171178
})
172179

scripts/sample_one_row.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"integer":"588","timestamp":"1381404436","string":"W 52 St & 11 Ave","float":"40.76727216","boolean":"false","date":"2013-10-10","datetime":"2013-10-10T11:27:16","time":"11:27:16","bytes":"7w=="}
1+
{"integer":"588","timestamp":"1381404436","string":"W 52 St & 11 Ave","float":"40.76727216","boolean":"false","date":"2013-10-10","datetime":"2013-10-10T11:27:16","time":"11:27:16","bytes":"7w==","record":{"name": "John Doe","age":"100"},"array":[1,2,3]}

scripts/schema.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,26 @@
4242
"mode": "NULLABLE",
4343
"name": "bytes",
4444
"type": "BYTES"
45+
},
46+
{
47+
"mode": "NULLABLE",
48+
"name": "record",
49+
"type": "RECORD",
50+
"fields": [
51+
{
52+
"mode": "NULLABLE",
53+
"name": "name",
54+
"type": "STRING"
55+
},
56+
{
57+
"mode": "NULLABLE",
58+
"name": "age",
59+
"type": "INTEGER"
60+
}
61+
]
62+
},
63+
{
64+
"mode": "REPEATED",
65+
"name": "array",
66+
"type": "INTEGER"
4567
}]

test/test_sqlalchemy_bigquery.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@
2222
datetime.date(2013, 10, 10),
2323
datetime.datetime(2013, 10, 10, 11, 27, 16),
2424
datetime.time(11, 27, 16),
25-
b'\xef'
25+
b'\xef',
26+
{
27+
'name': 'John Doe',
28+
'age': 100,
29+
},
30+
[1, 2, 3],
2631
]
2732

2833

@@ -47,7 +52,9 @@
4752
{'name': 'date', 'type': types.DATE(), 'nullable': True, 'default': None},
4853
{'name': 'datetime', 'type': types.DATETIME(), 'nullable': True, 'default': None},
4954
{'name': 'time', 'type': types.TIME(), 'nullable': True, 'default': None},
50-
{'name': 'bytes', 'type': types.BINARY(), 'nullable': True, 'default': None}
55+
{'name': 'bytes', 'type': types.BINARY(), 'nullable': True, 'default': None},
56+
{'name': 'record', 'type': types.JSON(), 'nullable': True, 'default': None},
57+
{'name': 'array', 'type': types.ARRAY(types.Integer()), 'nullable': True, 'default': None},
5158
]
5259

5360

@@ -101,7 +108,7 @@ def query(table):
101108

102109

103110
def test_reflect_select(engine, table):
104-
assert len(table.c) == 9
111+
assert len(table.c) == 11
105112
assert isinstance(table.c.integer, Column)
106113
assert isinstance(table.c.integer.type, types.Integer)
107114
assert isinstance(table.c.timestamp.type, types.TIMESTAMP)
@@ -112,6 +119,8 @@ def test_reflect_select(engine, table):
112119
assert isinstance(table.c.datetime.type, types.DATETIME)
113120
assert isinstance(table.c.time.type, types.TIME)
114121
assert isinstance(table.c.bytes.type, types.BINARY)
122+
assert isinstance(table.c.record.type, types.JSON)
123+
assert isinstance(table.c.array.type, types.ARRAY)
115124

116125
rows = table.select().execute().fetchall()
117126
assert len(rows) == 1000

0 commit comments

Comments
 (0)