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