[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

Reply via email to