Repository: googleapis/python-bigquery

Issue:
How to get period separated table id from Table Object
I am attempting to build a query using the table id in a `bigquery.Table` object:

	def do_query(client: bigquery.client, table: bigquery.Table):

		statement = f"""
			SELECT *
			FROM {table.full_table_id}
		"""
		query_job = client.query(statement, job_config=job_config)
		result = list(query_job.result())
		...

Unfortunately, this doesn't work.  The output of `table.full_table_id` is: `myProject:myDataSet.myTable` but queries need to be formatted with period separated values, aka: `myProject.myDataSet.myTable`.  

**Question:** How should I be constructing queries from a `bigquery.Table` object?  

Right now the work around I am using is this:


	statement = f"""
        SELECT *
        FROM {table.project}.{table.dataset_id}.{table.table_id}
	"""

But this feels icky.  

To search find this issue, this is the exception I was getting:


	Traceback (most recent call last):
	  ...
	  File "me.py", line 74, in me
		result = list(query_job.result())
	  File "/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/job.py", line 3230, in result
		super(QueryJob, self).result(retry=retry, timeout=timeout)
	  File "/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/job.py", line 835, in result
		return super(_AsyncJob, self).result(timeout=timeout)
	  File "/usr/local/lib/python3.7/site-packages/google/api_core/future/polling.py", line 130, in result
		raise self._exception
	google.api_core.exceptions.BadRequest: 400 Syntax error: Unexpected ":" at [3:30]

I can see from issue #115 that this is the intended behaviour of `full_table_id`, but I am still curious if there is a cleaner solution to what I am doing.  




Discussion:
We can probably add a `standard_table_id` property that does this format operation for you. Marking this as a feature request.
Note: If you're doing `SELECT *`, you can call [client.list_rows(table)](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.client.Client.html#google.cloud.bigquery.client.Client.list_rows) directly, which will save on time and query costs.

Resolving patch:
diff --git a/google/cloud/bigquery/table.py b/google/cloud/bigquery/table.py
--- a/google/cloud/bigquery/table.py
+++ b/google/cloud/bigquery/table.py
@@ -262,6 +262,9 @@ def __ne__(self, other):
     def __hash__(self):
         return hash(self._key())
 
+    def __str__(self):
+        return f"{self.project}.{self.dataset_id}.{self.table_id}"
+
     def __repr__(self):
         from google.cloud.bigquery.dataset import DatasetReference
 
@@ -475,7 +478,7 @@ def full_table_id(self):
         """Union[str, None]: ID for the table (:data:`None` until set from the
         server).
 
-        In the format ``project_id:dataset_id.table_id``.
+        In the format ``project-id:dataset_id.table_id``.
         """
         return self._properties.get("id")
 
@@ -484,7 +487,8 @@ def table_type(self):
         """Union[str, None]: The type of the table (:data:`None` until set from
         the server).
 
-        Possible values are ``'TABLE'``, ``'VIEW'``, or ``'EXTERNAL'``.
+        Possible values are ``'TABLE'``, ``'VIEW'``, ``'MATERIALIZED_VIEW'`` or
+        ``'EXTERNAL'``.
         """
         return self._properties.get("type")
 


Test patch:
diff --git a/tests/unit/test_table.py b/tests/unit/test_table.py
--- a/tests/unit/test_table.py
+++ b/tests/unit/test_table.py
@@ -272,6 +272,11 @@ def test___repr__(self):
         )
         self.assertEqual(repr(table1), expected)
 
+    def test___str__(self):
+        dataset = DatasetReference("project1", "dataset1")
+        table1 = self._make_one(dataset, "table1")
+        self.assertEqual(str(table1), "project1.dataset1.table1")
+
 
 class TestTable(unittest.TestCase, _SchemaBase):
 
@@ -813,6 +818,9 @@ def test_from_string(self):
         self.assertEqual(got.project, "string-project")
         self.assertEqual(got.dataset_id, "string_dataset")
         self.assertEqual(got.table_id, "string_table")
+        self.assertEqual(
+            str(got.reference), "string-project.string_dataset.string_table"
+        )
 
     def test_from_string_legacy_string(self):
         cls = self._get_target_class()