We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Closing a connection tries to close already closed cursors
When closing a connection the library will call `.close()` on every cursor created for that connection, including closed ones. While the code works, it produces a lot of logs of error level, polluting the logs.

#### Environment details

  - OS type and version: Mac OS 10.15.7
  - Python version: Python 3.8.2
  - pip version: pip 20.3.3
  - `google-cloud-bigquery` version: 2.7.0

#### Steps to reproduce

  1. Close a cursor
  2. Close the connection
  3. Error log shows `Exception closing connection <google.cloud.bigquery.dbapi.connection.Connection object at 0x...>`

#### Code example

```python
from contextlib import closing

# using pybigquery
with closing(engine.raw_connection()) as conn:
    with closing(conn.cursor()) as cursor:
        cursor.execute(sql)
```

#### Stack trace
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 270, in _close_connection
    self._dialect.do_close(connection)
  File "/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 549, in do_close
    dbapi_connection.close()
  File "/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/dbapi/_helpers.py", line 258, in with_closed_check
    return method(self, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/dbapi/connection.py", line 79, in close
    cursor_.close()
  File "/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/dbapi/_helpers.py", line 257, in with_closed_check
    raise exc_class(exc_msg)
google.cloud.bigquery.dbapi.exceptions.ProgrammingError: Operating on a closed cursor.
```

#### Suggested fix

```python
# google/cloud/bigquery/dbapi/connection.py
class Connection(object):
    ...
    def close(self):
        ...
        for cursor_ in self._cursors_created:
            if not cursor_._closed:
                cursor_.close()
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `google/cloud/bigquery/dbapi/connection.py`
Content:
```
1 # Copyright 2017 Google LLC
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 #  Unless required by applicable law or agreed to in writing, software
10 #  distributed under the License is distributed on an "AS IS" BASIS,
11 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 #  See the License for the specific language governing permissions and
13 #  limitations under the License.
14 
15 """Connection for the Google BigQuery DB-API."""
16 
17 import weakref
18 
19 from google.cloud import bigquery
20 from google.cloud.bigquery.dbapi import cursor
21 from google.cloud.bigquery.dbapi import _helpers
22 
23 
24 @_helpers.raise_on_closed("Operating on a closed connection.")
25 class Connection(object):
26     """DB-API Connection to Google BigQuery.
27 
28     Args:
29         client (Optional[google.cloud.bigquery.Client]):
30             A REST API client used to connect to BigQuery. If not passed, a
31             client is created using default options inferred from the environment.
32         bqstorage_client(\
33             Optional[google.cloud.bigquery_storage_v1.BigQueryReadClient] \
34         ):
35             A client that uses the faster BigQuery Storage API to fetch rows from
36             BigQuery. If not passed, it is created using the same credentials
37             as ``client`` (provided that BigQuery Storage dependencies are installed).
38 
39             If both clients are available, ``bqstorage_client`` is used for
40             fetching query results.
41     """
42 
43     def __init__(self, client=None, bqstorage_client=None):
44         if client is None:
45             client = bigquery.Client()
46             self._owns_client = True
47         else:
48             self._owns_client = False
49 
50         if bqstorage_client is None:
51             # A warning is already raised by the factory if instantiation fails.
52             bqstorage_client = client._create_bqstorage_client()
53             self._owns_bqstorage_client = bqstorage_client is not None
54         else:
55             self._owns_bqstorage_client = False
56 
57         self._client = client
58         self._bqstorage_client = bqstorage_client
59 
60         self._closed = False
61         self._cursors_created = weakref.WeakSet()
62 
63     def close(self):
64         """Close the connection and any cursors created from it.
65 
66         Any BigQuery clients explicitly passed to the constructor are *not*
67         closed, only those created by the connection instance itself.
68         """
69         self._closed = True
70 
71         if self._owns_client:
72             self._client.close()
73 
74         if self._owns_bqstorage_client:
75             # There is no close() on the BQ Storage client itself.
76             self._bqstorage_client._transport.grpc_channel.close()
77 
78         for cursor_ in self._cursors_created:
79             cursor_.close()
80 
81     def commit(self):
82         """No-op, but for consistency raise an error if connection is closed."""
83 
84     def cursor(self):
85         """Return a new cursor object.
86 
87         Returns:
88             google.cloud.bigquery.dbapi.Cursor: A DB-API cursor that uses this connection.
89         """
90         new_cursor = cursor.Cursor(self)
91         self._cursors_created.add(new_cursor)
92         return new_cursor
93 
94 
95 def connect(client=None, bqstorage_client=None):
96     """Construct a DB-API connection to Google BigQuery.
97 
98     Args:
99         client (Optional[google.cloud.bigquery.Client]):
100             A REST API client used to connect to BigQuery. If not passed, a
101             client is created using default options inferred from the environment.
102         bqstorage_client(\
103             Optional[google.cloud.bigquery_storage_v1.BigQueryReadClient] \
104         ):
105             A client that uses the faster BigQuery Storage API to fetch rows from
106             BigQuery. If not passed, it is created using the same credentials
107             as ``client`` (provided that BigQuery Storage dependencies are installed).
108 
109             If both clients are available, ``bqstorage_client`` is used for
110             fetching query results.
111 
112     Returns:
113         google.cloud.bigquery.dbapi.Connection: A new DB-API connection to BigQuery.
114     """
115     return Connection(client, bqstorage_client)
116
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/google/cloud/bigquery/dbapi/connection.py b/google/cloud/bigquery/dbapi/connection.py
--- a/google/cloud/bigquery/dbapi/connection.py
+++ b/google/cloud/bigquery/dbapi/connection.py
@@ -76,7 +76,8 @@
             self._bqstorage_client._transport.grpc_channel.close()
 
         for cursor_ in self._cursors_created:
-            cursor_.close()
+            if not cursor_._closed:
+                cursor_.close()
 
     def commit(self):
         """No-op, but for consistency raise an error if connection is closed."""

{"golden_diff": "diff --git a/google/cloud/bigquery/dbapi/connection.py b/google/cloud/bigquery/dbapi/connection.py\n--- a/google/cloud/bigquery/dbapi/connection.py\n+++ b/google/cloud/bigquery/dbapi/connection.py\n@@ -76,7 +76,8 @@\n             self._bqstorage_client._transport.grpc_channel.close()\n \n         for cursor_ in self._cursors_created:\n-            cursor_.close()\n+            if not cursor_._closed:\n+                cursor_.close()\n \n     def commit(self):\n         \"\"\"No-op, but for consistency raise an error if connection is closed.\"\"\"\n", "issue": "Closing a connection tries to close already closed cursors\nWhen closing a connection the library will call `.close()` on every cursor created for that connection, including closed ones. While the code works, it produces a lot of logs of error level, polluting the logs.\r\n\r\n#### Environment details\r\n\r\n  - OS type and version: Mac OS 10.15.7\r\n  - Python version: Python 3.8.2\r\n  - pip version: pip 20.3.3\r\n  - `google-cloud-bigquery` version: 2.7.0\r\n\r\n#### Steps to reproduce\r\n\r\n  1. Close a cursor\r\n  2. Close the connection\r\n  3. Error log shows `Exception closing connection <google.cloud.bigquery.dbapi.connection.Connection object at 0x...>`\r\n\r\n#### Code example\r\n\r\n```python\r\nfrom contextlib import closing\r\n\r\n# using pybigquery\r\nwith closing(engine.raw_connection()) as conn:\r\n    with closing(conn.cursor()) as cursor:\r\n        cursor.execute(sql)\r\n```\r\n\r\n#### Stack trace\r\n```\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.7/site-packages/sqlalchemy/pool/base.py\", line 270, in _close_connection\r\n    self._dialect.do_close(connection)\r\n  File \"/usr/local/lib/python3.7/site-packages/sqlalchemy/engine/default.py\", line 549, in do_close\r\n    dbapi_connection.close()\r\n  File \"/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/dbapi/_helpers.py\", line 258, in with_closed_check\r\n    return method(self, *args, **kwargs)\r\n  File \"/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/dbapi/connection.py\", line 79, in close\r\n    cursor_.close()\r\n  File \"/usr/local/lib/python3.7/site-packages/google/cloud/bigquery/dbapi/_helpers.py\", line 257, in with_closed_check\r\n    raise exc_class(exc_msg)\r\ngoogle.cloud.bigquery.dbapi.exceptions.ProgrammingError: Operating on a closed cursor.\r\n```\r\n\r\n#### Suggested fix\r\n\r\n```python\r\n# google/cloud/bigquery/dbapi/connection.py\r\nclass Connection(object):\r\n    ...\r\n    def close(self):\r\n        ...\r\n        for cursor_ in self._cursors_created:\r\n            if not cursor_._closed:\r\n                cursor_.close()\r\n```\n", "before_files": [{"content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n#  Unless required by applicable law or agreed to in writing, software\n#  distributed under the License is distributed on an \"AS IS\" BASIS,\n#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  See the License for the specific language governing permissions and\n#  limitations under the License.\n\n\"\"\"Connection for the Google BigQuery DB-API.\"\"\"\n\nimport weakref\n\nfrom google.cloud import bigquery\nfrom google.cloud.bigquery.dbapi import cursor\nfrom google.cloud.bigquery.dbapi import _helpers\n\n\n@_helpers.raise_on_closed(\"Operating on a closed connection.\")\nclass Connection(object):\n    \"\"\"DB-API Connection to Google BigQuery.\n\n    Args:\n        client (Optional[google.cloud.bigquery.Client]):\n            A REST API client used to connect to BigQuery. If not passed, a\n            client is created using default options inferred from the environment.\n        bqstorage_client(\\\n            Optional[google.cloud.bigquery_storage_v1.BigQueryReadClient] \\\n        ):\n            A client that uses the faster BigQuery Storage API to fetch rows from\n            BigQuery. If not passed, it is created using the same credentials\n            as ``client`` (provided that BigQuery Storage dependencies are installed).\n\n            If both clients are available, ``bqstorage_client`` is used for\n            fetching query results.\n    \"\"\"\n\n    def __init__(self, client=None, bqstorage_client=None):\n        if client is None:\n            client = bigquery.Client()\n            self._owns_client = True\n        else:\n            self._owns_client = False\n\n        if bqstorage_client is None:\n            # A warning is already raised by the factory if instantiation fails.\n            bqstorage_client = client._create_bqstorage_client()\n            self._owns_bqstorage_client = bqstorage_client is not None\n        else:\n            self._owns_bqstorage_client = False\n\n        self._client = client\n        self._bqstorage_client = bqstorage_client\n\n        self._closed = False\n        self._cursors_created = weakref.WeakSet()\n\n    def close(self):\n        \"\"\"Close the connection and any cursors created from it.\n\n        Any BigQuery clients explicitly passed to the constructor are *not*\n        closed, only those created by the connection instance itself.\n        \"\"\"\n        self._closed = True\n\n        if self._owns_client:\n            self._client.close()\n\n        if self._owns_bqstorage_client:\n            # There is no close() on the BQ Storage client itself.\n            self._bqstorage_client._transport.grpc_channel.close()\n\n        for cursor_ in self._cursors_created:\n            cursor_.close()\n\n    def commit(self):\n        \"\"\"No-op, but for consistency raise an error if connection is closed.\"\"\"\n\n    def cursor(self):\n        \"\"\"Return a new cursor object.\n\n        Returns:\n            google.cloud.bigquery.dbapi.Cursor: A DB-API cursor that uses this connection.\n        \"\"\"\n        new_cursor = cursor.Cursor(self)\n        self._cursors_created.add(new_cursor)\n        return new_cursor\n\n\ndef connect(client=None, bqstorage_client=None):\n    \"\"\"Construct a DB-API connection to Google BigQuery.\n\n    Args:\n        client (Optional[google.cloud.bigquery.Client]):\n            A REST API client used to connect to BigQuery. If not passed, a\n            client is created using default options inferred from the environment.\n        bqstorage_client(\\\n            Optional[google.cloud.bigquery_storage_v1.BigQueryReadClient] \\\n        ):\n            A client that uses the faster BigQuery Storage API to fetch rows from\n            BigQuery. If not passed, it is created using the same credentials\n            as ``client`` (provided that BigQuery Storage dependencies are installed).\n\n            If both clients are available, ``bqstorage_client`` is used for\n            fetching query results.\n\n    Returns:\n        google.cloud.bigquery.dbapi.Connection: A new DB-API connection to BigQuery.\n    \"\"\"\n    return Connection(client, bqstorage_client)\n", "path": "google/cloud/bigquery/dbapi/connection.py"}], "after_files": [{"content": "# Copyright 2017 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n#  Unless required by applicable law or agreed to in writing, software\n#  distributed under the License is distributed on an \"AS IS\" BASIS,\n#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  See the License for the specific language governing permissions and\n#  limitations under the License.\n\n\"\"\"Connection for the Google BigQuery DB-API.\"\"\"\n\nimport weakref\n\nfrom google.cloud import bigquery\nfrom google.cloud.bigquery.dbapi import cursor\nfrom google.cloud.bigquery.dbapi import _helpers\n\n\n@_helpers.raise_on_closed(\"Operating on a closed connection.\")\nclass Connection(object):\n    \"\"\"DB-API Connection to Google BigQuery.\n\n    Args:\n        client (Optional[google.cloud.bigquery.Client]):\n            A REST API client used to connect to BigQuery. If not passed, a\n            client is created using default options inferred from the environment.\n        bqstorage_client(\\\n            Optional[google.cloud.bigquery_storage_v1.BigQueryReadClient] \\\n        ):\n            A client that uses the faster BigQuery Storage API to fetch rows from\n            BigQuery. If not passed, it is created using the same credentials\n            as ``client`` (provided that BigQuery Storage dependencies are installed).\n\n            If both clients are available, ``bqstorage_client`` is used for\n            fetching query results.\n    \"\"\"\n\n    def __init__(self, client=None, bqstorage_client=None):\n        if client is None:\n            client = bigquery.Client()\n            self._owns_client = True\n        else:\n            self._owns_client = False\n\n        if bqstorage_client is None:\n            # A warning is already raised by the factory if instantiation fails.\n            bqstorage_client = client._create_bqstorage_client()\n            self._owns_bqstorage_client = bqstorage_client is not None\n        else:\n            self._owns_bqstorage_client = False\n\n        self._client = client\n        self._bqstorage_client = bqstorage_client\n\n        self._closed = False\n        self._cursors_created = weakref.WeakSet()\n\n    def close(self):\n        \"\"\"Close the connection and any cursors created from it.\n\n        Any BigQuery clients explicitly passed to the constructor are *not*\n        closed, only those created by the connection instance itself.\n        \"\"\"\n        self._closed = True\n\n        if self._owns_client:\n            self._client.close()\n\n        if self._owns_bqstorage_client:\n            # There is no close() on the BQ Storage client itself.\n            self._bqstorage_client._transport.grpc_channel.close()\n\n        for cursor_ in self._cursors_created:\n            if not cursor_._closed:\n                cursor_.close()\n\n    def commit(self):\n        \"\"\"No-op, but for consistency raise an error if connection is closed.\"\"\"\n\n    def cursor(self):\n        \"\"\"Return a new cursor object.\n\n        Returns:\n            google.cloud.bigquery.dbapi.Cursor: A DB-API cursor that uses this connection.\n        \"\"\"\n        new_cursor = cursor.Cursor(self)\n        self._cursors_created.add(new_cursor)\n        return new_cursor\n\n\ndef connect(client=None, bqstorage_client=None):\n    \"\"\"Construct a DB-API connection to Google BigQuery.\n\n    Args:\n        client (Optional[google.cloud.bigquery.Client]):\n            A REST API client used to connect to BigQuery. If not passed, a\n            client is created using default options inferred from the environment.\n        bqstorage_client(\\\n            Optional[google.cloud.bigquery_storage_v1.BigQueryReadClient] \\\n        ):\n            A client that uses the faster BigQuery Storage API to fetch rows from\n            BigQuery. If not passed, it is created using the same credentials\n            as ``client`` (provided that BigQuery Storage dependencies are installed).\n\n            If both clients are available, ``bqstorage_client`` is used for\n            fetching query results.\n\n    Returns:\n        google.cloud.bigquery.dbapi.Connection: A new DB-API connection to BigQuery.\n    \"\"\"\n    return Connection(client, bqstorage_client)\n", "path": "google/cloud/bigquery/dbapi/connection.py"}]}