Repository: ibis-project/ibis

Issue:
bug: Bigquery backend Table init does not respect the fully qualified table name input
### What happened?

In the case to initialize a Bigquery backend table object with a fully qualified name, e.g. `project.dataset.table_name`, the connection object will append its default project and dataset. This cause the wrong table name used in the executed SQL stmt.

The expected behavior should be that the backend logic check the input name and append the default proj / dataset if it is not a fully qualified one

The issue should be introduced via this commit: https://github.com/ibis-project/ibis/commit/5b009d2e2775f263628af12723cce6714cad6d22

### What version of ibis are you using?

6.0.0

### What backend(s) are you using, if any?

Bigquery

### Relevant log output

```sh
File "/usr/local/lib/python3.10/site-packages/ibis/backends/bigquery/__init__.py", line 480, in create_table
    self.raw_sql(f'CREATE TABLE {table_ref} AS ({sql_select})')
  File "/usr/local/lib/python3.10/site-packages/ibis/backends/bigquery/__init__.py", line 287, in raw_sql
    return self._execute(query, results=results, query_parameters=query_parameters)
  File "/usr/local/lib/python3.10/site-packages/ibis/backends/bigquery/__init__.py", line 271, in _execute
    query.result()  # blocks until finished
  File "/usr/local/lib/python3.10/site-packages/google/cloud/bigquery/job/query.py", line 1520, in result
    do_get_result()
  File "/usr/local/lib/python3.10/site-packages/google/api_core/retry.py", line 349, in retry_wrapped_func
    return retry_target(
  File "/usr/local/lib/python3.10/site-packages/google/api_core/retry.py", line 191, in retry_target
    return target()
  File "/usr/local/lib/python3.10/site-packages/google/cloud/bigquery/job/query.py", line 1510, in do_get_result
    super(QueryJob, self).result(retry=retry, timeout=timeout)
  File "/usr/local/lib/python3.10/site-packages/google/cloud/bigquery/job/base.py", line 922, in result
    return super(_AsyncJob, self).result(timeout=timeout, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/google/api_core/future/polling.py", line 261, in result
    raise self._exception
google.api_core.exceptions.BadRequest: 400 Invalid project ID 'gcp-project-default.bq_dataset_default.gcp-correct-project'. Project IDs must contain 6-63 lowercase letters, digits, or dashes. Some project IDs also include domai
n name separated by a colon. IDs must start with a letter and may not end with a dash.
```


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct
bug: Bigquery backend Table init does not respect the fully qualified table name input
### What happened?

In the case to initialize a Bigquery backend table object with a fully qualified name, e.g. `project.dataset.table_name`, the connection object will append its default project and dataset. This cause the wrong table name used in the executed SQL stmt.

The expected behavior should be that the backend logic check the input name and append the default proj / dataset if it is not a fully qualified one

The issue should be introduced via this commit: https://github.com/ibis-project/ibis/commit/5b009d2e2775f263628af12723cce6714cad6d22

### What version of ibis are you using?

6.0.0

### What backend(s) are you using, if any?

Bigquery

### Relevant log output

```sh
File "/usr/local/lib/python3.10/site-packages/ibis/backends/bigquery/__init__.py", line 480, in create_table
    self.raw_sql(f'CREATE TABLE {table_ref} AS ({sql_select})')
  File "/usr/local/lib/python3.10/site-packages/ibis/backends/bigquery/__init__.py", line 287, in raw_sql
    return self._execute(query, results=results, query_parameters=query_parameters)
  File "/usr/local/lib/python3.10/site-packages/ibis/backends/bigquery/__init__.py", line 271, in _execute
    query.result()  # blocks until finished
  File "/usr/local/lib/python3.10/site-packages/google/cloud/bigquery/job/query.py", line 1520, in result
    do_get_result()
  File "/usr/local/lib/python3.10/site-packages/google/api_core/retry.py", line 349, in retry_wrapped_func
    return retry_target(
  File "/usr/local/lib/python3.10/site-packages/google/api_core/retry.py", line 191, in retry_target
    return target()
  File "/usr/local/lib/python3.10/site-packages/google/cloud/bigquery/job/query.py", line 1510, in do_get_result
    super(QueryJob, self).result(retry=retry, timeout=timeout)
  File "/usr/local/lib/python3.10/site-packages/google/cloud/bigquery/job/base.py", line 922, in result
    return super(_AsyncJob, self).result(timeout=timeout, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/google/api_core/future/polling.py", line 261, in result
    raise self._exception
google.api_core.exceptions.BadRequest: 400 Invalid project ID 'gcp-project-default.bq_dataset_default.gcp-correct-project'. Project IDs must contain 6-63 lowercase letters, digits, or dashes. Some project IDs also include domai
n name separated by a colon. IDs must start with a letter and may not end with a dash.
```


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion:

Resolving patch:
diff --git a/ibis/backends/bigquery/__init__.py b/ibis/backends/bigquery/__init__.py
--- a/ibis/backends/bigquery/__init__.py
+++ b/ibis/backends/bigquery/__init__.py
@@ -234,8 +234,8 @@ def dataset_id(self):
     def table(self, name: str, database: str | None = None) -> ir.TableExpr:
         if database is None:
             database = f"{self.data_project}.{self.current_database}"
-        t = super().table(name, database=database)
         table_id = self._fully_qualified_name(name, database)
+        t = super().table(table_id)
         bq_table = self.client.get_table(table_id)
         return rename_partitioned_column(t, bq_table, self.partition_column)
 


Test patch:
diff --git a/ibis/backends/bigquery/tests/system/snapshots/test_client/test_cross_project_query/out.sql b/ibis/backends/bigquery/tests/system/snapshots/test_client/test_cross_project_query/out.sql
--- a/ibis/backends/bigquery/tests/system/snapshots/test_client/test_cross_project_query/out.sql
+++ b/ibis/backends/bigquery/tests/system/snapshots/test_client/test_cross_project_query/out.sql
@@ -1,6 +1,6 @@
 SELECT t0.`title`, t0.`tags`
 FROM (
   SELECT t1.*
-  FROM `bigquery-public-data.stackoverflow`.posts_questions t1
+  FROM `bigquery-public-data.stackoverflow.posts_questions` t1
   WHERE STRPOS(t1.`tags`, 'ibis') - 1 >= 0
 ) t0
\ No newline at end of file
diff --git a/ibis/backends/bigquery/tests/system/snapshots/test_client/test_subquery_scalar_params/out.sql b/ibis/backends/bigquery/tests/system/snapshots/test_client/test_subquery_scalar_params/out.sql
--- a/ibis/backends/bigquery/tests/system/snapshots/test_client/test_subquery_scalar_params/out.sql
+++ b/ibis/backends/bigquery/tests/system/snapshots/test_client/test_subquery_scalar_params/out.sql
@@ -1,6 +1,6 @@
 WITH t0 AS (
   SELECT t2.`float_col`, t2.`timestamp_col`, t2.`int_col`, t2.`string_col`
-  FROM `ibis-gbq.ibis_gbq_testing`.functional_alltypes t2
+  FROM `ibis-gbq.ibis_gbq_testing.functional_alltypes` t2
   WHERE t2.`timestamp_col` < @param_0
 )
 SELECT count(t1.`foo`) AS `count`
diff --git a/ibis/backends/bigquery/tests/system/test_client.py b/ibis/backends/bigquery/tests/system/test_client.py
--- a/ibis/backends/bigquery/tests/system/test_client.py
+++ b/ibis/backends/bigquery/tests/system/test_client.py
@@ -254,8 +254,8 @@ def test_multiple_project_queries(con):
     result = join.compile()
     expected = """\
 SELECT t0.`title`
-FROM `bigquery-public-data.stackoverflow`.posts_questions t0
-  INNER JOIN `nyc-tlc.yellow`.trips t1
+FROM `bigquery-public-data.stackoverflow.posts_questions` t0
+  INNER JOIN `nyc-tlc.yellow.trips` t1
     ON t0.`tags` = t1.`rate_code`"""
     assert result == expected
 
@@ -270,8 +270,8 @@ def test_multiple_project_queries_database_api(con):
     result = join.compile()
     expected = """\
 SELECT t0.`title`
-FROM `bigquery-public-data.stackoverflow`.posts_questions t0
-  INNER JOIN `nyc-tlc.yellow`.trips t1
+FROM `bigquery-public-data.stackoverflow.posts_questions` t0
+  INNER JOIN `nyc-tlc.yellow.trips` t1
     ON t0.`tags` = t1.`rate_code`"""
     assert result == expected
 
@@ -387,3 +387,9 @@ def test_timestamp_table(con, temp_table):
             ("timestamp_col", dt.Timestamp(timezone="UTC")),
         ]
     )
+
+
+def test_fully_qualified_table_creation(con, project_id, dataset_id, temp_table):
+    schema = ibis.schema({'col1': dt.GeoSpatial(geotype="geography", srid=4326)})
+    t = con.create_table(f"{project_id}.{dataset_id}.{temp_table}", schema=schema)
+    assert t.get_name() == f"{project_id}.{dataset_id}.{temp_table}"