[sqlalchemy] Re: Insert from Select Implentation

2010-02-12 Thread Ed Singleton
To partially clarify and answer my own question here (I was very tired  
by the time I pasted this last night)


In the case of:

sa 
.insert 
(mytable 
).values(myothertable.select().filter_by(foo=sa.bindparam(bar))


This doesn't currently work because the bindparam required for the  
select statement gets discarded.  Having gone through the way SA  
compiles statements, it appears that parameters are used to fill in an  
insert statements values.  The only way I can see of getting around  
this is that at the time of adding the values, they bindparams are  
extracted from the select and added to a new attribute on the insert  
statement, and around the time of creating the ExecutionContext, they  
are processed and added back into the query.


My previous question about getting both the column names and the  
bindparams, I managed to solve by (duh!) getting the column names,  
compiling the select statement and then getting the bindparams.  I  
would still be interested to know where the bindparams are stored in  
an uncompiled select though.  I couldn't work it out.


Sorry if all this is a little fuzzy.  It's my first time at really  
trying to understand what SA does behind the scenes.


Thanks

Ed


On 11 Feb 2010, at 22:23, Ed Singleton wrote:

I've been having a clumsy hack at enabling myself to pass a select  
statement as a value to an insert statement. IE:  
sa.insert(mytable).values(myothertable.select())


I've got it working in that most basic case, but I'm struggling when  
the select statement has bindparams.  The insert needs to take them,  
as well as (for some dialects) the column names of the select.  The  
thing is I can work out how to get the bindparams from a compiled  
statement, and I can work out how to get the column names from a  
uncompiled statement, but I can't work out how to get both from one  
or the other.  Any hints on this would be appreciated.


Also, I slightly worried that there's an obvious reason why this  
hasn't been done before.  Is there something stupid I'm missing?  If  
anyone's tried patching SA to do this I would greatly appreciate any  
advice.


Thanks

Ed


--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] Re: Insert from Select Implentation

2010-02-12 Thread Michael Bayer
Ed Singleton wrote:
 To partially clarify and answer my own question here (I was very tired
 by the time I pasted this last night)

 In the case of:

 sa
 .insert
 (mytable
 ).values(myothertable.select().filter_by(foo=sa.bindparam(bar))

 This doesn't currently work because the bindparam required for the
 select statement gets discarded.  Having gone through the way SA
 compiles statements, it appears that parameters are used to fill in an
 insert statements values.  The only way I can see of getting around
 this is that at the time of adding the values, they bindparams are
 extracted from the select and added to a new attribute on the insert
 statement, and around the time of creating the ExecutionContext, they
 are processed and added back into the query.

 My previous question about getting both the column names and the
 bindparams, I managed to solve by (duh!) getting the column names,
 compiling the select statement and then getting the bindparams.  I
 would still be interested to know where the bindparams are stored in
 an uncompiled select though.  I couldn't work it out.

 Sorry if all this is a little fuzzy.  It's my first time at really
 trying to understand what SA does behind the scenes.

if you're using the @compiler extension to generate this, the same
compiler object would generate the string representation of both the
insert() and the select(), gathering bindparam() objects from the
structure as it goes.  the params structure embedded on the Compiled at
the end is what gets sent to execute() along with any ad-hoc values.

The example here:
http://www.sqlalchemy.org/docs/reference/ext/compiler.html?highlight=compiler#compiling-sub-elements-of-a-custom-expression-construct
 should accomplish all this.




 Thanks

 Ed


 On 11 Feb 2010, at 22:23, Ed Singleton wrote:

 I've been having a clumsy hack at enabling myself to pass a select
 statement as a value to an insert statement. IE:
 sa.insert(mytable).values(myothertable.select())

 I've got it working in that most basic case, but I'm struggling when
 the select statement has bindparams.  The insert needs to take them,
 as well as (for some dialects) the column names of the select.  The
 thing is I can work out how to get the bindparams from a compiled
 statement, and I can work out how to get the column names from a
 uncompiled statement, but I can't work out how to get both from one
 or the other.  Any hints on this would be appreciated.

 Also, I slightly worried that there's an obvious reason why this
 hasn't been done before.  Is there something stupid I'm missing?  If
 anyone's tried patching SA to do this I would greatly appreciate any
 advice.

 Thanks

 Ed

 --
 You received this message because you are subscribed to the Google Groups
 sqlalchemy group.
 To post to this group, send email to sqlalch...@googlegroups.com.
 To unsubscribe from this group, send email to
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at
 http://groups.google.com/group/sqlalchemy?hl=en.



-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] Re: Insert from Select Implentation

2010-02-12 Thread Ed Singleton

On 12 Feb 2010, at 17:43, Michael Bayer wrote:


Ed Singleton wrote:
To partially clarify and answer my own question here (I was very  
tired

by the time I pasted this last night)

In the case of:

sa
.insert
(mytable
).values(myothertable.select().filter_by(foo=sa.bindparam(bar))

This doesn't currently work because the bindparam required for the
select statement gets discarded.  Having gone through the way SA
compiles statements, it appears that parameters are used to fill in  
an

insert statements values.  The only way I can see of getting around
this is that at the time of adding the values, they bindparams are
extracted from the select and added to a new attribute on the insert
statement, and around the time of creating the ExecutionContext, they
are processed and added back into the query.

My previous question about getting both the column names and the
bindparams, I managed to solve by (duh!) getting the column names,
compiling the select statement and then getting the bindparams.  I
would still be interested to know where the bindparams are stored in
an uncompiled select though.  I couldn't work it out.

Sorry if all this is a little fuzzy.  It's my first time at really
trying to understand what SA does behind the scenes.


if you're using the @compiler extension to generate this, the same
compiler object would generate the string representation of both the
insert() and the select(), gathering bindparam() objects from the
structure as it goes.  the params structure embedded on the Compiled  
at

the end is what gets sent to execute() along with any ad-hoc values.

The example here:
http://www.sqlalchemy.org/docs/reference/ext/compiler.html?highlight=compiler#compiling-sub-elements-of-a-custom-expression-construct
should accomplish all this.



You mean I've spent a day googling and two days reading SA source code  
and that's all I had to do?  I am both miffed and delighted at the  
same time.


I didn't know about this compiler extension and had been hacking away  
at the SA source to get it working as a default feature of insert  
statements.  I'll try this out post-haste.


Thanks again

Ed


--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] Re: Insert from Select Implentation

2010-02-12 Thread Ed Singleton

On 12 Feb 2010, at 17:43, Michael Bayer wrote:


Ed Singleton wrote:


In the case of:

sa
.insert
(mytable
).values(myothertable.select().filter_by(foo=sa.bindparam(bar))

This doesn't currently work because... [snip]

if you're using the @compiler extension to generate this, the same
compiler object would generate the string representation of both the
insert() and the select(), gathering bindparam() objects from the
structure as it goes.  the params structure embedded on the Compiled  
at

the end is what gets sent to execute() along with any ad-hoc values.

The example here:
http://www.sqlalchemy.org/docs/reference/ext/compiler.html?highlight=compiler#compiling-sub-elements-of-a-custom-expression-construct
should accomplish all this.


I've had a go at this.  I had to modify the string template slightly  
to suit sqlites insert from syntax, but otherwise pretty much copy and  
pasted from the docs. I keep getting the following error (sample  
script below):


Traceback (most recent call last):
  File lib/nm_mail/insert_from_test.py, line 58, in module
session.execute(ins)
  File /Users/singletoned/.envs/newman-mail/lib/python2.6/site- 
packages/SQLAlchemy-0.6beta1-py2.6.egg/sqlalchemy/orm/session.py,  
line 737, in execute

clause, params or {})
  File /Users/singletoned/.envs/newman-mail/lib/python2.6/site- 
packages/SQLAlchemy-0.6beta1-py2.6.egg/sqlalchemy/engine/base.py,  
line 1035, in execute

return Connection.executors[c](self, object, multiparams, params)
  File /Users/singletoned/.envs/newman-mail/lib/python2.6/site- 
packages/SQLAlchemy-0.6beta1-py2.6.egg/sqlalchemy/engine/base.py,  
line 1095, in _execute_clauseelement

parameters=params
  File /Users/singletoned/.envs/newman-mail/lib/python2.6/site- 
packages/SQLAlchemy-0.6beta1-py2.6.egg/sqlalchemy/engine/base.py,  
line 1170, in __create_execution_context
return dialect.execution_ctx_cls(dialect, connection=self,  
**kwargs)
  File /Users/singletoned/.envs/newman-mail/lib/python2.6/site- 
packages/SQLAlchemy-0.6beta1-py2.6.egg/sqlalchemy/engine/default.py,  
line 271, in __init__

raise exc.ArgumentError(Not an executable clause: %s % compiled)
sqlalchemy.exc.ArgumentError: Not an executable clause: INSERT INTO  
Foo (id, name, body) SELECT Bar.id, Bar.name, Bar.body

FROM Bar

# -*- coding: utf-8 -*-

import sqlalchemy.orm
import sqlalchemy as sa

uri = sqlite://

metadata = sa.MetaData()
engine = sa.create_engine(uri)
metadata.bind = engine
Session = sa.orm.sessionmaker(bind=engine)

foo_table = sa.Table(Foo, metadata,
sa.Column(id, sa.Integer, primary_key=True),
sa.Column(name, sa.String(64)),
sa.Column(body, sa.String),
)

bar_table = sa.Table(Bar, metadata,
sa.Column(id, sa.Integer, primary_key=True),
sa.Column(name, sa.String(64)),
sa.Column(body, sa.String),
)

metadata.create_all()

session = Session()
q = bar_table.insert()
session.execute(q.values(name=bar1, body=blah))
session.execute(q.values(name=bar2, body=blah))
session.execute(q.values(name=bar3, body=flibble))

session.commit()
session.close()

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import ClauseElement

class InsertFromSelect(ClauseElement):
def __init__(self, table, select):
self.table = table
self.select = select

@compiles(InsertFromSelect)
def visit_insert_from_select(element, compiler, **kw):
return INSERT INTO %s (%s) %s % (
compiler.process(element.table, asfrom=True),
, .join([col.name for col in element.select.columns]),
compiler.process(element.select),
)

s = bar_table.select()

ins = InsertFromSelect(foo_table, s)

print ins

session.execute(ins)
session.commit()


--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] Re: Insert from Select Implentation

2010-02-12 Thread Michael Bayer
Ed Singleton wrote:
 class InsertFromSelect(ClauseElement):
  def __init__(self, table, select):
  self.table = table
  self.select = select

 @compiles(InsertFromSelect)
 def visit_insert_from_select(element, compiler, **kw):
  return INSERT INTO %s (%s) %s % (
  compiler.process(element.table, asfrom=True),
  , .join([col.name for col in element.select.columns]),
  compiler.process(element.select),
  )



add the mixin sqlalchemy.sql.expression._Executable to your
InsertFromSelect class.I'm going to rename it to Executable and will
add it to the docs now.

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] Re: Insert from Select Implentation

2010-02-12 Thread Ed Singleton

On 12 Feb 2010, at 19:36, Michael Bayer wrote:


Ed Singleton wrote:

class InsertFromSelect(ClauseElement):
def __init__(self, table, select):
self.table = table
self.select = select

@compiles(InsertFromSelect)
def visit_insert_from_select(element, compiler, **kw):
return INSERT INTO %s (%s) %s % (
compiler.process(element.table, asfrom=True),
, .join([col.name for col in element.select.columns]),
compiler.process(element.select),
)




add the mixin sqlalchemy.sql.expression._Executable to your
InsertFromSelect class.I'm going to rename it to Executable  
and will

add it to the docs now.


Okay, I've modified it to this, and it seems to work perfectly

from sqlalchemy.sql.expression import ClauseElement, _Executable

class InsertFromSelect( _Executable, ClauseElement):
def __init__(self, table, select):
self.table = table
self.select = select

@compiles(InsertFromSelect)
def visit_insert_from_select(element, compiler, **kw):
return INSERT INTO %s (%s) %s % (
compiler.process(element.table, asfrom=True),
, .join([col.name for col in element.select.columns]),
compiler.process(element.select),
)

Thanks for all your help.  You are a king.  (If not one who leads me  
to throw away the hacking I did on SA.  And I was so close to getting  
it working).


Ed

--
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.