[Michael Bayer]
[Daniel Miller]
The engine only knows how to
make new connections, it does not maintain a reference to the
connection (the Session does that), and it loses it's execute() method
(that's the Connection's job). The engine then becomes a stateless
object to assist the mapper in constructing database-specific SQL
statements, which is what Ian Bicking wanted. This is how I see that API:
conn = engine.connect()
trans = conn.begin()
conn.execute("<raw sql>")
conn.execute(engine.create(mytable))
trans.commit()
Actually, I said that wrong: as long as an engine is associated with a database
it is a stateful object. What I'm really looking for is to have Mapper and
Table decoupled from the engine.
OK that example is not entirely clear to me, do I now have to take every
ClauseElement construction and send it to a specific "conn" every time
now ? I.e. I can no longer say:
select([mytable]).execute()
since theres no "connection" present there ? are we looking to just
add an "execute(statement)" method to the existing "SQLSession" object,
allowing this:
engine.connect().execute(select([mytable])) ?
If you are looking for a stateless object to construct database-specific
SQL statements, there is the Compiler object which has been there all
along, and after Ian's SQLAPI announcement i made some minor changes to
support SQL construction without any engine
Well, that's not quite the point. The problem is how Connection, Engine, Table,
and Mapper are coupled. Connection is at the root of the chain, which means I
can only execute queries from a given mapper/table on a single
engine/connection at runtime. This is unnecessarily constrained. I should be
able to configure my tables/mappers separately from my engines/connections.
They only need to interact at runtime, and a given mapper could interact with
multiple engines. I'd like to be able to do this:
class User(object):
...
user_table = Table(<columns only>)
User.mapper = mapper(User, user_table, properties=...)
# notice, no engine in sight
# later
engine = create_engine(...)
conn = engine.connect()
users = conn.execute(User.mapper.select(uid=25))
Since the connection is created by the engine, it knows about the particular
SQL dialect of the engine. The mapper has no need to know this information.
I don't understand why Table and Mapper need to be tied to an engine instance.
IMHO Mapper is doing more than it should: currently it is a stateful object
since it is tied to an engine. ProxyEngine is a hack that allows the choice of
engine implementation to be deferred until just before mapper statements need
to be executed.
A mapper is an abstract SQL statement generator. Since SQL statements are
stateless, the mapper can also be stateless. Mapper should do two things:
1. produce an abstract representation of SQL suitable for translation by an
engine
2. map the results of that SQL onto objects
Mapper should not execute SQL, and it should not know who to call to execute
SQL. It should be possible to pass a given mapper query to any engine.
I just focused on Mapper in the above example, but the same applies to Table as
well.
Now this creates a problem: all code that wants to use Mappers must have a
reference to the engine. This is where objectstore.Session comes in. The
session is where mappers and engines meet:
s = objectstore.get_session()
s.bind(engine, mapper).select(...)
# bind can also create a mapper on-the-fly
s.bind(engine, User, user_table).select(...)
Notice that it would be possible to bind a single mapper to more than one
engine.
The "bind" method would return a Query object that would have all of the select, get,
count, etc. methods that currently reside on Mapper. It would also be possible to use a monkeypatch
function like assign_mapper to "bind" those Query functions onto a mapper or class like
this:
bind_mapper(engine, mapper, session_factory)
bind_mapper(engine, User, user_table, session_factory)
mapper.select(...)
User.select(...)
User.mapper.select(...)
This would decorate the mapper with functions that know which engine to use and
how to get the current session. The session factory could implement the
thread-local pattern by default, but it's pluggable so any implementation could
be used.
....that SA did not allow
"connectionless SQL" has always been a strawman from the start. Theres
the ProxyEngine, and theres No Engine. There hasnt really been any
interest in pure SQL generation without ever executing it either, but
the general ability is there, introduced at
http://www.sqlalchemy.org/docs/metadata.myt#metadata_adapting_primitives.
As an example, I dont have MS SQL Server installed at all but am able to
run the SQL construction unit test with the new MSSQL compiler.
To summarize, I'm not really concerned with generating "connectionless SQL".
I'm more concerned with keeping my Tables and Mappers stateless and decoupled from the
engine. This will allow modules to import mapped classes without first configuring an
engine. It also allows queries generated by a single single Mapper instance to be
executed on separate engines without changing the state of the mapper.
I know this is big, but I think it's worth it. Maybe you have other ideas about
how this could be done. I'm definitely interested to hear your thoughts. Don't
feel rushed to respond to this. I know you're busy with many other things (BTW,
how do you do it? you must be going crazy with all this activity).
~ Daniel
-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users