From 27814dfbea8267d1b7abbc47fbbb56bb36f3585d Mon Sep 17 00:00:00 2001
From: aribray <45905583+aribray@users.noreply.github.com>
Date: Thu, 2 Feb 2023 13:49:04 -0600
Subject: [PATCH 01/25] fix: remove type annotations from _struct.py (#733)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: remove type annotations from _struct.py

* pin geoalchemy2 dependency

* lower sqlalchemy version

* revert version change

* Update _struct.py

Removed unused imports to satisfy linter.

* Update setup.py

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 sqlalchemy_bigquery/_struct.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/sqlalchemy_bigquery/_struct.py b/sqlalchemy_bigquery/_struct.py
index 6ebb5a64..fc551c12 100644
--- a/sqlalchemy_bigquery/_struct.py
+++ b/sqlalchemy_bigquery/_struct.py
@@ -17,8 +17,6 @@
 # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-from typing import Mapping, Tuple
-
 import packaging.version
 import sqlalchemy.sql.default_comparator
 import sqlalchemy.sql.sqltypes
@@ -54,8 +52,8 @@ class STRUCT(sqlalchemy.sql.sqltypes.Indexable, sqlalchemy.types.UserDefinedType
 
     def __init__(
         self,
-        *fields: Tuple[str, sqlalchemy.types.TypeEngine],
-        **kwfields: Mapping[str, sqlalchemy.types.TypeEngine],
+        *fields,
+        **kwfields,
     ):
         # Note that because:
         # https://docs.python.org/3/whatsnew/3.6.html#pep-468-preserving-keyword-argument-order

From 8e9aa893520a37089c93817f2cccfb09c0c07dce Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Wed, 8 Feb 2023 17:10:11 +0000
Subject: [PATCH 02/25] build(deps): bump cryptography from 38.0.3 to 39.0.1 in
 /synthtool/gcp/templates/python_library/.kokoro (#745)

Source-Link: https://togithub.com/googleapis/synthtool/commit/bb171351c3946d3c3c32e60f5f18cee8c464ec51
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:f62c53736eccb0c4934a3ea9316e0d57696bb49c1a7c86c726e9bb8a2f87dadf
---
 .github/.OwlBot.lock.yaml |  2 +-
 .kokoro/requirements.txt  | 49 ++++++++++++++++++---------------------
 2 files changed, 23 insertions(+), 28 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index f0f3b24b..894fb6bc 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,4 +13,4 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:f946c75373c2b0040e8e318c5e85d0cf46bc6e61d0a01f3ef94d8de974ac6790
+  digest: sha256:f62c53736eccb0c4934a3ea9316e0d57696bb49c1a7c86c726e9bb8a2f87dadf
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
index 05dc4672..096e4800 100644
--- a/.kokoro/requirements.txt
+++ b/.kokoro/requirements.txt
@@ -113,33 +113,28 @@ commonmark==0.9.1 \
     --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \
     --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9
     # via rich
-cryptography==38.0.3 \
-    --hash=sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d \
-    --hash=sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd \
-    --hash=sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146 \
-    --hash=sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7 \
-    --hash=sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436 \
-    --hash=sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0 \
-    --hash=sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828 \
-    --hash=sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b \
-    --hash=sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55 \
-    --hash=sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36 \
-    --hash=sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50 \
-    --hash=sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2 \
-    --hash=sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a \
-    --hash=sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8 \
-    --hash=sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0 \
-    --hash=sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548 \
-    --hash=sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320 \
-    --hash=sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748 \
-    --hash=sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249 \
-    --hash=sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959 \
-    --hash=sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f \
-    --hash=sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0 \
-    --hash=sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd \
-    --hash=sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220 \
-    --hash=sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c \
-    --hash=sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722
+cryptography==39.0.1 \
+    --hash=sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4 \
+    --hash=sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f \
+    --hash=sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502 \
+    --hash=sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41 \
+    --hash=sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965 \
+    --hash=sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e \
+    --hash=sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc \
+    --hash=sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad \
+    --hash=sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505 \
+    --hash=sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388 \
+    --hash=sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6 \
+    --hash=sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2 \
+    --hash=sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac \
+    --hash=sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695 \
+    --hash=sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6 \
+    --hash=sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336 \
+    --hash=sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0 \
+    --hash=sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c \
+    --hash=sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106 \
+    --hash=sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a \
+    --hash=sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8
     # via
     #   gcp-releasetool
     #   secretstorage

From 074321ddaa10001773e7e6044f4a0df1bb530331 Mon Sep 17 00:00:00 2001
From: Alvaro Viebrantz <alvarowolfx@gmail.com>
Date: Wed, 8 Feb 2023 22:00:20 -0400
Subject: [PATCH 03/25] docs: pass credentials as python dictionary (#737)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Document how to pass the credentials directly as dictionary/json instead of key json file path.

Fixes #331  🦕
---
 README.rst | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/README.rst b/README.rst
index d4b4ea68..7898cc67 100644
--- a/README.rst
+++ b/README.rst
@@ -104,12 +104,27 @@ Project
 Authentication
 ^^^^^^^^^^^^^^
 
-Follow the `Google Cloud library guide <https://google-cloud-python.readthedocs.io/en/latest/core/auth.html>`_ for authentication. Alternatively, you can provide the path to a service account JSON file in ``create_engine()``:
+Follow the `Google Cloud library guide <https://google-cloud-python.readthedocs.io/en/latest/core/auth.html>`_ for authentication. 
+
+Alternatively, you can choose either of the following approaches:
+
+* provide the path to a service account JSON file in ``create_engine()`` using the ``credentials_path`` parameter:
 
 .. code-block:: python
 
+    # provide the path to a service account JSON file
     engine = create_engine('bigquery://', credentials_path='/path/to/keyfile.json')
 
+* pass the credentials in ``create_engine()`` as a Python dictionary using the ``credentials_info`` parameter:
+
+.. code-block:: python
+    
+    # provide credentials as a Python dictionary
+    credentials_info = {
+        "type": "service_account", 
+        "project_id": "your-service-account-project-id"
+    },
+    engine = create_engine('bigquery://', credentials_info=credentials_info)
 
 Location
 ^^^^^^^^

From 942720a19a9bdba0c6ec29ccfb3b2fd80ea7447b Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Mon, 13 Feb 2023 17:59:35 +0000
Subject: [PATCH 04/25] chore(deps): update dependency protobuf to v4.21.6
 [security] (#495)

Co-authored-by: Anthonios Partheniou <partheniou@google.com>
---
 samples/snippets/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 48a238da..28835d1e 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -19,7 +19,7 @@ mako==1.2.2
 markupsafe==2.1.1
 packaging==21.3
 proto-plus==1.22.1
-protobuf==4.21.5
+protobuf==4.21.6
 pyasn1==0.4.8
 pyasn1-modules==0.2.8
 pyparsing==3.0.9

From 4a26e2bc4ca7e6b41b45e93fc7c2ab50f0a80235 Mon Sep 17 00:00:00 2001
From: Chalmer Lowe <chalmerlowe@google.com>
Date: Thu, 16 Feb 2023 11:00:32 -0500
Subject: [PATCH 05/25] chore: updates owlbot to set pytest version and add
 troubleshooting output (#837)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Sets cache support to false, removes upper req limit

* chore: updates owlbot to set pytest version and add troubleshooting output

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 noxfile.py | 6 +++---
 owlbot.py  | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/noxfile.py b/noxfile.py
index 2c4cc035..457800d6 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -302,9 +302,7 @@ def compliance(session):
     session.install("--pre", "--no-deps", "--upgrade", "sqlalchemy<2.0.0")
     session.install(
         "mock",
-        # TODO: Allow latest version of pytest once SQLAlchemy 1.4.28+ is supported.
-        # See: https://github.com/googleapis/python-bigquery-sqlalchemy/issues/413
-        "pytest<=7.0.0dev",
+        "pytest",
         "pytest-rerunfailures",
         "google-cloud-testutils",
         "-c",
@@ -318,6 +316,8 @@ def compliance(session):
         extras = "[tests]"
     session.install("-e", f".{extras}", "-c", constraints_path)
 
+    session.run("python", "-m", "pip", "freeze")
+
     session.run(
         "py.test",
         "-vv",
diff --git a/owlbot.py b/owlbot.py
index 1ea90868..a2882583 100644
--- a/owlbot.py
+++ b/owlbot.py
@@ -140,9 +140,7 @@ def compliance(session):
     session.install("--pre", "--no-deps", "--upgrade", "sqlalchemy<2.0.0") 
     session.install(
         "mock",
-        # TODO: Allow latest version of pytest once SQLAlchemy 1.4.28+ is supported.
-        # See: https://github.com/googleapis/python-bigquery-sqlalchemy/issues/413
-        "pytest<=7.0.0dev",
+        "pytest",
         "pytest-rerunfailures",
         "google-cloud-testutils",
         "-c",
@@ -156,6 +154,8 @@ def compliance(session):
         extras = "[tests]"
     session.install("-e", f".{extras}", "-c", constraints_path)
 
+    session.run("python", "-m", "pip", "freeze")
+
     session.run(
         "py.test",
         "-vv",

From 55276a2bfca2a94d9a58c6d69f29a67b568db64b Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Tue, 28 Feb 2023 05:39:49 -0500
Subject: [PATCH 06/25] chore(python): upgrade gcp-releasetool in .kokoro
 [autoapprove] (#840)

Source-Link: https://github.com/googleapis/synthtool/commit/5f2a6089f73abf06238fe4310f6a14d6f6d1eed3
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:8555f0e37e6261408f792bfd6635102d2da5ad73f8f09bcb24f25e6afb5fac97

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .github/.OwlBot.lock.yaml | 2 +-
 .kokoro/requirements.in   | 2 +-
 .kokoro/requirements.txt  | 6 +++---
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 894fb6bc..5fc5daa3 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,4 +13,4 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:f62c53736eccb0c4934a3ea9316e0d57696bb49c1a7c86c726e9bb8a2f87dadf
+  digest: sha256:8555f0e37e6261408f792bfd6635102d2da5ad73f8f09bcb24f25e6afb5fac97
diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in
index cbd7e77f..882178ce 100644
--- a/.kokoro/requirements.in
+++ b/.kokoro/requirements.in
@@ -1,5 +1,5 @@
 gcp-docuploader
-gcp-releasetool
+gcp-releasetool>=1.10.5 # required for compatibility with cryptography>=39.x
 importlib-metadata
 typing-extensions
 twine
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
index 096e4800..fa99c129 100644
--- a/.kokoro/requirements.txt
+++ b/.kokoro/requirements.txt
@@ -154,9 +154,9 @@ gcp-docuploader==0.6.4 \
     --hash=sha256:01486419e24633af78fd0167db74a2763974765ee8078ca6eb6964d0ebd388af \
     --hash=sha256:70861190c123d907b3b067da896265ead2eeb9263969d6955c9e0bb091b5ccbf
     # via -r requirements.in
-gcp-releasetool==1.10.0 \
-    --hash=sha256:72a38ca91b59c24f7e699e9227c90cbe4dd71b789383cb0164b088abae294c83 \
-    --hash=sha256:8c7c99320208383d4bb2b808c6880eb7a81424afe7cdba3c8d84b25f4f0e097d
+gcp-releasetool==1.10.5 \
+    --hash=sha256:174b7b102d704b254f2a26a3eda2c684fd3543320ec239baf771542a2e58e109 \
+    --hash=sha256:e29d29927fe2ca493105a82958c6873bb2b90d503acac56be2c229e74de0eec9
     # via -r requirements.in
 google-api-core==2.10.2 \
     --hash=sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320 \

From 1920081b9e4c3451279a0e692eb03eaf1ae0d0c5 Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Thu, 16 Mar 2023 08:27:57 -0400
Subject: [PATCH 07/25] chore(deps): Update nox in .kokoro/requirements.in
 [autoapprove] (#846)

Source-Link: https://github.com/googleapis/synthtool/commit/92006bb3cdc84677aa93c7f5235424ec2b157146
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:2e247c7bf5154df7f98cce087a20ca7605e236340c7d6d1a14447e5c06791bd6

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .github/.OwlBot.lock.yaml |  2 +-
 .kokoro/requirements.in   |  2 +-
 .kokoro/requirements.txt  | 14 +++++---------
 3 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 5fc5daa3..b8edda51 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,4 +13,4 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:8555f0e37e6261408f792bfd6635102d2da5ad73f8f09bcb24f25e6afb5fac97
+  digest: sha256:2e247c7bf5154df7f98cce087a20ca7605e236340c7d6d1a14447e5c06791bd6
diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in
index 882178ce..ec867d9f 100644
--- a/.kokoro/requirements.in
+++ b/.kokoro/requirements.in
@@ -5,6 +5,6 @@ typing-extensions
 twine
 wheel
 setuptools
-nox
+nox>=2022.11.21 # required to remove dependency on py
 charset-normalizer<3
 click<8.1.0
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
index fa99c129..66a2172a 100644
--- a/.kokoro/requirements.txt
+++ b/.kokoro/requirements.txt
@@ -1,6 +1,6 @@
 #
-# This file is autogenerated by pip-compile with python 3.10
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
 #
 #    pip-compile --allow-unsafe --generate-hashes requirements.in
 #
@@ -335,9 +335,9 @@ more-itertools==9.0.0 \
     --hash=sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41 \
     --hash=sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab
     # via jaraco-classes
-nox==2022.8.7 \
-    --hash=sha256:1b894940551dc5c389f9271d197ca5d655d40bdc6ccf93ed6880e4042760a34b \
-    --hash=sha256:96cca88779e08282a699d672258ec01eb7c792d35bbbf538c723172bce23212c
+nox==2022.11.21 \
+    --hash=sha256:0e41a990e290e274cb205a976c4c97ee3c5234441a8132c8c3fd9ea3c22149eb \
+    --hash=sha256:e21c31de0711d1274ca585a2c5fde36b1aa962005ba8e9322bf5eeed16dcd684
     # via -r requirements.in
 packaging==21.3 \
     --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
@@ -380,10 +380,6 @@ protobuf==3.20.3 \
     #   gcp-docuploader
     #   gcp-releasetool
     #   google-api-core
-py==1.11.0 \
-    --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \
-    --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378
-    # via nox
 pyasn1==0.4.8 \
     --hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
     --hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba

From 5d6b38c94dfef0bd0dbf5037ad21a352d2bd8e4f Mon Sep 17 00:00:00 2001
From: Chalmer Lowe <chalmerlowe@google.com>
Date: Wed, 22 Mar 2023 12:06:20 -0400
Subject: [PATCH 08/25] feat: Remove pyarrow and bqstorage as dependencies
 (#847)

* Sets cache support to false, removes upper req limit

* feat: remove dependency on bqstorage and pyarrow

* minor tweaks based on comments, remove unneeded constraints file

* updates coverage flags

* experiment with no cover, cause issues

* updates no cover pragma in all caps

* fixes linting error

* updates owlbot
---
 README.rst                      | 14 ++++++
 noxfile.py                      | 59 +++++++++++++++++++++-
 owlbot.py                       | 86 ++++++++++++++++++++++++++++++++-
 setup.py                        | 27 ++++++++---
 sqlalchemy_bigquery/__init__.py |  2 +-
 sqlalchemy_bigquery/_types.py   |  4 +-
 sqlalchemy_bigquery/base.py     |  2 +-
 testing/constraints-3.6.txt     | 12 -----
 testing/constraints-3.7.txt     |  2 +-
 tests/unit/test_geography.py    |  4 +-
 10 files changed, 183 insertions(+), 29 deletions(-)
 delete mode 100644 testing/constraints-3.6.txt

diff --git a/README.rst b/README.rst
index 7898cc67..0e0d30bc 100644
--- a/README.rst
+++ b/README.rst
@@ -81,6 +81,20 @@ Windows
     <your-env>\Scripts\activate
     <your-env>\Scripts\pip.exe install sqlalchemy-bigquery
 
+
+Installations when processing large datasets
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When handling large datasets, you may see speed increases by also installing the
+`bqstorage` dependencies. See the instructions above about creating a virtual 
+environment and then install `sqlalchemy-bigquery` using the `bqstorage` extras:
+
+.. code-block:: console
+
+    source <your-env>/bin/activate
+    <your-env>/bin/pip install sqlalchemy-bigquery[bqstorage]
+
+
 Usage
 -----
 
diff --git a/noxfile.py b/noxfile.py
index 457800d6..a6c38339 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -50,10 +50,12 @@
     "3.8": [
         "tests",
         "alembic",
+        "bqstorage",
     ],
     "3.11": [
         "tests",
         "geography",
+        "bqstorage",
     ],
 }
 
@@ -73,10 +75,12 @@
     "3.8": [
         "tests",
         "alembic",
+        "bqstorage",
     ],
     "3.11": [
         "tests",
         "geography",
+        "bqstorage",
     ],
 }
 
@@ -179,7 +183,7 @@ def install_unittest_dependencies(session, *constraints):
         session.install("-e", ".", *constraints)
 
 
-def default(session):
+def default(session, install_extras=True):
     # Install all test dependencies, then install this package in-place.
 
     constraints_path = str(
@@ -187,6 +191,14 @@ def default(session):
     )
     install_unittest_dependencies(session, "-c", constraints_path)
 
+    if install_extras and session.python == "3.11":
+        install_target = ".[geography,alembic,tests,bqstorage]"
+    elif install_extras:
+        install_target = ".[all]"
+    else:
+        install_target = "."
+    session.install("-e", install_target, "-c", constraints_path)
+
     # Run py.test against the unit tests.
     session.run(
         "py.test",
@@ -283,6 +295,51 @@ def system(session):
         )
 
 
+@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
+def system_noextras(session):
+    """Run the system test suite."""
+    constraints_path = str(
+        CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
+    )
+    system_test_path = os.path.join("tests", "system.py")
+    system_test_folder_path = os.path.join("tests", "system")
+
+    # Check the value of `RUN_SYSTEM_TESTS` env var. It defaults to true.
+    if os.environ.get("RUN_SYSTEM_TESTS", "true") == "false":
+        session.skip("RUN_SYSTEM_TESTS is set to false, skipping")
+    # Install pyopenssl for mTLS testing.
+    if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true":
+        session.install("pyopenssl")
+
+    system_test_exists = os.path.exists(system_test_path)
+    system_test_folder_exists = os.path.exists(system_test_folder_path)
+    # Sanity check: only run tests if found.
+    if not system_test_exists and not system_test_folder_exists:
+        session.skip("System tests were not found")
+
+    global SYSTEM_TEST_EXTRAS_BY_PYTHON
+    SYSTEM_TEST_EXTRAS_BY_PYTHON = False
+    install_systemtest_dependencies(session, "-c", constraints_path)
+
+    # Run py.test against the system tests.
+    if system_test_exists:
+        session.run(
+            "py.test",
+            "--quiet",
+            f"--junitxml=system_{session.python}_sponge_log.xml",
+            system_test_path,
+            *session.posargs,
+        )
+    if system_test_folder_exists:
+        session.run(
+            "py.test",
+            "--quiet",
+            f"--junitxml=system_{session.python}_sponge_log.xml",
+            system_test_folder_path,
+            *session.posargs,
+        )
+
+
 @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS[-1])
 def compliance(session):
     """Run the SQLAlchemy dialect-compliance system tests"""
diff --git a/owlbot.py b/owlbot.py
index a2882583..4cc564d4 100644
--- a/owlbot.py
+++ b/owlbot.py
@@ -29,8 +29,8 @@
 # ----------------------------------------------------------------------------
 extras = ["tests"]
 extras_by_python = {
-    "3.8": ["tests", "alembic"],
-    "3.11": ["tests", "geography"],
+    "3.8": ["tests", "alembic", "bqstorage"],
+    "3.11": ["tests", "geography", "bqstorage"],
 }
 templated_files = common.py_library(
     unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11"],
@@ -95,6 +95,14 @@
 )
 
 
+s.replace(
+    ["noxfile.py"],
+    r"def default\(session\)",
+    "def default(session, install_extras=True)",    
+)
+
+
+
 
 def place_before(path, text, *before_text, escape=None):
     replacement = "\n".join(before_text) + "\n" + text
@@ -117,6 +125,23 @@ def place_before(path, text, *before_text, escape=None):
 )
 
 
+install_logic = '''
+    if install_extras and session.python == "3.11":
+        install_target = ".[geography,alembic,tests,bqstorage]"
+    elif install_extras:
+        install_target = ".[all]"
+    else:
+        install_target = "."
+    session.install("-e", install_target, "-c", constraints_path)
+'''
+
+place_before(
+    "noxfile.py",
+    "# Run py.test against the unit tests.",
+    install_logic,
+)
+
+
 # Maybe we can get rid of this when we don't need pytest-rerunfailures,
 # which we won't need when BQ retries itself:
 # https://github.com/googleapis/python-bigquery/pull/837
@@ -189,6 +214,63 @@ def compliance(session):
 s.replace(["noxfile.py"], '"alabaster"', '"alabaster", "geoalchemy2", "shapely"')
 
 
+system_noextras = '''
+@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS)
+def system_noextras(session):
+    """Run the system test suite."""
+    constraints_path = str(
+        CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
+    )
+    system_test_path = os.path.join("tests", "system.py")
+    system_test_folder_path = os.path.join("tests", "system")
+
+    # Check the value of `RUN_SYSTEM_TESTS` env var. It defaults to true.
+    if os.environ.get("RUN_SYSTEM_TESTS", "true") == "false":
+        session.skip("RUN_SYSTEM_TESTS is set to false, skipping")
+    # Install pyopenssl for mTLS testing.
+    if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true":
+        session.install("pyopenssl")
+
+    system_test_exists = os.path.exists(system_test_path)
+    system_test_folder_exists = os.path.exists(system_test_folder_path)
+    # Sanity check: only run tests if found.
+    if not system_test_exists and not system_test_folder_exists:
+        session.skip("System tests were not found")
+
+    global SYSTEM_TEST_EXTRAS_BY_PYTHON
+    SYSTEM_TEST_EXTRAS_BY_PYTHON = False
+    install_systemtest_dependencies(session, "-c", constraints_path)
+
+    # Run py.test against the system tests.
+    if system_test_exists:
+        session.run(
+            "py.test",
+            "--quiet",
+            f"--junitxml=system_{session.python}_sponge_log.xml",
+            system_test_path,
+            *session.posargs,
+        )
+    if system_test_folder_exists:
+        session.run(
+            "py.test",
+            "--quiet",
+            f"--junitxml=system_{session.python}_sponge_log.xml",
+            system_test_folder_path,
+            *session.posargs,
+        )
+
+        
+'''
+
+
+place_before(
+    "noxfile.py",
+    "@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS[-1])\n"
+    "def compliance(session):", 
+    system_noextras,
+    escape="()[]",
+    )
+
 
 # Add DB config for SQLAlchemy dialect test suite.
 # https://github.com/googleapis/python-bigquery-sqlalchemy/issues/89
diff --git a/setup.py b/setup.py
index bed5195c..026013aa 100644
--- a/setup.py
+++ b/setup.py
@@ -45,11 +45,26 @@ def readme():
         return f.read()
 
 
-extras = dict(
-    geography=["GeoAlchemy2", "shapely"],
-    alembic=["alembic"],
-    tests=["packaging", "pytz"],
-)
+extras = {
+    "geography": ["GeoAlchemy2", "shapely"],
+    "alembic": ["alembic"],
+    "tests": ["packaging", "pytz"],
+    # Keep the no-op bqstorage extra for backward compatibility.
+    # See: https://github.com/googleapis/python-bigquery/issues/757
+    "bqstorage": [
+        "google-cloud-bigquery-storage >= 2.0.0, <3.0.0dev",
+        # Due to an issue in pip's dependency resolver, the `grpc` extra is not
+        # installed, even though `google-cloud-bigquery-storage` specifies it
+        # as `google-api-core[grpc]`. We thus need to explicitly specify it here.
+        # See: https://github.com/googleapis/python-bigquery/issues/83 The
+        # grpc.Channel.close() method isn't added until 1.32.0.
+        # https://github.com/grpc/grpc/pull/15254
+        "grpcio >= 1.47.0, < 2.0dev",
+        "grpcio >= 1.49.1, < 2.0dev; python_version>='3.11'",
+        "pyarrow >= 3.0.0",
+    ],
+}
+
 extras["all"] = set(itertools.chain.from_iterable(extras.values()))
 
 setup(
@@ -85,9 +100,7 @@ def readme():
         # https://github.com/googleapis/google-cloud-python/issues/10566
         "google-auth>=1.25.0,<3.0.0dev",  # Work around pip wack.
         "google-cloud-bigquery>=2.25.2,<4.0.0dev",
-        "google-cloud-bigquery-storage>=2.0.0,<3.0.0dev",
         "packaging",
-        "pyarrow>=3.0.0",
         "sqlalchemy>=1.2.0,<2.0.0dev",
         "future",
     ],
diff --git a/sqlalchemy_bigquery/__init__.py b/sqlalchemy_bigquery/__init__.py
index 2739dfa2..55253049 100644
--- a/sqlalchemy_bigquery/__init__.py
+++ b/sqlalchemy_bigquery/__init__.py
@@ -68,7 +68,7 @@
 
 try:
     from .geography import GEOGRAPHY, WKB, WKT  # noqa
-except ImportError:
+except ImportError:  # pragma: NO COVER
     pass
 else:
     __all__.extend(["GEOGRAPHY", "WKB", "WKT"])
diff --git a/sqlalchemy_bigquery/_types.py b/sqlalchemy_bigquery/_types.py
index 4e18dc2a..8399e978 100644
--- a/sqlalchemy_bigquery/_types.py
+++ b/sqlalchemy_bigquery/_types.py
@@ -24,7 +24,7 @@
 
 try:
     from .geography import GEOGRAPHY
-except ImportError:
+except ImportError:  # pragma: NO COVER
     pass
 
 from ._struct import STRUCT
@@ -69,7 +69,7 @@
 
 try:
     _type_map["GEOGRAPHY"] = GEOGRAPHY
-except NameError:
+except NameError:  # pragma: NO COVER
     pass
 
 STRUCT_FIELD_TYPES = "RECORD", "STRUCT"
diff --git a/sqlalchemy_bigquery/base.py b/sqlalchemy_bigquery/base.py
index 843f6312..4fae670e 100644
--- a/sqlalchemy_bigquery/base.py
+++ b/sqlalchemy_bigquery/base.py
@@ -1055,7 +1055,7 @@ def __init__(self, *args, **kwargs):
 
 try:
     import alembic  # noqa
-except ImportError:
+except ImportError:  # pragma: NO COVER
     pass
 else:
     from alembic.ddl import impl
diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt
deleted file mode 100644
index 9d2df4fe..00000000
--- a/testing/constraints-3.6.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-# This constraints file is used to check that lower bounds
-# are correct in setup.py
-# List *all* library dependencies and extras in this file.
-# Pin the version to the lower bound.
-#
-# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
-sqlalchemy==1.2.0
-google-auth==1.25.0
-google-cloud-bigquery==2.25.2
-google-cloud-bigquery-storage==2.0.0
-google-api-core==1.31.5
-pyarrow==3.0.0
diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt
index 9d2df4fe..1d0a1b72 100644
--- a/testing/constraints-3.7.txt
+++ b/testing/constraints-3.7.txt
@@ -6,7 +6,7 @@
 # e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev",
 sqlalchemy==1.2.0
 google-auth==1.25.0
-google-cloud-bigquery==2.25.2
+google-cloud-bigquery==3.3.6
 google-cloud-bigquery-storage==2.0.0
 google-api-core==1.31.5
 pyarrow==3.0.0
diff --git a/tests/unit/test_geography.py b/tests/unit/test_geography.py
index 25d3c605..fb856dca 100644
--- a/tests/unit/test_geography.py
+++ b/tests/unit/test_geography.py
@@ -104,7 +104,7 @@ def test_geoalchemy2_core(faux_conn, last_query):
 
     try:
         conn.execute(
-            select([lake_table.c.name, lake_table.c.geog.ST_AREA().label("area")])
+            select([lake_table.c.name, lake_table.c.geog.ST_Area().label("area")])
         )
     except Exception:
         pass  # sqlite had no special functions :)
@@ -169,7 +169,7 @@ def test_calling_st_functions_that_dont_take_geographies(faux_conn, last_query):
     from sqlalchemy import select, func
 
     try:
-        faux_conn.execute(select([func.ST_GEOGFROMTEXT("point(0 0)")]))
+        faux_conn.execute(select([func.ST_GeogFromText("point(0 0)")]))
     except Exception:
         pass  # sqlite had no special functions :)
 

From fd780938ca4a0d32448ed666753360f05584d2ab Mon Sep 17 00:00:00 2001
From: Teddy <teddy.crepineau@gmail.com>
Date: Thu, 23 Mar 2023 18:55:32 +0100
Subject: [PATCH 09/25] feat: Added regexp_match operator support for bigquery
 (#511)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: Added regexp_match operator support for bigquery

* feat: address tests implementation

* feat: fixed python tests

* Adds two tests, tweaks test workflow

* adds unit tests, fixes linting, adds troubleshooting code

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 sqlalchemy_bigquery/base.py              | 12 +++++++
 tests/system/test_sqlalchemy_bigquery.py | 32 +++++++++++++++++-
 tests/unit/test_select.py                | 41 ++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/sqlalchemy_bigquery/base.py b/sqlalchemy_bigquery/base.py
index 4fae670e..3a55af69 100644
--- a/sqlalchemy_bigquery/base.py
+++ b/sqlalchemy_bigquery/base.py
@@ -553,6 +553,18 @@ def visit_getitem_binary(self, binary, operator_, **kw):
         right = self.process(binary.right, **kw)
         return f"{left}[OFFSET({right})]"
 
+    def _get_regexp_args(self, binary, kw):
+        string = self.process(binary.left, **kw)
+        pattern = self.process(binary.right, **kw)
+        return string, pattern
+
+    def visit_regexp_match_op_binary(self, binary, operator, **kw):
+        string, pattern = self._get_regexp_args(binary, kw)
+        return "REGEXP_CONTAINS(%s, %s)" % (string, pattern)
+
+    def visit_not_regexp_match_op_binary(self, binary, operator, **kw):
+        return "NOT %s" % self.visit_regexp_match_op_binary(binary, operator, **kw)
+
 
 class BigQueryTypeCompiler(GenericTypeCompiler):
     def visit_INTEGER(self, type_, **kw):
diff --git a/tests/system/test_sqlalchemy_bigquery.py b/tests/system/test_sqlalchemy_bigquery.py
index 0772e10a..1baabda3 100644
--- a/tests/system/test_sqlalchemy_bigquery.py
+++ b/tests/system/test_sqlalchemy_bigquery.py
@@ -25,7 +25,7 @@
 from sqlalchemy.engine import create_engine
 from sqlalchemy.schema import Table, MetaData, Column
 from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy import types, func, case, inspect
+from sqlalchemy import types, func, case, inspect, not_
 from sqlalchemy.sql import expression, select, literal_column
 from sqlalchemy.exc import NoSuchTableError
 from sqlalchemy.orm import sessionmaker
@@ -774,3 +774,33 @@ def test_unnest(engine, bigquery_dataset):
         f" unnest(`{bigquery_dataset}.test_unnest_1`.`objects`) AS `foo_objects`"
     )
     assert sorted(r[0] for r in conn.execute(query)) == ["a", "b", "c", "x", "y"]
+
+
+@pytest.mark.skipif(
+    packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"),
+    reason="regexp_match support requires version 1.4 or higher",
+)
+def test_regexp_match(session, table):
+    results = (
+        session.query(table.c.string)
+        .where(table.c.string.regexp_match(".*52 St &.*"))
+        .all()
+    )
+
+    number_of_52st_records = 12
+    assert len(results) == number_of_52st_records
+
+
+@pytest.mark.skipif(
+    packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"),
+    reason="regexp_match support requires version 1.4 or higher",
+)
+def test_not_regexp_match(session, table):
+    results = (
+        session.query(table.c.string)
+        .where(not_(table.c.string.regexp_match("^Barrow St & Hudson St$")))
+        .all()
+    )
+
+    number_of_non_barrowst_records = 993
+    assert len(results) == number_of_non_barrowst_records
diff --git a/tests/unit/test_select.py b/tests/unit/test_select.py
index 08fc0225..ee5e01cb 100644
--- a/tests/unit/test_select.py
+++ b/tests/unit/test_select.py
@@ -23,6 +23,7 @@
 import packaging.version
 import pytest
 import sqlalchemy
+from sqlalchemy import not_
 
 import sqlalchemy_bigquery
 
@@ -445,3 +446,43 @@ def test_array_indexing(faux_conn, metadata):
     )
     got = str(sqlalchemy.select([t.c.a[0]]).compile(faux_conn.engine))
     assert got == "SELECT `t`.`a`[OFFSET(%(a_1:INT64)s)] AS `anon_1` \nFROM `t`"
+
+
+@pytest.mark.skipif(
+    packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"),
+    reason="regexp_match support requires version 1.4 or higher",
+)
+def test_visit_regexp_match_op_binary(faux_conn):
+    table = setup_table(
+        faux_conn,
+        "table",
+        sqlalchemy.Column("foo", sqlalchemy.String),
+    )
+
+    # NOTE: "sample_pattern" is not used in this test, we are not testing
+    # the regex engine, we are testing the ability to create SQL
+    sql_statement = table.c.foo.regexp_match("sample_pattern")
+    result = sql_statement.compile(faux_conn).string
+    expected = "REGEXP_CONTAINS(`table`.`foo`, %(foo_1:STRING)s)"
+
+    assert result == expected
+
+
+@pytest.mark.skipif(
+    packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"),
+    reason="regexp_match support requires version 1.4 or higher",
+)
+def test_visit_not_regexp_match_op_binary(faux_conn):
+    table = setup_table(
+        faux_conn,
+        "table",
+        sqlalchemy.Column("foo", sqlalchemy.String),
+    )
+
+    # NOTE: "sample_pattern" is not used in this test, we are not testing
+    # the regex engine, we are testing the ability to create SQL
+    sql_statement = not_(table.c.foo.regexp_match("sample_pattern"))
+    result = sql_statement.compile(faux_conn).string
+    expected = "NOT REGEXP_CONTAINS(`table`.`foo`, %(foo_1:STRING)s)"
+
+    assert result == expected

From 493430a58914dc89fa5c16ea2127e4bed58bd0bf Mon Sep 17 00:00:00 2001
From: benvdh-incentro <99739432+benvdh-incentro@users.noreply.github.com>
Date: Thu, 30 Mar 2023 13:59:40 +0200
Subject: [PATCH 10/25] fix: ensure correct alter table alter column statement
 is generated on data type changes in alembic (#845)

* #471 - Add identifier_preparer attribute to BigQueryDialect

* #471 - Add support for data type changes in alembic migrations

* #471 - Update alter_column signature on alembic support doc page.

* #471 - Move alembic code back to base.py as it makes alembic system test fail

* #471 - Add system test for altering column types

* Update sqlalchemy_bigquery/base.py

* Update sqlalchemy_bigquery/base.py

* Update sqlalchemy_bigquery/base.py

* Update sqlalchemy_bigquery/base.py

* fixes linting errors in base.py

---------

Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
Co-authored-by: Chalmer Lowe <chalmer.lowe@gmail.com>
---
 docs/alembic.rst             |  2 +-
 sqlalchemy_bigquery/base.py  | 15 +++++++++++++++
 tests/system/test_alembic.py | 13 +++++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/docs/alembic.rst b/docs/alembic.rst
index 8b5df741..70925b1e 100644
--- a/docs/alembic.rst
+++ b/docs/alembic.rst
@@ -15,7 +15,7 @@ Supported operations:
 `add_column(table_name, column, schema=None)
 <https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.add_column>`_
 
-`alter_column(table_name, column_name, nullable=None, schema=None)
+`alter_column(table_name, column_name, nullable=None, schema=None, type_=None)
 <https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.alter_column>`_
 
 `bulk_insert(table, rows, multiinsert=True)
diff --git a/sqlalchemy_bigquery/base.py b/sqlalchemy_bigquery/base.py
index 3a55af69..1510b7f4 100644
--- a/sqlalchemy_bigquery/base.py
+++ b/sqlalchemy_bigquery/base.py
@@ -40,6 +40,7 @@
 import sqlalchemy.sql.type_api
 from sqlalchemy.exc import NoSuchTableError
 from sqlalchemy import util
+from sqlalchemy.ext.compiler import compiles
 from sqlalchemy.sql.compiler import (
     SQLCompiler,
     GenericTypeCompiler,
@@ -786,6 +787,7 @@ def __init__(
         self.credentials_info = credentials_info
         self.credentials_base64 = credentials_base64
         self.location = location
+        self.identifier_preparer = self.preparer(self)
         self.dataset_id = None
         self.list_tables_page_size = list_tables_page_size
 
@@ -1071,6 +1073,19 @@ def __init__(self, *args, **kwargs):
     pass
 else:
     from alembic.ddl import impl
+    from alembic.ddl.base import ColumnType, format_type, alter_table, alter_column
 
     class SqlalchemyBigqueryImpl(impl.DefaultImpl):
         __dialect__ = "bigquery"
+
+    @compiles(ColumnType, "bigquery")
+    def visit_column_type(element: ColumnType, compiler: DDLCompiler, **kw) -> str:
+        """Replaces the visit_column_type() function in alembic/alembic/ddl/base.py.
+        The alembic version ends in TYPE <element type>, but bigquery requires this syntax:
+        SET DATA TYPE <element type>"""
+
+        return "%s %s %s" % (  # pragma: NO COVER
+            alter_table(compiler, element.table_name, element.schema),
+            alter_column(compiler, element.column_name),
+            "SET DATA TYPE %s" % format_type(compiler, element.type_),
+        )
diff --git a/tests/system/test_alembic.py b/tests/system/test_alembic.py
index 6c4736c8..1d77da28 100644
--- a/tests/system/test_alembic.py
+++ b/tests/system/test_alembic.py
@@ -20,7 +20,7 @@
 import contextlib
 
 import pytest
-from sqlalchemy import Column, DateTime, Integer, String
+from sqlalchemy import Column, DateTime, Integer, String, Numeric
 
 import google.api_core.exceptions
 from google.cloud.bigquery import SchemaField
@@ -149,7 +149,7 @@ def test_alembic_scenario(alembic_table):
         """
     )
 
-    # The only thing we can alter about a column is we can make it
+    # One thing we can alter about a column is we can make it
     # nullable:
     op.alter_column("transactions", "amount", True)
     assert alembic_table("transactions", "schema") == [
@@ -162,3 +162,12 @@ def test_alembic_scenario(alembic_table):
     assert alembic_table("transactions").description == "Transaction log"
 
     op.drop_table("transactions")
+
+    # Another thing we can do is alter the datatype of a nullable column,
+    # if allowed by BigQuery's type coercion rules
+    op.create_table("identifiers", Column("id", Integer))
+
+    op.alter_column("identifiers", "id", type_=Numeric)
+    assert alembic_table("identifiers", "schema") == [SchemaField("id", "NUMERIC")]
+
+    op.drop_table("identifiers")

From 912fa39bc86771a7e5fe88fdc9154974d2018c2b Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Thu, 27 Apr 2023 17:47:36 +0200
Subject: [PATCH 11/25] chore(deps): update dependency certifi to v2022.12.7
 [security] (#538)

---
 samples/snippets/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 28835d1e..5b5af3ae 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1,5 +1,5 @@
 alembic==1.8.1
-certifi==2022.6.15
+certifi==2022.12.7
 charset-normalizer==2.1.1
 future==0.18.2
 geoalchemy2==0.12.5

From adfc3b122cbc560a3b234eed740bbef0f917d88e Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Thu, 25 May 2023 12:43:24 -0400
Subject: [PATCH 12/25] build(deps): bump requests from 2.28.1 to 2.31.0 in
 /synthtool/gcp/templates/python_library/.kokoro (#867)

Source-Link: https://github.com/googleapis/synthtool/commit/30bd01b4ab78bf1b2a425816e15b3e7e090993dd
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:9bc5fa3b62b091f60614c08a7fb4fd1d3e1678e326f34dd66ce1eefb5dc3267b

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .github/.OwlBot.lock.yaml | 3 ++-
 .kokoro/requirements.txt  | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index b8edda51..32b3c486 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,4 +13,5 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:2e247c7bf5154df7f98cce087a20ca7605e236340c7d6d1a14447e5c06791bd6
+  digest: sha256:9bc5fa3b62b091f60614c08a7fb4fd1d3e1678e326f34dd66ce1eefb5dc3267b
+# created: 2023-05-25T14:56:16.294623272Z
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
index 66a2172a..3b8d7ee8 100644
--- a/.kokoro/requirements.txt
+++ b/.kokoro/requirements.txt
@@ -419,9 +419,9 @@ readme-renderer==37.3 \
     --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \
     --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343
     # via twine
-requests==2.28.1 \
-    --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \
-    --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349
+requests==2.31.0 \
+    --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
+    --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
     # via
     #   gcp-releasetool
     #   google-api-core

From d7b00aadab7aef4abebf5c1328369b86e2183244 Mon Sep 17 00:00:00 2001
From: Chalmer Lowe <chalmerlowe@google.com>
Date: Wed, 31 May 2023 08:28:34 -0400
Subject: [PATCH 13/25] Adds keyword argument to test due to changes in
 sqlalchemy alembic (#868)

---
 tests/system/test_alembic.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/tests/system/test_alembic.py b/tests/system/test_alembic.py
index 1d77da28..1948a19a 100644
--- a/tests/system/test_alembic.py
+++ b/tests/system/test_alembic.py
@@ -149,9 +149,7 @@ def test_alembic_scenario(alembic_table):
         """
     )
 
-    # One thing we can alter about a column is we can make it
-    # nullable:
-    op.alter_column("transactions", "amount", True)
+    op.alter_column("transactions", "amount", nullable=True)
     assert alembic_table("transactions", "schema") == [
         SchemaField("account", "INTEGER", "REQUIRED"),
         SchemaField("transaction_time", "DATETIME", "REQUIRED"),

From 07b40f5589d02c66173e7d987f14077eb47ad94e Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Thu, 1 Jun 2023 15:56:12 +0200
Subject: [PATCH 14/25] chore(deps): update dependency requests to v2.31.0
 [security] (#864)

Co-authored-by: Anthonios Partheniou <partheniou@google.com>
---
 samples/snippets/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 5b5af3ae..48f2fc19 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -25,7 +25,7 @@ pyasn1-modules==0.2.8
 pyparsing==3.0.9
 python-dateutil==2.8.2
 pytz==2022.2.1
-requests==2.28.1
+requests==2.31.0
 rsa==4.9
 shapely==1.8.4
 six==1.16.0

From 48120bc619a5f6a954eafede4b0cb55455052ab4 Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Sat, 3 Jun 2023 19:19:10 -0400
Subject: [PATCH 15/25] build(deps): bump cryptography from 39.0.1 to 41.0.0 in
 /synthtool/gcp/templates/python_library/.kokoro (#869)

Source-Link: https://github.com/googleapis/synthtool/commit/d0f51a0c2a9a6bcca86911eabea9e484baadf64b
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:240b5bcc2bafd450912d2da2be15e62bc6de2cf839823ae4bf94d4f392b451dc

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .github/.OwlBot.lock.yaml |  4 ++--
 .kokoro/requirements.txt  | 42 +++++++++++++++++++--------------------
 2 files changed, 22 insertions(+), 24 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 32b3c486..02a4dedc 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,5 +13,5 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:9bc5fa3b62b091f60614c08a7fb4fd1d3e1678e326f34dd66ce1eefb5dc3267b
-# created: 2023-05-25T14:56:16.294623272Z
+  digest: sha256:240b5bcc2bafd450912d2da2be15e62bc6de2cf839823ae4bf94d4f392b451dc
+# created: 2023-06-03T21:25:37.968717478Z
diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt
index 3b8d7ee8..c7929db6 100644
--- a/.kokoro/requirements.txt
+++ b/.kokoro/requirements.txt
@@ -113,28 +113,26 @@ commonmark==0.9.1 \
     --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \
     --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9
     # via rich
-cryptography==39.0.1 \
-    --hash=sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4 \
-    --hash=sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f \
-    --hash=sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502 \
-    --hash=sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41 \
-    --hash=sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965 \
-    --hash=sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e \
-    --hash=sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc \
-    --hash=sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad \
-    --hash=sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505 \
-    --hash=sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388 \
-    --hash=sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6 \
-    --hash=sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2 \
-    --hash=sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac \
-    --hash=sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695 \
-    --hash=sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6 \
-    --hash=sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336 \
-    --hash=sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0 \
-    --hash=sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c \
-    --hash=sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106 \
-    --hash=sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a \
-    --hash=sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8
+cryptography==41.0.0 \
+    --hash=sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55 \
+    --hash=sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895 \
+    --hash=sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be \
+    --hash=sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928 \
+    --hash=sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d \
+    --hash=sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8 \
+    --hash=sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237 \
+    --hash=sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9 \
+    --hash=sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78 \
+    --hash=sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d \
+    --hash=sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0 \
+    --hash=sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46 \
+    --hash=sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5 \
+    --hash=sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4 \
+    --hash=sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d \
+    --hash=sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75 \
+    --hash=sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb \
+    --hash=sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2 \
+    --hash=sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be
     # via
     #   gcp-releasetool
     #   secretstorage

From ba0d93fbfbc98922f8c9b67e29514b83f6707b39 Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Fri, 9 Jun 2023 19:22:51 +0200
Subject: [PATCH 16/25] chore(deps): update all dependencies (#490)

* chore(deps): update all dependencies

* reverting urllib3 due to a compatibility issue.

Error message:

```
The conflict is caused by:
    The user requested urllib3==2.0.3
    google-auth 2.19.1 depends on urllib3<2.0
```

---------

Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
---
 dev_requirements.txt                   |  4 +--
 samples/snippets/requirements-test.txt | 14 ++++----
 samples/snippets/requirements.txt      | 48 +++++++++++++-------------
 3 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/dev_requirements.txt b/dev_requirements.txt
index 17e4e6d0..d64f143c 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -1,6 +1,6 @@
-sqlalchemy>=1.1.9,<2.0.0
+sqlalchemy>=2.0.15,<2.1.0
 google-cloud-bigquery>=1.6.0
 future==0.18.2
 pytest===6.2.5
 pytest-flake8===1.1.0  # versions 1.1.1 and above require pytest 7
-pytz==2022.2.1
+pytz==2023.3
diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt
index bd43d0d7..9857cde8 100644
--- a/samples/snippets/requirements-test.txt
+++ b/samples/snippets/requirements-test.txt
@@ -1,16 +1,16 @@
-attrs==22.1.0
+attrs==23.1.0
 click==8.1.3
-google-auth==2.11.0
+google-auth==2.19.1
 google-cloud-testutils==1.3.3
-iniconfig==1.1.1
-packaging==21.3
+iniconfig==2.0.0
+packaging==23.1
 pluggy==1.0.0
 py==1.11.0
-pyasn1==0.4.8
-pyasn1-modules==0.2.8
+pyasn1==0.5.0
+pyasn1-modules==0.3.0
 pyparsing==3.0.9
 pytest===6.2.5
 rsa==4.9
 six==1.16.0
 toml==0.10.2
-typing-extensions==4.3.0
+typing-extensions==4.6.3
diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 48f2fc19..7829956e 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1,34 +1,34 @@
-alembic==1.8.1
-certifi==2022.12.7
-charset-normalizer==2.1.1
+alembic==1.11.1
+certifi==2023.5.7
+charset-normalizer==3.1.0
 future==0.18.2
-geoalchemy2==0.12.5
-google-api-core[grpc]==2.10.0
-google-auth==2.11.0
-google-cloud-bigquery==3.3.2
+geoalchemy2==0.13.3
+google-api-core[grpc]==2.11.0
+google-auth==2.19.1
+google-cloud-bigquery==3.11.0
 google-cloud-core==2.3.2
 google-crc32c==1.5.0
-google-resumable-media==2.3.3
-googleapis-common-protos==1.56.4
-greenlet==1.1.3
-grpcio==1.48.1
-grpcio-status==1.48.1
-idna==3.3
-importlib-resources==5.9.0
-mako==1.2.2
-markupsafe==2.1.1
-packaging==21.3
-proto-plus==1.22.1
-protobuf==4.21.6
-pyasn1==0.4.8
-pyasn1-modules==0.2.8
+google-resumable-media==2.5.0
+googleapis-common-protos==1.59.0
+greenlet==2.0.2
+grpcio==1.54.2
+grpcio-status==1.54.2
+idna==3.4
+importlib-resources==5.12.0
+mako==1.2.4
+markupsafe==2.1.3
+packaging==23.1
+proto-plus==1.22.2
+protobuf==4.23.2
+pyasn1==0.5.0
+pyasn1-modules==0.3.0
 pyparsing==3.0.9
 python-dateutil==2.8.2
-pytz==2022.2.1
+pytz==2023.3
 requests==2.31.0
 rsa==4.9
-shapely==1.8.4
+shapely==2.0.1
 six==1.16.0
 sqlalchemy===1.4.27
-typing-extensions==4.3.0
+typing-extensions==4.6.3
 urllib3==1.26.12

From ba5e24487e798fd77e7db93321c374a9196293f4 Mon Sep 17 00:00:00 2001
From: Jan Doms <jan.doms@gmail.com>
Date: Mon, 12 Jun 2023 20:23:23 +0200
Subject: [PATCH 17/25] fix: remove "future" dependency (#542)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore: remove "future" dependency

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Anthonios Partheniou <partheniou@google.com>
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 dev_requirements.txt              | 1 -
 samples/snippets/requirements.txt | 1 -
 setup.py                          | 1 -
 sqlalchemy_bigquery/base.py       | 3 ---
 4 files changed, 6 deletions(-)

diff --git a/dev_requirements.txt b/dev_requirements.txt
index d64f143c..ddc53054 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -1,6 +1,5 @@
 sqlalchemy>=2.0.15,<2.1.0
 google-cloud-bigquery>=1.6.0
-future==0.18.2
 pytest===6.2.5
 pytest-flake8===1.1.0  # versions 1.1.1 and above require pytest 7
 pytz==2023.3
diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 7829956e..4c248abb 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1,7 +1,6 @@
 alembic==1.11.1
 certifi==2023.5.7
 charset-normalizer==3.1.0
-future==0.18.2
 geoalchemy2==0.13.3
 google-api-core[grpc]==2.11.0
 google-auth==2.19.1
diff --git a/setup.py b/setup.py
index 026013aa..0ed6037d 100644
--- a/setup.py
+++ b/setup.py
@@ -102,7 +102,6 @@ def readme():
         "google-cloud-bigquery>=2.25.2,<4.0.0dev",
         "packaging",
         "sqlalchemy>=1.2.0,<2.0.0dev",
-        "future",
     ],
     extras_require=extras,
     python_requires=">=3.7, <3.12",
diff --git a/sqlalchemy_bigquery/base.py b/sqlalchemy_bigquery/base.py
index 1510b7f4..188cca1c 100644
--- a/sqlalchemy_bigquery/base.py
+++ b/sqlalchemy_bigquery/base.py
@@ -19,9 +19,6 @@
 
 """Integration between SQLAlchemy and BigQuery."""
 
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
 from decimal import Decimal
 import random
 import operator

From 94d113dcb0b763c74ef56842d1fbb0b046edfd11 Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Tue, 13 Jun 2023 20:14:57 +0200
Subject: [PATCH 18/25] chore(deps): update all dependencies (#870)

* chore(deps): update all dependencies

* Update samples/snippets/requirements.txt

---------

Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
---
 samples/snippets/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 4c248abb..50592df3 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -8,7 +8,7 @@ google-cloud-bigquery==3.11.0
 google-cloud-core==2.3.2
 google-crc32c==1.5.0
 google-resumable-media==2.5.0
-googleapis-common-protos==1.59.0
+googleapis-common-protos==1.59.1
 greenlet==2.0.2
 grpcio==1.54.2
 grpcio-status==1.54.2

From 6a4b7ece2cf114638d01c62b9fbc913dcf94c66d Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Tue, 27 Jun 2023 10:11:03 -0400
Subject: [PATCH 19/25] chore: remove pinned Sphinx version [autoapprove]
 (#873)

Source-Link: https://github.com/googleapis/synthtool/commit/909573ce9da2819eeb835909c795d29aea5c724e
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:ddf4551385d566771dc713090feb7b4c1164fb8a698fe52bbe7670b24236565b

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .github/.OwlBot.lock.yaml | 4 ++--
 noxfile.py                | 3 +--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 02a4dedc..1b3cb6c5 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,5 +13,5 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:240b5bcc2bafd450912d2da2be15e62bc6de2cf839823ae4bf94d4f392b451dc
-# created: 2023-06-03T21:25:37.968717478Z
+  digest: sha256:ddf4551385d566771dc713090feb7b4c1164fb8a698fe52bbe7670b24236565b
+# created: 2023-06-27T13:04:21.96690344Z
diff --git a/noxfile.py b/noxfile.py
index a6c38339..e9b1e619 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -442,12 +442,11 @@ def docfx(session):
 
     session.install("-e", ".")
     session.install(
-        "sphinx==4.0.1",
+        "gcp-sphinx-docfx-yaml",
         "alabaster",
         "geoalchemy2",
         "shapely",
         "recommonmark",
-        "gcp-sphinx-docfx-yaml",
     )
 
     shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True)

From e8d1adbcc53016e494b17992c4acb18efba54e0f Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Thu, 29 Jun 2023 12:32:55 -0400
Subject: [PATCH 20/25] chore: store artifacts in placer (#874)

Source-Link: https://github.com/googleapis/synthtool/commit/cb960373d12d20f8dc38beee2bf884d49627165e
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:2d816f26f728ac8b24248741e7d4c461c09764ef9f7be3684d557c9632e46dbd

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .github/.OwlBot.lock.yaml  | 4 ++--
 .kokoro/release/common.cfg | 9 +++++++++
 noxfile.py                 | 2 +-
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index 1b3cb6c5..98994f47 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,5 +13,5 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
-  digest: sha256:ddf4551385d566771dc713090feb7b4c1164fb8a698fe52bbe7670b24236565b
-# created: 2023-06-27T13:04:21.96690344Z
+  digest: sha256:2d816f26f728ac8b24248741e7d4c461c09764ef9f7be3684d557c9632e46dbd
+# created: 2023-06-28T17:03:33.371210701Z
diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg
index 5624d4e6..0982525d 100644
--- a/.kokoro/release/common.cfg
+++ b/.kokoro/release/common.cfg
@@ -38,3 +38,12 @@ env_vars: {
   key: "SECRET_MANAGER_KEYS"
   value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem"
 }
+
+# Store the packages we uploaded to PyPI.  That way, we have a record of exactly
+# what we published, which we can use to generate SBOMs and attestations.
+action {
+  define_artifacts {
+    regex: "github/python-bigquery-sqlalchemy/**/*.tar.gz"
+    strip_prefix: "github/python-bigquery-sqlalchemy"
+  }
+}
diff --git a/noxfile.py b/noxfile.py
index e9b1e619..ee728ffe 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -519,6 +519,7 @@ def prerelease_deps(session):
         "grpcio!=1.52.0rc1",
         "grpcio-status",
         "google-api-core",
+        "google-auth",
         "proto-plus",
         "google-cloud-testutils",
         # dependencies of google-cloud-testutils"
@@ -531,7 +532,6 @@ def prerelease_deps(session):
     # Remaining dependencies
     other_deps = [
         "requests",
-        "google-auth",
     ]
     session.install(*other_deps)
 

From 436b68c4bc7b27e9238eeaa38f23bcdebdc294ac Mon Sep 17 00:00:00 2001
From: Chalmer Lowe <chalmerlowe@google.com>
Date: Mon, 10 Jul 2023 10:57:16 -0400
Subject: [PATCH 21/25] test: corrects a defect in the data supplied to a test
 (#876)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: corrects a defect in the data supplied to a test

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 tests/unit/test_geography.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/tests/unit/test_geography.py b/tests/unit/test_geography.py
index fb856dca..6924ade0 100644
--- a/tests/unit/test_geography.py
+++ b/tests/unit/test_geography.py
@@ -160,9 +160,11 @@ def test_GEOGRAPHY_ElementType_bad_extended():
 def test_GEOGRAPHY_ElementType():
     from sqlalchemy_bigquery import GEOGRAPHY, WKB
 
-    data = GEOGRAPHY.ElementType("data")
+    # The data argument here should be composed of hex characters:
+    # 1-0 and a-f
+    data = GEOGRAPHY.ElementType("123def")
     assert isinstance(data, WKB)
-    assert (data.data, data.srid, data.extended) == ("data", 4326, True)
+    assert (data.data, data.srid, data.extended) == ("123def", 4326, True)
 
 
 def test_calling_st_functions_that_dont_take_geographies(faux_conn, last_query):

From d86af9921cb6e4435849db7a83c6f7600bd889c7 Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Mon, 10 Jul 2023 17:49:28 +0200
Subject: [PATCH 22/25] chore(deps): update all dependencies (#872)

* chore(deps): update all dependencies

* Update samples/snippets/requirements.txt

---------

Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
Co-authored-by: Anthonios Partheniou <partheniou@google.com>
---
 samples/snippets/requirements-test.txt |  6 +++---
 samples/snippets/requirements.txt      | 10 +++++-----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt
index 9857cde8..ad314712 100644
--- a/samples/snippets/requirements-test.txt
+++ b/samples/snippets/requirements-test.txt
@@ -1,14 +1,14 @@
 attrs==23.1.0
 click==8.1.3
-google-auth==2.19.1
+google-auth==2.20.0
 google-cloud-testutils==1.3.3
 iniconfig==2.0.0
 packaging==23.1
-pluggy==1.0.0
+pluggy==1.1.0
 py==1.11.0
 pyasn1==0.5.0
 pyasn1-modules==0.3.0
-pyparsing==3.0.9
+pyparsing==3.1.0
 pytest===6.2.5
 rsa==4.9
 six==1.16.0
diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 50592df3..84770a41 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -2,9 +2,9 @@ alembic==1.11.1
 certifi==2023.5.7
 charset-normalizer==3.1.0
 geoalchemy2==0.13.3
-google-api-core[grpc]==2.11.0
-google-auth==2.19.1
-google-cloud-bigquery==3.11.0
+google-api-core[grpc]==2.11.1
+google-auth==2.20.0
+google-cloud-bigquery==3.11.1
 google-cloud-core==2.3.2
 google-crc32c==1.5.0
 google-resumable-media==2.5.0
@@ -18,10 +18,10 @@ mako==1.2.4
 markupsafe==2.1.3
 packaging==23.1
 proto-plus==1.22.2
-protobuf==4.23.2
+protobuf==4.23.3
 pyasn1==0.5.0
 pyasn1-modules==0.3.0
-pyparsing==3.0.9
+pyparsing==3.1.0
 python-dateutil==2.8.2
 pytz==2023.3
 requests==2.31.0

From 81212cb745e8111179c9908c663221939436089a Mon Sep 17 00:00:00 2001
From: Mend Renovate <bot@renovateapp.com>
Date: Mon, 10 Jul 2023 20:48:48 +0200
Subject: [PATCH 23/25] chore(deps): update all dependencies (#877)

* chore(deps): update all dependencies

* pin importlib-resources for python 3.7

* revert urllib3

---------

Co-authored-by: Anthonios Partheniou <partheniou@google.com>
---
 samples/snippets/requirements-test.txt |  8 ++++----
 samples/snippets/requirements.txt      | 23 ++++++++++++-----------
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt
index ad314712..4e326e01 100644
--- a/samples/snippets/requirements-test.txt
+++ b/samples/snippets/requirements-test.txt
@@ -1,10 +1,10 @@
 attrs==23.1.0
-click==8.1.3
-google-auth==2.20.0
+click==8.1.4
+google-auth==2.21.0
 google-cloud-testutils==1.3.3
 iniconfig==2.0.0
 packaging==23.1
-pluggy==1.1.0
+pluggy==1.2.0
 py==1.11.0
 pyasn1==0.5.0
 pyasn1-modules==0.3.0
@@ -13,4 +13,4 @@ pytest===6.2.5
 rsa==4.9
 six==1.16.0
 toml==0.10.2
-typing-extensions==4.6.3
+typing-extensions==4.7.1
diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt
index 84770a41..e4e437c5 100644
--- a/samples/snippets/requirements.txt
+++ b/samples/snippets/requirements.txt
@@ -1,24 +1,25 @@
 alembic==1.11.1
 certifi==2023.5.7
-charset-normalizer==3.1.0
-geoalchemy2==0.13.3
+charset-normalizer==3.2.0
+geoalchemy2==0.14.0
 google-api-core[grpc]==2.11.1
-google-auth==2.20.0
-google-cloud-bigquery==3.11.1
-google-cloud-core==2.3.2
+google-auth==2.21.0
+google-cloud-bigquery==3.11.3
+google-cloud-core==2.3.3
 google-crc32c==1.5.0
 google-resumable-media==2.5.0
 googleapis-common-protos==1.59.1
 greenlet==2.0.2
-grpcio==1.54.2
-grpcio-status==1.54.2
+grpcio==1.56.0
+grpcio-status==1.56.0
 idna==3.4
-importlib-resources==5.12.0
+importlib-resources===5.12.0; python_version == '3.7'
+importlib-resources==6.0.0; python_version >= '3.8'
 mako==1.2.4
 markupsafe==2.1.3
 packaging==23.1
-proto-plus==1.22.2
-protobuf==4.23.3
+proto-plus==1.22.3
+protobuf==4.23.4
 pyasn1==0.5.0
 pyasn1-modules==0.3.0
 pyparsing==3.1.0
@@ -29,5 +30,5 @@ rsa==4.9
 shapely==2.0.1
 six==1.16.0
 sqlalchemy===1.4.27
-typing-extensions==4.6.3
+typing-extensions==4.7.1
 urllib3==1.26.12

From 8a1f694e59f5bac5ce66d9f8a04066980d7f9893 Mon Sep 17 00:00:00 2001
From: Shachar Snapiri <snapiri@users.noreply.github.com>
Date: Tue, 11 Jul 2023 23:02:21 +0300
Subject: [PATCH 24/25] fix: Avoid aliasing known CTEs (#839)

* fix: Avoid aliasing known CTEs

* Add unit-test

---------

Co-authored-by: Tim Swast <swast@google.com>
Co-authored-by: Anthonios Partheniou <partheniou@google.com>
---
 sqlalchemy_bigquery/base.py              |  1 +
 tests/system/test_sqlalchemy_bigquery.py | 48 ++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/sqlalchemy_bigquery/base.py b/sqlalchemy_bigquery/base.py
index 188cca1c..a3496f93 100644
--- a/sqlalchemy_bigquery/base.py
+++ b/sqlalchemy_bigquery/base.py
@@ -261,6 +261,7 @@ def _known_tables(self):
             if isinstance(from_, Table):
                 known_tables.add(from_.name)
             elif isinstance(from_, CTE):
+                known_tables.add(from_.name)
                 for column in from_.original.selected_columns:
                     table = getattr(column, "table", None)
                     if table is not None:
diff --git a/tests/system/test_sqlalchemy_bigquery.py b/tests/system/test_sqlalchemy_bigquery.py
index 1baabda3..01078596 100644
--- a/tests/system/test_sqlalchemy_bigquery.py
+++ b/tests/system/test_sqlalchemy_bigquery.py
@@ -776,6 +776,54 @@ def test_unnest(engine, bigquery_dataset):
     assert sorted(r[0] for r in conn.execute(query)) == ["a", "b", "c", "x", "y"]
 
 
+@pytest.mark.skipif(
+    packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"),
+    reason="unnest (and other table-valued-function) support required version 1.4",
+)
+def test_unnest_with_cte(engine, bigquery_dataset):
+    from sqlalchemy import select, func, String
+    from sqlalchemy_bigquery import ARRAY
+
+    conn = engine.connect()
+    metadata = MetaData()
+    table_name = "test_unnest_with_cte"
+    table = Table(
+        f"{bigquery_dataset}.{table_name}",
+        metadata,
+        Column("foo", String),
+        Column("bars", ARRAY(String)),
+    )
+    metadata.create_all(engine)
+    conn.execute(
+        table.insert(),
+        [dict(foo="first", bars=["a", "b", "c"]), dict(foo="second", bars=["x", "y"])],
+    )
+    selectable = select(table.c).select_from(table).cte("cte")
+    query = select(
+        [
+            selectable.c.foo,
+            func.unnest(selectable.c.bars).column_valued("unnest_bars"),
+        ]
+    ).select_from(selectable)
+    compiled = str(query.compile(engine))
+    assert " ".join(compiled.strip().split()) == (
+        f"WITH `cte` "
+        f"AS (SELECT `{bigquery_dataset}.{table_name}`.`foo` AS `foo`,"
+        f" `{bigquery_dataset}.{table_name}`.`bars` AS `bars`"
+        f" FROM `{bigquery_dataset}.{table_name}`) "
+        f"SELECT `cte`.`foo`, `unnest_bars` "
+        f"FROM `cte`, unnest(`cte`.`bars`) AS `unnest_bars`"
+    )
+
+    assert sorted(r for r in conn.execute(query)) == [
+        ("first", "a"),
+        ("first", "b"),
+        ("first", "c"),
+        ("second", "x"),
+        ("second", "y"),
+    ]
+
+
 @pytest.mark.skipif(
     packaging.version.parse(sqlalchemy.__version__) < packaging.version.parse("1.4"),
     reason="regexp_match support requires version 1.4 or higher",

From 39a55f4c5b2532ab04be50dd6dabd1c8c82b052c Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
 <55107282+release-please[bot]@users.noreply.github.com>
Date: Tue, 11 Jul 2023 16:02:13 -0500
Subject: [PATCH 25/25] chore(main): release 1.7.0 (#744)

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
---
 CHANGELOG.md                   | 21 +++++++++++++++++++++
 sqlalchemy_bigquery/version.py |  2 +-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a280f8a..42a12b52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,27 @@ Older versions of this project were distributed as [pybigquery][0].
 [2]: https://pypi.org/project/pybigquery/#history
 
 
+## [1.7.0](https://github.com/googleapis/python-bigquery-sqlalchemy/compare/v1.6.1...v1.7.0) (2023-07-11)
+
+
+### Features
+
+* Added regexp_match operator support for bigquery ([#511](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/511)) ([fd78093](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/fd780938ca4a0d32448ed666753360f05584d2ab))
+* Remove pyarrow and bqstorage as dependencies ([#847](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/847)) ([5d6b38c](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/5d6b38c94dfef0bd0dbf5037ad21a352d2bd8e4f))
+
+
+### Bug Fixes
+
+* Avoid aliasing known CTEs ([#839](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/839)) ([8a1f694](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/8a1f694e59f5bac5ce66d9f8a04066980d7f9893))
+* Ensure correct alter table alter column statement is generated on data type changes in alembic ([#845](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/845)) ([493430a](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/493430a58914dc89fa5c16ea2127e4bed58bd0bf))
+* Remove "future" dependency ([#542](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/542)) ([ba5e244](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/ba5e24487e798fd77e7db93321c374a9196293f4))
+* Remove type annotations from _struct.py ([#733](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/733)) ([27814df](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/27814dfbea8267d1b7abbc47fbbb56bb36f3585d))
+
+
+### Documentation
+
+* Pass credentials as python dictionary ([#737](https://github.com/googleapis/python-bigquery-sqlalchemy/issues/737)) ([074321d](https://github.com/googleapis/python-bigquery-sqlalchemy/commit/074321ddaa10001773e7e6044f4a0df1bb530331))
+
 ## [1.6.1](https://github.com/googleapis/python-bigquery-sqlalchemy/compare/v1.6.0...v1.6.1) (2023-02-01)
 
 
diff --git a/sqlalchemy_bigquery/version.py b/sqlalchemy_bigquery/version.py
index b47d6225..5b3e9059 100644
--- a/sqlalchemy_bigquery/version.py
+++ b/sqlalchemy_bigquery/version.py
@@ -17,4 +17,4 @@
 # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-__version__ = "1.6.1"
+__version__ = "1.7.0"