In case anyone tries to use the compile visitor below, I'll document a
couple snags I ran into. For now, I think I'm satisfied to go back to
my original patch and deal with any merge conflicts during upgrades.
The hook has false positives when it runs on inner queries that
correlate to tables from outer queries. I think these could be
addressed by borrowing most of the logic from _get_display_froms having
to do with dropping extra FROMs to make this work right, although you
would need to get the values for explicit_correlate_froms and
implicit_correlate_froms from somewhere. I'm sure it could be made to
work eventually.
Additionally, it raises even in contexts where no sql is being
executed. For example, if someone renders a yet-to-be-nested select to
a string for debugging purposes, they might get an exception. It's also
a bit confusing to the client, because if they render the selectable, it
will show them the FROM entity that it simultaneously claims it missing
(that is, if you had a way of preventing the exception in the rendering
process). Sending the bad SQL to the RDBMS avoids these two caveats.
Also, I found it necessary to use col._from_objects instead of col.table
for non-trivial column elements.
After all that, what I can suggest for you is that since you are
looking to "raise an error", that is actually easy to do here using
events or compiles. All you need to do is compare the calculated
FROM list to the explicit FROM list:
from sqlalchemy import table, column, select
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql import Select
from sqlalchemy import exc
@compiles(Select)
def _strict_froms(stmt, compiler, **kw):
actual_froms = stmt.froms
# TODO: tune this as desired
if stmt._from_obj:
# TODO: we can give you non-underscored access to
# _from_obj
legal_froms = stmt._from_obj
else:
legal_froms = set([
col.table for col in stmt.inner_columns
])
if len(legal_froms) != len(actual_froms):
raise exc.CompileError("SELECT has implicit froms")
return compiler.visit_select(stmt, **kw)
t1 = table('t1', column('a'))
t2 = table('t2', column('b'))
# works
print select([t1]).where(t2.c.b == 5).select_from(t1).select_from(t2)
# raises
print select([t1]).where(t2.c.b == 5)
The issue of "FROM with comma" *is* a problem I'd like to continue to
explore solutions towards. I've long considered various rules in
_display_froms but none have ever gotten through the various issues
I've detailed above. I hope this helps to clarify the complexity
and depth of this situation.
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.