Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for Standard SQL views #2898

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions bigquery/google/cloud/bigquery/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,43 @@ def view_query(self, value):
"""
if not isinstance(value, six.string_types):
raise ValueError("Pass a string")
self._properties['view'] = {'query': value}
if 'view' in self._properties:
self._properties['view']['query'] = value
else:
self._properties['view'] = {
'query': value,
'useLegacySql': True
}

@property
def view_use_legacy_sql(self):
"""Boolean value definin SQL dialect of a view.

:rtype: bool, or ``NoneType``
:returns: boolean indicating whether the view uses standard
or legacy SQL
"""
view = self._properties.get('view')
if view is not None:
return view.get('useLegacySql', True)

@view_use_legacy_sql.setter
def view_use_legacy_sql(self, value):
"""Update SQL dialect for view

:type value: bool
:param value: Whether to use legacy SQL

:raises: ValueError for invalid value types.
"""
if not isinstance(value, bool):
raise ValueError("Pass a boolean value")
if 'view' in self._properties:
self._properties['view']['useLegacySql'] = value
else:
self._properties['view'] = {
'useLegacySql': value
}

@view_query.deleter
def view_query(self):
Expand Down Expand Up @@ -469,6 +505,8 @@ def _build_resource(self):
if self.view_query is not None:
view = resource['view'] = {}
view['query'] = self.view_query
if self.view_use_legacy_sql is not None:
view['useLegacySql'] = self.view_use_legacy_sql
elif self._schema:
resource['schema'] = {
'fields': _build_schema_resource(self._schema)
Expand Down Expand Up @@ -544,6 +582,7 @@ def patch(self,
location=_MARKER,
expires=_MARKER,
view_query=_MARKER,
view_use_legacy_sql=_MARKER,
schema=_MARKER):
"""API call: update individual table properties via a PATCH request

Expand Down Expand Up @@ -571,6 +610,9 @@ def patch(self,
:type view_query: str
:param view_query: SQL query defining the table as a view

:type view_use_legacy_sql: str
:param view_use_legacy_sql: Boolean indicating view_query dialect

:type schema: list of :class:`SchemaField`
:param schema: fields describing the schema

Expand Down Expand Up @@ -599,7 +641,16 @@ def patch(self,
if view_query is None:
partial['view'] = None
else:
partial['view'] = {'query': view_query}
partial['view'] = {
'query': view_query,
'useLegacySql': True
}

if view_use_legacy_sql is not _MARKER:
if 'view' in partial:
partial['view']['useLegacySql'] = view_use_legacy_sql
else:
partial['view'] = {'useLegacySql': view_use_legacy_sql}

if schema is not _MARKER:
if schema is None:
Expand Down
34 changes: 29 additions & 5 deletions bigquery/unit_tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ def _verifyResourceProperties(self, table, resource):

if 'view' in resource:
self.assertEqual(table.view_query, resource['view']['query'])
self.assertEqual(table.view_use_legacy_sql, resource['view']['useLegacySql'])
else:
self.assertIsNone(table.view_query)
self.assertIsNone(table.view_use_legacy_sql)

if 'schema' in resource:
self._verifySchema(table.schema, resource)
Expand Down Expand Up @@ -159,6 +161,7 @@ def test_ctor(self):
self.assertIsNone(table.friendly_name)
self.assertIsNone(table.location)
self.assertIsNone(table.view_query)
self.assertIsNone(table.view_use_legacy_sql)

def test_ctor_w_schema(self):
from google.cloud.bigquery.table import SchemaField
Expand Down Expand Up @@ -354,6 +357,20 @@ def test_view_query_deleter(self):
del table.view_query
self.assertIsNone(table.view_query)

def test_view_use_legacy_sql_setter_bad_value(self):
client = _Client(self.PROJECT)
dataset = _Dataset(client)
table = self._make_one(self.TABLE_NAME, dataset)
with self.assertRaises(ValueError):
table.use_legacy_sql = 12345

def test_view_use_legacy_sql_setter(self):
client = _Client(self.PROJECT)
dataset = _Dataset(client)
table = self._make_one(self.TABLE_NAME, dataset)
table.use_legacy_sql = False
self.assertFalse(table.use_legacy_sql)

def test_from_api_repr_missing_identity(self):
self._setUpConstants()
client = _Client(self.PROJECT)
Expand Down Expand Up @@ -637,6 +654,7 @@ def test_create_w_alternate_client(self):
DESCRIPTION = 'DESCRIPTION'
TITLE = 'TITLE'
QUERY = 'select fullname, age from person_ages'
LEGACY_SQL = False
RESOURCE = self._makeResource()
RESOURCE['description'] = DESCRIPTION
RESOURCE['friendlyName'] = TITLE
Expand All @@ -645,6 +663,7 @@ def test_create_w_alternate_client(self):
RESOURCE['expirationTime'] = _millis(self.EXP_TIME)
RESOURCE['view'] = {}
RESOURCE['view']['query'] = QUERY
RESOURCE['view']['useLegacySql'] = LEGACY_SQL
RESOURCE['type'] = 'VIEW'
conn1 = _Connection()
client1 = _Client(project=self.PROJECT, connection=conn1)
Expand Down Expand Up @@ -673,7 +692,10 @@ def test_create_w_alternate_client(self):
'tableId': self.TABLE_NAME},
'description': DESCRIPTION,
'friendlyName': TITLE,
'view': {'query': QUERY},
'view': {
'query': QUERY,
'useLegacySql': LEGACY_SQL
},
}
self.assertEqual(req['data'], SENT)
self._verifyResourceProperties(table, RESOURCE)
Expand Down Expand Up @@ -833,9 +855,10 @@ def test_patch_w_alternate_client(self):
PATH = 'projects/%s/datasets/%s/tables/%s' % (
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
QUERY = 'select fullname, age from person_ages'
LEGACY_SQL = True
LOCATION = 'EU'
RESOURCE = self._makeResource()
RESOURCE['view'] = {'query': QUERY}
RESOURCE['view'] = {'query': QUERY, 'useLegacySql': LEGACY_SQL}
RESOURCE['type'] = 'VIEW'
RESOURCE['location'] = LOCATION
self.EXP_TIME = datetime.datetime(2015, 8, 1, 23, 59, 59,
Expand All @@ -859,7 +882,7 @@ def test_patch_w_alternate_client(self):
self.assertEqual(req['method'], 'PATCH')
self.assertEqual(req['path'], '/%s' % PATH)
SENT = {
'view': {'query': QUERY},
'view': {'query': QUERY, 'useLegacySql': LEGACY_SQL},
'location': LOCATION,
'expirationTime': _millis(self.EXP_TIME),
'schema': {'fields': [
Expand Down Expand Up @@ -943,13 +966,14 @@ def test_update_w_alternate_client(self):
DEF_TABLE_EXP = 12345
LOCATION = 'EU'
QUERY = 'select fullname, age from person_ages'
LEGACY_SQL = True
RESOURCE = self._makeResource()
RESOURCE['defaultTableExpirationMs'] = 12345
RESOURCE['location'] = LOCATION
self.EXP_TIME = datetime.datetime(2015, 8, 1, 23, 59, 59,
tzinfo=UTC)
RESOURCE['expirationTime'] = _millis(self.EXP_TIME)
RESOURCE['view'] = {'query': QUERY}
RESOURCE['view'] = {'query': QUERY, 'useLegacySql': LEGACY_SQL}
RESOURCE['type'] = 'VIEW'
conn1 = _Connection()
client1 = _Client(project=self.PROJECT, connection=conn1)
Expand All @@ -976,7 +1000,7 @@ def test_update_w_alternate_client(self):
'tableId': self.TABLE_NAME},
'expirationTime': _millis(self.EXP_TIME),
'location': 'EU',
'view': {'query': QUERY},
'view': {'query': QUERY, 'useLegacySql': LEGACY_SQL},
}
self.assertEqual(req['data'], SENT)
self._verifyResourceProperties(table, RESOURCE)
Expand Down