Dan -

The ORM idea is interesting. we sort of have that now with the "using" thing, but i see where youre going there. I dont have a strong reaction to it yet, other than it would seriously rearrange things, and also that the style might already be possible with some extensions that wrap around mappers. Also I think SQLEngines are already a lot more flexible than I think you are giving them credit for...my proposal tried to address that. Keep in mind that Tables in fact only need a SchemaEngine object, which is nothing more than a dictionary of Tables. its analgous to a Table the way a Table is analgous to a Column; it is its natural container (and you almost dont even need the SchemaEngine, if you work with lexically- constructed TableClause objects). As far as decoupling Schema/ Engine, well maybe. ive thought a lot about that but have yet to see how it doenst unnecessarily complicate the average use case.

With regards to using just tables and SQL statements, without any discussion about the ORM, is this what we're talking about ?

changing this:

        t = Table('mytable', e, columns...)

        t.select().execute()

to this:

      t = Table('mytable', columns...)

      something.get_connection().execute(t.select())

?

(i think both styles should be supported. style #1 was largely a result of my looking at SQLObject and i think has helped SA a lot to use a pattern that was already popular, which includes the ProxyEngine too).

(i had more to say on all of this but i think its too early in the day for that.)


On Apr 4, 2006, at 11:53 PM, Daniel Miller wrote:

[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



-------------------------------------------------------
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