Skip to content

Commit d766d21

Browse files
feat: support UPDATE + JOIN in BigQuery dialect (#1083)
* feat: support UPDATE JOIN * vendor code from sqlalchemy * remove code and add comments * add vendored folder to path in MANIFEST.in * remove extra code * include third_party in pytest * update MANIFEST.in * add init file * add init file * add pyproject.toml * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * update noxfile and owlbot * lint * fix owlbot * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * undo changes for testing * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * add comments to pyproject.toml --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 5d58038 commit d766d21

File tree

15 files changed

+157
-4
lines changed

15 files changed

+157
-4
lines changed

MANIFEST.in

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616

1717
# Generated by synthtool. DO NOT EDIT!
1818
include README.rst LICENSE
19-
recursive-include google *.json *.proto py.typed
19+
recursive-include third_party/sqlalchemy_bigquery_vendored *
20+
recursive-include sqlalchemy_bigquery *.json *.proto py.typed
2021
recursive-include tests *
2122
global-exclude *.py[co]
2223
global-exclude __pycache__

noxfile.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@
3131
FLAKE8_VERSION = "flake8==6.1.0"
3232
BLACK_VERSION = "black[jupyter]==23.7.0"
3333
ISORT_VERSION = "isort==5.11.0"
34-
LINT_PATHS = ["docs", "sqlalchemy_bigquery", "tests", "noxfile.py", "setup.py"]
34+
LINT_PATHS = [
35+
"third_party",
36+
"docs",
37+
"sqlalchemy_bigquery",
38+
"tests",
39+
"noxfile.py",
40+
"setup.py",
41+
]
3542

3643
DEFAULT_PYTHON_VERSION = "3.8"
3744

owlbot.py

+21
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""This script is used to synthesize generated parts of this library."""
1616

1717
import pathlib
18+
import re
1819

1920
import synthtool as s
2021
from synthtool import gcp
@@ -76,13 +77,24 @@
7677
"import re\nimport shutil",
7778
)
7879

80+
s.replace(
81+
["noxfile.py"],
82+
"LINT_PATHS = \[",
83+
"LINT_PATHS = [\"third_party\", "
84+
)
7985

8086
s.replace(
8187
["noxfile.py"],
8288
"--cov=google",
8389
"--cov=sqlalchemy_bigquery",
8490
)
8591

92+
s.replace(
93+
["noxfile.py"],
94+
"""os.path.join("tests", "unit"),""",
95+
"""os.path.join("tests", "unit"),
96+
os.path.join("third_party", "sqlalchemy_bigquery_vendored"),""",
97+
)
8698

8799
s.replace(
88100
["noxfile.py"],
@@ -284,6 +296,15 @@ def system_noextras(session):
284296
""",
285297
)
286298

299+
300+
# Make sure build includes all necessary files.
301+
s.replace(
302+
["MANIFEST.in"],
303+
re.escape("recursive-include google"),
304+
"""recursive-include third_party/sqlalchemy_bigquery_vendored *
305+
recursive-include sqlalchemy_bigquery""",
306+
)
307+
287308
# ----------------------------------------------------------------------------
288309
# Samples templates
289310
# ----------------------------------------------------------------------------

pyproject.toml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Added so third_party folder is included when running `pip install -e .`
2+
# See PR #1083 for more detail
3+
[build-system]
4+
requires = ["setuptools"]
5+
build-backend = "setuptools.build_meta"

setup.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import itertools
2323
import os
2424
import re
25+
import setuptools
2526
from setuptools import setup
2627

2728
# Package metadata.
@@ -67,6 +68,16 @@ def readme():
6768

6869
extras["all"] = set(itertools.chain.from_iterable(extras.values()))
6970

71+
packages = [
72+
package
73+
for package in setuptools.find_namespace_packages()
74+
if package.startswith("sqlalchemy_bigquery")
75+
] + [
76+
package
77+
for package in setuptools.find_namespace_packages("third_party")
78+
if package.startswith("sqlalchemy_bigquery_vendored")
79+
]
80+
7081
setup(
7182
name=name,
7283
version=version,
@@ -75,7 +86,11 @@ def readme():
7586
long_description_content_type="text/x-rst",
7687
author="The Sqlalchemy-Bigquery Authors",
7788
author_email="[email protected]",
78-
packages=["sqlalchemy_bigquery"],
89+
package_dir={
90+
"sqlalchemy-bigquery": "sqlalchemy_bigquery",
91+
"sqlalchemy_bigquery_vendored": "third_party/sqlalchemy_bigquery_vendored",
92+
},
93+
packages=packages,
7994
url="https://github.com/googleapis/python-bigquery-sqlalchemy",
8095
keywords=["bigquery", "sqlalchemy"],
8196
classifiers=[

sqlalchemy_bigquery/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
from .parse_url import parse_url
6262
from . import _helpers, _struct, _types
63+
import sqlalchemy_bigquery_vendored.sqlalchemy.postgresql.base as vendored_postgresql
6364

6465
# Illegal characters is intended to be all characters that are not explicitly
6566
# allowed as part of the flexible column names.
@@ -189,7 +190,7 @@ def pre_exec(self):
189190
)
190191

191192

192-
class BigQueryCompiler(_struct.SQLCompiler, SQLCompiler):
193+
class BigQueryCompiler(_struct.SQLCompiler, vendored_postgresql.PGCompiler):
193194
compound_keywords = SQLCompiler.compound_keywords.copy()
194195
compound_keywords[selectable.CompoundSelect.UNION] = "UNION DISTINCT"
195196
compound_keywords[selectable.CompoundSelect.UNION_ALL] = "UNION ALL"

tests/unit/test_compiler.py

+29
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,35 @@ def prepare_implicit_join_base_query(
161161
return q
162162

163163

164+
# Test vendored method update_from_clause()
165+
# from sqlalchemy_bigquery_vendored.sqlalchemy.postgresql.base.PGCompiler
166+
def test_update_from_clause(faux_conn, metadata):
167+
table1 = setup_table(
168+
faux_conn,
169+
"table1",
170+
metadata,
171+
sqlalchemy.Column("foo", sqlalchemy.String),
172+
sqlalchemy.Column("bar", sqlalchemy.Integer),
173+
)
174+
table2 = setup_table(
175+
faux_conn,
176+
"table2",
177+
metadata,
178+
sqlalchemy.Column("foo", sqlalchemy.String),
179+
sqlalchemy.Column("bar", sqlalchemy.Integer),
180+
)
181+
182+
stmt = (
183+
sqlalchemy.update(table1)
184+
.where(table1.c.foo == table2.c.foo)
185+
.where(table2.c.bar == 1)
186+
.values(bar=2)
187+
)
188+
expected_sql = "UPDATE `table1` SET `bar`=%(bar:INT64)s FROM `table2` WHERE `table1`.`foo` = `table2`.`foo` AND `table2`.`bar` = %(bar_1:INT64)s"
189+
found_sql = stmt.compile(faux_conn).string
190+
assert found_sql == expected_sql
191+
192+
164193
@sqlalchemy_before_2_0
165194
def test_no_implicit_join_asterix_for_inner_unnest_before_2_0(faux_conn, metadata):
166195
# See: https://github.com/googleapis/python-bigquery-sqlalchemy/issues/368

third_party/__init__.py

Whitespace-only changes.

third_party/sqlalchemy_bigquery_vendored/__init__.py

Whitespace-only changes.

third_party/sqlalchemy_bigquery_vendored/py.typed

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
SQLAlchemy was created by Michael Bayer.
2+
3+
Major contributing authors include:
4+
5+
- Mike Bayer
6+
- Jason Kirtland
7+
- Michael Trier
8+
- Diana Clarke
9+
- Gaetan de Menten
10+
- Lele Gaifax
11+
- Jonathan Ellis
12+
- Gord Thompson
13+
- Federico Caselli
14+
- Philip Jenvey
15+
- Rick Morrison
16+
- Chris Withers
17+
- Ants Aasma
18+
- Sheila Allen
19+
- Paul Johnston
20+
- Tony Locke
21+
- Hajime Nakagami
22+
- Vraj Mohan
23+
- Robert Leftwich
24+
- Taavi Burns
25+
- Jonathan Vanasco
26+
- Jeff Widman
27+
- Scott Dugas
28+
- Dobes Vandermeer
29+
- Ville Skytta
30+
- Rodrigo Menezes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright 2005-2024 SQLAlchemy authors and contributors <see AUTHORS file>.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the "Software"), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7+
of the Software, and to permit persons to whom the Software is furnished to do
8+
so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# __init__.py
2+
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3+
# <see AUTHORS file>
4+
#
5+
# This module is part of SQLAlchemy and is released under
6+
# the MIT License: https://www.opensource.org/licenses/mit-license.php

third_party/sqlalchemy_bigquery_vendored/sqlalchemy/postgresql/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# dialects/postgresql/base.py
2+
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3+
# <see AUTHORS file>
4+
#
5+
# This module is part of SQLAlchemy and is released under
6+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
7+
# mypy: ignore-errors
8+
9+
from sqlalchemy.sql import compiler
10+
11+
12+
class PGCompiler(compiler.SQLCompiler):
13+
def update_from_clause(
14+
self, update_stmt, from_table, extra_froms, from_hints, **kw
15+
):
16+
kw["asfrom"] = True
17+
return "FROM " + ", ".join(
18+
t._compiler_dispatch(self, fromhints=from_hints, **kw) for t in extra_froms
19+
)

0 commit comments

Comments
 (0)