Skip to content

Commit a2e711c

Browse files
committed
fix: synchronize compiler guards with current driver capabilities
1 parent 6ba8a67 commit a2e711c

2 files changed

Lines changed: 94 additions & 3 deletions

File tree

src/sqlalchemy_excel/compiler.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66
Supported:
77
SELECT columns FROM table [WHERE ...] [GROUP BY ...] [HAVING ...] [ORDER BY col [ASC|DESC]] [LIMIT n] [OFFSET n]
88
SELECT DISTINCT columns FROM table [WHERE ...] ...
9-
SELECT cols FROM t1 [INNER|LEFT|RIGHT] JOIN t2 ON ... { JOIN t3 ON ... } [WHERE ...] [ORDER BY ...] [LIMIT n] [OFFSET n]
9+
SELECT cols FROM t1 [INNER|LEFT|RIGHT|FULL OUTER] JOIN t2 ON ... { JOIN t3 ON ... } [WHERE ...] [ORDER BY ...] [LIMIT n] [OFFSET n]
10+
SELECT cols FROM t1 CROSS JOIN t2 [WHERE ...] [ORDER BY ...] [LIMIT n] [OFFSET n]
11+
UNION / UNION ALL / INTERSECT / EXCEPT compound SELECT
1012
INSERT INTO table (cols) VALUES (vals), (vals2), ...
1113
INSERT INTO table (cols) SELECT cols FROM source [WHERE ...]
1214
UPDATE table SET col=val [WHERE ...]
1315
DELETE FROM table [WHERE ...]
1416
1517
Rejected (raises CompileError):
16-
CTEs, window functions, RETURNING, FOR UPDATE,
17-
NATURAL JOIN, non-equality/OR/non-column ON clauses
18+
CTEs, window functions, aggregate FILTER, RETURNING, FOR UPDATE,
19+
NATURAL JOIN, non-equality/OR/non-column ON clauses,
20+
DISTINCT with JOIN, grouped/nested compound branches
1821
1922
Partially supported:
2023
non-correlated subqueries in WHERE ... IN (SELECT single_col FROM table [WHERE ...])

tests/test_e2e.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,94 @@ def test_e2e_join_left(tmp_path) -> None:
380380
engine.dispose()
381381

382382

383+
def test_e2e_right_join_raw_sql(tmp_path) -> None:
384+
engine = _engine_for(tmp_path)
385+
metadata = MetaData()
386+
users = _users_table(metadata)
387+
orders = Table(
388+
"orders",
389+
metadata,
390+
Column("id", Integer, primary_key=True),
391+
Column("user_id", Integer),
392+
Column("amount", Integer),
393+
)
394+
metadata.create_all(engine)
395+
396+
with engine.begin() as conn:
397+
conn.execute(insert(users).values(id=1, name="Alice", age=30))
398+
conn.execute(insert(users).values(id=2, name="Bob", age=25))
399+
conn.execute(insert(orders).values(id=1, user_id=1, amount=100))
400+
401+
with engine.connect() as conn:
402+
rows = conn.execute(
403+
sa.text(
404+
"SELECT users.name, orders.amount "
405+
"FROM users RIGHT JOIN orders ON users.id = orders.user_id"
406+
)
407+
).all()
408+
assert rows == [("Alice", 100)]
409+
410+
engine.dispose()
411+
412+
413+
def test_e2e_full_outer_join_raw_sql(tmp_path) -> None:
414+
engine = _engine_for(tmp_path)
415+
metadata = MetaData()
416+
users = _users_table(metadata)
417+
orders = Table(
418+
"orders",
419+
metadata,
420+
Column("id", Integer, primary_key=True),
421+
Column("user_id", Integer),
422+
Column("amount", Integer),
423+
)
424+
metadata.create_all(engine)
425+
426+
with engine.begin() as conn:
427+
conn.execute(insert(users).values(id=1, name="Alice", age=30))
428+
conn.execute(insert(users).values(id=2, name="Bob", age=25))
429+
conn.execute(insert(orders).values(id=1, user_id=1, amount=100))
430+
conn.execute(insert(orders).values(id=2, user_id=999, amount=200))
431+
432+
with engine.connect() as conn:
433+
rows = conn.execute(
434+
sa.text(
435+
"SELECT users.name, orders.amount "
436+
"FROM users FULL OUTER JOIN orders ON users.id = orders.user_id"
437+
)
438+
).all()
439+
assert set(rows) == {("Alice", 100), ("Bob", None), (None, 200)}
440+
441+
engine.dispose()
442+
443+
444+
def test_e2e_cross_join_raw_sql(tmp_path) -> None:
445+
engine = _engine_for(tmp_path)
446+
metadata = MetaData()
447+
users = _users_table(metadata)
448+
teams = Table(
449+
"teams",
450+
metadata,
451+
Column("id", Integer, primary_key=True),
452+
Column("name", String),
453+
)
454+
metadata.create_all(engine)
455+
456+
with engine.begin() as conn:
457+
conn.execute(insert(users), [{"id": 1, "name": "Alice", "age": 30}, {"id": 2, "name": "Bob", "age": 25}])
458+
conn.execute(insert(teams), [{"id": 1, "name": "A"}, {"id": 2, "name": "B"}])
459+
460+
with engine.connect() as conn:
461+
rows = conn.execute(
462+
sa.text(
463+
"SELECT users.id, teams.id FROM users CROSS JOIN teams ORDER BY users.id, teams.id"
464+
)
465+
).all()
466+
assert rows == [(1, 1), (1, 2), (2, 1), (2, 2)]
467+
468+
engine.dispose()
469+
470+
383471
def test_e2e_subquery_in_where(tmp_path) -> None:
384472
engine = _engine_for(tmp_path)
385473
metadata = MetaData()

0 commit comments

Comments
 (0)