prompt: 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()
  
  ```
  
golden_diff: 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."""
  
verification_info: {"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"}]}