Re: [sqlalchemy] Memory leaks in DecimalResultProcessor

2010-11-19 Thread Michael Bayer

On Nov 19, 2010, at 11:19 PM, Taavi Burns wrote:

> We recently ported our app to 0.6.4, and enabled cextensions.  Our
> app's memory usage ballooned, and HTTP response times with it due to
> the extra load on the garbage collector.  Turns out the
> DecimalResultProcessor cextension code is a bit leaky!  :)
> 
> I've opened ticket http://www.sqlalchemy.org/trac/ticket/1978
> including a patch that plugs those leaks and includes tests to verify
> that they're plugged.  The changeset is also available at
> https://bitbucket.org/taavi_burns/sqlalchemy/changeset/c90e0cfdf9cc
> for review.
> 
> I was considering marking the new tests with
> @testing.requires.cextensions since nobody should expect the Python
> version to leak like this.  Thoughts?

oh i like that test, yeah just run the decimal processor 50 times, sure.   Yeah 
you can mark that with @requires.cextensions and you can push that to tip on 
hg.sqlalchemy.org, thanks !


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



[sqlalchemy] Memory leaks in DecimalResultProcessor

2010-11-19 Thread Taavi Burns
We recently ported our app to 0.6.4, and enabled cextensions.  Our
app's memory usage ballooned, and HTTP response times with it due to
the extra load on the garbage collector.  Turns out the
DecimalResultProcessor cextension code is a bit leaky!  :)

I've opened ticket http://www.sqlalchemy.org/trac/ticket/1978
including a patch that plugs those leaks and includes tests to verify
that they're plugged.  The changeset is also available at
https://bitbucket.org/taavi_burns/sqlalchemy/changeset/c90e0cfdf9cc
for review.

I was considering marking the new tests with
@testing.requires.cextensions since nobody should expect the Python
version to leak like this.  Thoughts?

-- 
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] Longer tuple passed to query.get doesn't throw exception

2010-11-19 Thread Michael Bayer

On Nov 19, 2010, at 1:06 PM, Eoghan Murray wrote:

> With a class with 3 primary key columns, e.g.
> 
>class MyClass(Entity):
>   a = Field(Unicode(64), primary_key=True)
>   b = Field(Unicode(64), primary_key=True)
>   c = Field(Unicode(64), primary_key=True)
>   ...
> 
> If you pass 4 values to query.get;
> 
>MyClass.get(('a_val', 'b_val', 'c_val', 'd_val'))
> 
> it silently ignores 'd_val' and constructs a query with just a, b and
> c columns.
> I don't think this behaviour is desirable!  It currently throws an
> exception if a 2-tuple is passed to it.
> 
> Fix against trunk attached.

sure thing this is ticket #1977, should be good once tests are added.


> 
> In my case I had inadvertently masked the 'd' column:
> 
>class MyClass(Entity):
>   a = Field(Unicode(64), primary_key=True)
>   b = Field(Unicode(64), primary_key=True)
>   c = Field(Unicode(64), primary_key=True)
>   d = Field(Unicode(64), primary_key=True)
>   ...
>   d = Field(Unicode(64))
> 
> So that MyClass.table.primary_key.columns only had a, b and c.  Having
> the MyClass.get only accepting 3-tuples would have caught this error.
> Side issue - this example uses elixir - should elixir have caught the
> redeclaration of 'd'?

There's no way elixir could catch that, Python gives you access to the 
just-created class with its final list of attributes.   The original "d" is 
gone.   Unless Elixir kept track of every Field() object created, kept a strong 
reference to it, and then raised "hey there's an extra Field here !", seems 
pretty intrusive overkill to me...possible recipe for memory leaks too.



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



[sqlalchemy] Re: Weird error when adding a new object

2010-11-19 Thread Alvaro Reinoso
Got it. I just change session.add(screenGroup) to
session.merge(screenGroup) and it works.

Thank you for you help, it's always really useful.

On Nov 19, 3:10 pm, Michael Bayer  wrote:
> The most suspicious thing is self.__dict__['foo'] = list() as well as the 
> direct access to self.__dict__['foo'].  If "self" is a mapped object, then 
> thats your issue.
>
> Also note this is still an out of context cut-and-paste, if you really want 
> me to figure it out it has to be an isolated test script.
>
> On Nov 19, 2010, at 2:49 PM, Alvaro Reinoso wrote:
>
> > Ok, This is the code:
>
> >   �...@staticmethod
> >    def insert(string, mode):
> >            """Insert a new item given a string"""
> >            screenGroup = ScreenGroup()
> >            session = rdb.Session()
>
> >            try:
> >                    if mode == "XML":
> >                            screenGroup.fromXML(string)
> >                    elif mode == "JSON":
> >                            screenGroup.fromJSON(string)
> >                    if screenGroup.id == None:
> >                            session.add(screenGroup)
> >                    else:
> >                            screenGroup = session.merge(screenGroup)
>
> >                    session.flush()
> >                    result = "Successful:" + str(screenGroup.id) + "," +
> > screenGroup.title
> >                    return result
> >            except Exception, e:
> >                    log.warn("::ScreenGroupManager, insert > The error is " 
> > + str(e))
> >                    return "Error connecting to the database. Please 
> > try again
> > later."
>
> >            return "Error connecting to the database. Please try again
> > later."
>
> > This is the function where it gets the error. When I already have the
> > item and I merge It works out. However, if it's a new item, breaks.
> > This anothe fromXML function:
>
> >    def fromXML(self, data):
> >            """Unserializes this class from an XML formatted file or 
> > string."""
> >            root = ""
> >            try:
> >                    if(data.endswith(".xml")):
> >                            doc = etree.parse(data)
> >                            root = doc.getroot()
> >                    else:
> >                            root = etree.XML(data)
> >            except etree.XMLSyntaxError, e:
> >                    print e
> >                    pass
>
> >            if not type(root) == type(etree.XML("test")):
> >                    return
>
> >            if root.tag == "screenGroup":
> >                    for child in root.iterchildren():
> >                            if child.tag == "screens" or child.tag == 
> > "screenGroups":
> >                                    if child.text:
> >                                            items = child.text.split(",")
> >                                            self.__dict__[child.tag] = list()
> >                                            for itemId in items:
> >                                                    if child.tag == 
> > "screens":
> >                                                            item = 
> > ScreenManager.getScreenById(itemId)
> >                                                    else:
> >                                                            item = 
> > ScreenGroupManager.getGroupScreenById(itemId)
> >                                                    
> > self.__dict__[child.tag].append(item)
> >                            else:
> >                                    self.setAttr(child.tag, child.text)
>
> > ScreenManager.getScreenById(itemId) and
> > ScreenGroupManager.getGroupScreenById(itemId) just get the item from
> > the database (session.query(Screen).get(int(itemId))).
>
> > This is the XML string : Group 10 > title>4,3 > screenGroup>
>
> > Thanks!
>
> > On Nov 18, 7:07 pm, Michael Bayer  wrote:
> >> looks fine to me, would need full stack trace + single script file of 
> >> runnable, reproducing code, thanks.
>
> >> On Nov 18, 2010, at 6:37 PM, Alvaro Reinoso wrote:
>
> >>> Hello,
>
> >>> When trying to add a new item doing:
>
> >>> session = session.add(mediaGroup)
>
> >>> I got this error:
>
> >>> Module sqlalchemy.orm.attributes:834 in get_collection
> >>>   return getattr(user_data, '_sa_adapter')
> >>>   class GenericBackrefExtension(interfaces.AttributeExtension):>>
> >>> return getattr(user_data, '_sa_adapter')
>
> >>> "AttributeError: 'list' object has no attribute '_sa_adapter'"
>
> >>> This object's class:
>
> >>> class ScreenGroup(rdb.Model):
> >>>    """The ScreenGroup is a class derived from ScreenGroup, it is used to
> >>> control users within a group"""
> >>>    rdb.metadata(metadata)
> >>>    rdb.tablename("screen_groups")
>
> >>>    id = Column("id", Integer, primary_key=True)
> >>>    title = Column("title", String(100))
> >>>    parents = Column("parents", String(512))
>
> >>>    screens = relationship("Screen", secondary=group_screens,
> >>> order_by="Screen.title", backref="screen_g

Re: [sqlalchemy] Longer tuple passed to query.get doesn't throw exception

2010-11-19 Thread Michael Bayer

On Nov 19, 2010, at 1:06 PM, Eoghan Murray wrote:

> With a class with 3 primary key columns, e.g.
> 
>class MyClass(Entity):
>   a = Field(Unicode(64), primary_key=True)
>   b = Field(Unicode(64), primary_key=True)
>   c = Field(Unicode(64), primary_key=True)
>   ...
> 
> If you pass 4 values to query.get;
> 
>MyClass.get(('a_val', 'b_val', 'c_val', 'd_val'))
> 
> it silently ignores 'd_val' and constructs a query with just a, b and
> c columns.
> I don't think this behaviour is desirable!  It currently throws an
> exception if a 2-tuple is passed to it.
> 
> Fix against trunk attached.

sure thing this is ticket #1977, should be good once tests are added.


> 
> In my case I had inadvertently masked the 'd' column:
> 
>class MyClass(Entity):
>   a = Field(Unicode(64), primary_key=True)
>   b = Field(Unicode(64), primary_key=True)
>   c = Field(Unicode(64), primary_key=True)
>   d = Field(Unicode(64), primary_key=True)
>   ...
>   d = Field(Unicode(64))
> 
> So that MyClass.table.primary_key.columns only had a, b and c.  Having
> the MyClass.get only accepting 3-tuples would have caught this error.
> Side issue - this example uses elixir - should elixir have caught the
> redeclaration of 'd'?

There's no way elixir could catch that, Python gives you access to the 
just-created class with its final list of attributes.   The original "d" is 
gone.   Unless Elixir kept track of every Field() object created, kept a strong 
reference to it, and then raised "hey there's an extra Field here !", seems 
pretty intrusive overkill to me and a recipe for memory leaks.


-- 
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: Assigning to a deferred column

2010-11-19 Thread Michael Bayer

On Nov 19, 2010, at 1:41 PM, Eoghan Murray wrote:

> I guess I can't disable the mutability flag?

not if you're changing the value "inline".  If you assign a new value, then you 
don't need "mutability".

> 
> From reading up on mutability: 
> http://www.sqlalchemy.org/docs/core/types.html#sqlalchemy.types.MutableType
> I think I understand why it's required to lookup the previous value;
> so that the orm can decide whether to construct an update query for
> the instance when a flush event occurs (is that correct?).

The ORM uses attribute set events to flag changes.It then uses the 
"previous" value, if present, to determine if a change has taken place at flush 
time, and if so issues an UPDATE.   If "previous" is not present, in most cases 
we don't need to load it, and the UPDATE is unconditional.

With a mutable structure where the user expects inline changes to take place, 
the task is more complex, since there is no "set" event.  We have no choice but 
to immediately copy the structure on load so that we can detect this change.   
An eventual solution to this would be that we instrument the mutable structure 
itself to flag change events on occurrence, but this is a very involved 
enhancement.


> Should there be a shortcut for column assignment, so that rather than
> looking up the previous value, the column is flagged as 'mutated'?

If by "column assignment" you mean direct assignment of the attribute, i.e. 
myobject.foo = {newvalue}, that is universally considered to be an event.   We 
take the previous value and store it in the object's state, if we have it.   If 
we don't have it, in most cases we don't go back to the DB to load it - the 
column receives an UPDATE unconditionally in that case.In the case of 
mutable, its assumed that we already have it.

> If that was the case you'd get a spurious update if the old_value
> happens to be equal to the new_value, but shouldn't it be up to the
> app programmer to do this check?
> It seems to me that there's an unnecessary lookup of the old value
> when it is being replaced wholesale,

like i said, if we don't have the "old_value", in most cases we don't bother 
loading it, the UPDATE is unconditional.  If we do have the "old value" we use 
it.We are required to load the "old value" on change if the target is a 
collection, its a scalar attribute that is part of the object's primary key 
(I'll leave it as an exercise why that is), or a many-to-one with a non-simple 
join condition or which is using "orphan" detection, otherwise its not loaded.  
 

> which I cannot stop even though
> I've marked the column as deferred.

so here is the magic word, "deferred".  mutable + deferred has never been 
considered.Ticket #1976 is added to evaluate if we can skip the "load" when 
the col is deferred and the value wasn't loaded.  Seems to work so far but 
needs more tests and this would probably be in 0.7.

Note however that this "SELECT" is nowheres near the central inefficiency of a 
mutable type.  The central inefficiency is that we need to scan the whole 
session on every flush and compare every mutable value to its old one.   Your 
usage of "deferred" doesn't help that too much since if you are modifying the 
item in place, we still have to store it twice and compare on every flush.
"mutable" is not a flag you'd ever see within 100 yards of any of my 
applications until we work out a way to instrument the values.



> 
> All the best,
> 
> Eoghan
> 
> On Nov 4, 1:47 pm, Michael Bayer  wrote:
>> is this a mutable type like PickleType?  the previous value should not be 
>> loaded otherwise (assuming at least a recent 0.5 or 0.6 version).  If so, 
>> the mutability flag should be disabled.
>> 
>> On Nov 4, 2010, at 7:05 AM, Eoghan Murray wrote:
>> 
>> 
>> 
>>> I have the following:
>> 
>>>objs =
>>> MyTable.query.options(defer(Table.potentially_very_long_str_column)).all()
>>>for obj in objs:
>>> obj.potentially_very_long_str_column = ''
>> 
>>> At the moment, I'm seeing the assignment in the loop issue the `SELECT
>>> potentially_very_long_str_column`, even though that column will be
>>> overwritten.
>> 
>>> I pdb'd this down to orm/state.py::InstanceState.modified_event()
>>> where the previous value is stored in self.committed_state
>>> I'm ignorant as to what self.committed_state is used for, but maybe
>>> rather than evaluating 'previous' value, it could be further deferred
>>> until it is needed?  This is assuming that self.committed_state will
>>> only be used up until the session is flushed (at which time the
>>> previous value would be lost)..
>> 
>>> At the moment, is there any other way of writing to a column without
>>> first reading it using the orm?
>>> Haven't checked, but I assume  sql.expression.update  doesn't have
>>> this behaviour..
>> 
>>> Cheers!
>> 
>>> Eoghan
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send ema

Re: [sqlalchemy] Re: Weird error when adding a new object

2010-11-19 Thread Michael Bayer
The most suspicious thing is self.__dict__['foo'] = list() as well as the 
direct access to self.__dict__['foo'].  If "self" is a mapped object, then 
thats your issue.

Also note this is still an out of context cut-and-paste, if you really want me 
to figure it out it has to be an isolated test script.


On Nov 19, 2010, at 2:49 PM, Alvaro Reinoso wrote:

> Ok, This is the code:
> 
>   @staticmethod
>   def insert(string, mode):
>   """Insert a new item given a string"""
>   screenGroup = ScreenGroup()
>   session = rdb.Session()
> 
>   try:
>   if mode == "XML":
>   screenGroup.fromXML(string)
>   elif mode == "JSON":
>   screenGroup.fromJSON(string)
>   if screenGroup.id == None:
>   session.add(screenGroup)
>   else:
>   screenGroup = session.merge(screenGroup)
> 
>   session.flush()
>   result = "Successful:" + str(screenGroup.id) + "," +
> screenGroup.title
>   return result
>   except Exception, e:
>   log.warn("::ScreenGroupManager, insert > The error is " 
> + str(e))
>   return "Error connecting to the database. Please 
> try again
> later."
> 
>   return "Error connecting to the database. Please try again
> later."
> 
> This is the function where it gets the error. When I already have the
> item and I merge It works out. However, if it's a new item, breaks.
> This anothe fromXML function:
> 
>   def fromXML(self, data):
>   """Unserializes this class from an XML formatted file or 
> string."""
>   root = ""
>   try:
>   if(data.endswith(".xml")):
>   doc = etree.parse(data)
>   root = doc.getroot()
>   else:
>   root = etree.XML(data)
>   except etree.XMLSyntaxError, e:
>   print e
>   pass
> 
>   if not type(root) == type(etree.XML("test")):
>   return
> 
>   if root.tag == "screenGroup":
>   for child in root.iterchildren():
>   if child.tag == "screens" or child.tag == 
> "screenGroups":
>   if child.text:
>   items = child.text.split(",")
>   self.__dict__[child.tag] = 
> list()
>   for itemId in items:
>   if child.tag == 
> "screens":
>   item = 
> ScreenManager.getScreenById(itemId)
>   else:
>   item = 
> ScreenGroupManager.getGroupScreenById(itemId)
>   
> self.__dict__[child.tag].append(item)
>   else:
>   self.setAttr(child.tag, child.text)
> 
> ScreenManager.getScreenById(itemId) and
> ScreenGroupManager.getGroupScreenById(itemId) just get the item from
> the database (session.query(Screen).get(int(itemId))).
> 
> This is the XML string : Group 10 title>4,3 screenGroup>
> 
> Thanks!
> 
> On Nov 18, 7:07 pm, Michael Bayer  wrote:
>> looks fine to me, would need full stack trace + single script file of 
>> runnable, reproducing code, thanks.
>> 
>> On Nov 18, 2010, at 6:37 PM, Alvaro Reinoso wrote:
>> 
>>> Hello,
>> 
>>> When trying to add a new item doing:
>> 
>>> session = session.add(mediaGroup)
>> 
>>> I got this error:
>> 
>>> Module sqlalchemy.orm.attributes:834 in get_collection
>>>   return getattr(user_data, '_sa_adapter')
>>>   class GenericBackrefExtension(interfaces.AttributeExtension):>>
>>> return getattr(user_data, '_sa_adapter')
>> 
>>> "AttributeError: 'list' object has no attribute '_sa_adapter'"
>> 
>>> This object's class:
>> 
>>> class ScreenGroup(rdb.Model):
>>>"""The ScreenGroup is a class derived from ScreenGroup, it is used to
>>> control users within a group"""
>>>rdb.metadata(metadata)
>>>rdb.tablename("screen_groups")
>> 
>>>id = Column("id", Integer, primary_key=True)
>>>title = Column("title", String(100))
>>>parents = Column("parents", String(512))
>> 
>>>screens = relationship("Screen", secondary=group_screens,
>>> order_by="Screen.title", backref="screen_groups")
>>>screenGroups = relationship("ScreenGroup",
>>> secondary=screen_group_groups, order_by="ScreenGroup.title",
>>>primaryjoin=lambda: ScreenGroup.id ==
>>> screen_group_groups.c.screen_

[sqlalchemy] Re: Weird error when adding a new object

2010-11-19 Thread Alvaro Reinoso
Ok, This is the code:

@staticmethod
def insert(string, mode):
"""Insert a new item given a string"""
screenGroup = ScreenGroup()
session = rdb.Session()

try:
if mode == "XML":
screenGroup.fromXML(string)
elif mode == "JSON":
screenGroup.fromJSON(string)
if screenGroup.id == None:
session.add(screenGroup)
else:
screenGroup = session.merge(screenGroup)

session.flush()
result = "Successful:" + str(screenGroup.id) + "," +
screenGroup.title
return result
except Exception, e:
log.warn("::ScreenGroupManager, insert > The error is " 
+ str(e))
return "Error connecting to the database. Please 
try again
later."

return "Error connecting to the database. Please try again
later."

This is the function where it gets the error. When I already have the
item and I merge It works out. However, if it's a new item, breaks.
This anothe fromXML function:

def fromXML(self, data):
"""Unserializes this class from an XML formatted file or 
string."""
root = ""
try:
if(data.endswith(".xml")):
doc = etree.parse(data)
root = doc.getroot()
else:
root = etree.XML(data)
except etree.XMLSyntaxError, e:
print e
pass

if not type(root) == type(etree.XML("test")):
return

if root.tag == "screenGroup":
for child in root.iterchildren():
if child.tag == "screens" or child.tag == 
"screenGroups":
if child.text:
items = child.text.split(",")
self.__dict__[child.tag] = 
list()
for itemId in items:
if child.tag == 
"screens":
item = 
ScreenManager.getScreenById(itemId)
else:
item = 
ScreenGroupManager.getGroupScreenById(itemId)

self.__dict__[child.tag].append(item)
else:
self.setAttr(child.tag, child.text)

ScreenManager.getScreenById(itemId) and
ScreenGroupManager.getGroupScreenById(itemId) just get the item from
the database (session.query(Screen).get(int(itemId))).

This is the XML string : Group 104,3

Thanks!

On Nov 18, 7:07 pm, Michael Bayer  wrote:
> looks fine to me, would need full stack trace + single script file of 
> runnable, reproducing code, thanks.
>
> On Nov 18, 2010, at 6:37 PM, Alvaro Reinoso wrote:
>
> > Hello,
>
> > When trying to add a new item doing:
>
> > session = session.add(mediaGroup)
>
> > I got this error:
>
> > Module sqlalchemy.orm.attributes:834 in get_collection
> >   return getattr(user_data, '_sa_adapter')
> >   class GenericBackrefExtension(interfaces.AttributeExtension):>>
> > return getattr(user_data, '_sa_adapter')
>
> > "AttributeError: 'list' object has no attribute '_sa_adapter'"
>
> > This object's class:
>
> > class ScreenGroup(rdb.Model):
> >    """The ScreenGroup is a class derived from ScreenGroup, it is used to
> > control users within a group"""
> >    rdb.metadata(metadata)
> >    rdb.tablename("screen_groups")
>
> >    id = Column("id", Integer, primary_key=True)
> >    title = Column("title", String(100))
> >    parents = Column("parents", String(512))
>
> >    screens = relationship("Screen", secondary=group_screens,
> > order_by="Screen.title", backref="screen_groups")
> >    screenGroups = relationship("ScreenGroup",
> > secondary=screen_group_groups, order_by="ScreenGroup.title",
> >            primaryjoin=lambda: ScreenGroup.id ==
> > screen_group_groups.c.screen_groupA_id,
> >            secondaryjoin=lambda: ScreenGroup.id ==
> > screen_group_groups.c.screen_groupB_id,
> >            backref="screen_groups")
>
> > Thanks in advance!
>
> > --
> > 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 option

[sqlalchemy] Re: Assigning to a deferred column

2010-11-19 Thread Eoghan Murray
Thanks for the reply, yes it is a mutable type:

import jsonpickle

class JsonType(types.MutableType, types.TypeDecorator):
impl = types.Unicode

def process_bind_param(self, value, engine):
return unicode(jsonpickle.encode(value))

def process_result_value(self, value, engine):
if value:
return jsonpickle.decode(value)
else:
return {}

I guess I can't disable the mutability flag?

>From reading up on mutability: 
>http://www.sqlalchemy.org/docs/core/types.html#sqlalchemy.types.MutableType
I think I understand why it's required to lookup the previous value;
so that the orm can decide whether to construct an update query for
the instance when a flush event occurs (is that correct?).
Should there be a shortcut for column assignment, so that rather than
looking up the previous value, the column is flagged as 'mutated'?
If that was the case you'd get a spurious update if the old_value
happens to be equal to the new_value, but shouldn't it be up to the
app programmer to do this check?
It seems to me that there's an unnecessary lookup of the old value
when it is being replaced wholesale, which I cannot stop even though
I've marked the column as deferred.

All the best,

Eoghan

On Nov 4, 1:47 pm, Michael Bayer  wrote:
> is this a mutable type like PickleType?  the previous value should not be 
> loaded otherwise (assuming at least a recent 0.5 or 0.6 version).  If so, the 
> mutability flag should be disabled.
>
> On Nov 4, 2010, at 7:05 AM, Eoghan Murray wrote:
>
>
>
> > I have the following:
>
> >    objs =
> > MyTable.query.options(defer(Table.potentially_very_long_str_column)).all()
> >    for obj in objs:
> >         obj.potentially_very_long_str_column = ''
>
> > At the moment, I'm seeing the assignment in the loop issue the `SELECT
> > potentially_very_long_str_column`, even though that column will be
> > overwritten.
>
> > I pdb'd this down to orm/state.py::InstanceState.modified_event()
> > where the previous value is stored in self.committed_state
> > I'm ignorant as to what self.committed_state is used for, but maybe
> > rather than evaluating 'previous' value, it could be further deferred
> > until it is needed?  This is assuming that self.committed_state will
> > only be used up until the session is flushed (at which time the
> > previous value would be lost)..
>
> > At the moment, is there any other way of writing to a column without
> > first reading it using the orm?
> > Haven't checked, but I assume  sql.expression.update  doesn't have
> > this behaviour..
>
> > Cheers!
>
> > Eoghan

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



[sqlalchemy] Longer tuple passed to query.get doesn't throw exception

2010-11-19 Thread Eoghan Murray
With a class with 3 primary key columns, e.g.

class MyClass(Entity):
   a = Field(Unicode(64), primary_key=True)
   b = Field(Unicode(64), primary_key=True)
   c = Field(Unicode(64), primary_key=True)
   ...

If you pass 4 values to query.get;

MyClass.get(('a_val', 'b_val', 'c_val', 'd_val'))

it silently ignores 'd_val' and constructs a query with just a, b and
c columns.
I don't think this behaviour is desirable!  It currently throws an
exception if a 2-tuple is passed to it.

Fix against trunk attached.

In my case I had inadvertently masked the 'd' column:

class MyClass(Entity):
   a = Field(Unicode(64), primary_key=True)
   b = Field(Unicode(64), primary_key=True)
   c = Field(Unicode(64), primary_key=True)
   d = Field(Unicode(64), primary_key=True)
   ...
   d = Field(Unicode(64))

So that MyClass.table.primary_key.columns only had a, b and c.  Having
the MyClass.get only accepting 3-tuples would have caught this error.
Side issue - this example uses elixir - should elixir have caught the
redeclaration of 'd'?

Cheers!

Eoghan


diff -r 0d9a1a57caac lib/sqlalchemy/orm/query.py
--- a/lib/sqlalchemy/orm/query.py   Thu Nov 18 20:12:24 2010 -0500
+++ b/lib/sqlalchemy/orm/query.py   Fri Nov 19 18:04:59 2010 +
@@ -1919,6 +1919,12 @@
 q = self._clone()

 if ident is not None:
+if len(ident) != len(mapper.primary_key):
+raise sa_exc.InvalidRequestError(
+"Incorrect number of values in identifier to
formulate "
+"primary key for query.get(); primary key columns are
%s" %
+','.join("'%s'" % c for c in
mapper.primary_key))
+
 (_get_clause, _get_params) = mapper._get_clause

 # None present in ident - turn those comparisons
@@ -1939,12 +1945,6 @@
 for id_val, primary_key in zip(ident,
mapper.primary_key)
 ])

-if len(params) != len(mapper.primary_key):
-raise sa_exc.InvalidRequestError(
-"Incorrect number of values in identifier to
formulate "
-"primary key for query.get(); primary key columns are
%s" %
-','.join("'%s'" % c for c in mapper.primary_key))
-
 q._params = params

 if lockmode is not None:

-- 
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] Mapping via multiple joined tables

2010-11-19 Thread Michael Bayer

On Nov 19, 2010, at 6:37 AM, Mene wrote:

> Hi,
> 
> I want an voting application, for a huge building. Each user has one
> room at each floor of the building. The room is shared with other
> users and may change over time. Each user has one vote for each room
> he is in (so also one per level) to vote for things like the color the
> room shall be painted in, his favorite canvas, preferred music etc.
> The number of rooms and the number of users is expected to be giant,
> but the number of votes is probably relatively low, also in the future
> there will be other voting options.

This schema doesn't illustrate the association of a user/photo combination with 
a particular room.  I see table names "tile_user" and "photo" which are not 
illustrated here.  So I can't answer how user.photo_votes[room_name] would be 
accessed.   Also there's some terminology breakdown here, are you using the 
term "photo" to describe things like "the color the room shall be painted in", 
"his favorite canvas" ?  

I don't see the need for room_user.room_user_id as far as allowing particular 
mutations - natural primary keys don't prohibit anything as long as you have ON 
UPDATE CASCADE available.   If the reason is that you just want surrogate 
primary keys, then you should have one for "room" as well otherwise it's 
inconsistent.

Its fine to have tables which associate users with all their possible rooms, 
and all possible photos with a room.  Storing "user x voted for photo y in room 
z" would need a "fact" table, however, along the lines of:

user_votes = Table('user_votes', metadata,
   Column('user_id', Integer, ForeignKey('user.id', ondelete='CASCADE'), 
primary_key=True),
   Column('photo_id', Integer, ForeignKey('photo.id',ondelete='CASCADE'), 
primary_key=True)
   Column('room_name', Unicode(8), ForeignKey('room.room_name', 
onupdate="CASCADE"), primary_key=True)
)



> 
> 
> I want the following database layout
> 
> // Simple user table
> user_table = Table('user', metadata,
>Column('user_id', Integer, primary_key=True)
> )
> 
> // Simple room table
> room_table = Table('room', metadata,
>Column('room_name', Unicode(8), primary_key=True)
> )
> 
> // Table associating a user with a room.
> // The surrogate key is needed, because users may move to other rooms
> and I want the database to be
> // referential integer
> room_user_table = Table('room_user', metadata,
>Column('room_user_id', Integer, primary_key=True),
>Column('room_name', Unicode(8), ForeignKey('room.room_name')),
>Column('user_id', Integer, ForeignKey('user.user_id',
> ondelete='CASCADE'))
> )
> 
> // Defines which photos are available for this room
> room_photo_table = Table('room_photo', metadata,
>Column('room_name', Unicode(8), ForeignKey('room.room_name'),
> primary_key=True),
>Column('photo', Integer, ForeignKey('photo.id',
> ondelete='CASCADE'), primary_key=True)
> )
> 
> // Stores the votes of each user
> photo_vote_table = Table('photo_vote', metadata,
>Column('tile_user', Integer, ForeignKey('tile_user.id',
> ondelete='CASCADE'), primary_key=True),
>Column('photo_id', Integer, ForeignKey('photo.id',
> ondelete='CASCADE'))
> )
> 
> 
> I need to access the votes from python like this:
> user.photo_votes[room_name] = photo
> This should work for read and write access.
> 
> I'd get it working if I'd drop the photo_vote_table and store the
> photo_id in the room_user_table, however, this would lead to a very
> sparse database and I'll be in need to add more and more fields to
> room_user_table, as more voting options become available.
> 
> The mapping for this looked like:
> 
> user_mapper = mapper(User, user_table,
>'_photos_votes_dict':relation(RoomUser, lazy=True, cascade="all,
> delete" \
>   ,
> collection_class=column_mapped_collection(room_user_table.c.room_name)
>   )
> })
> 
> class User(object):
>photos_votes = association_proxy('_photos_votes_dict', 'photo')
> 
> (the rest is trivial)
> 
> I hope I mentioned everything and you understood me ;D
> 
> Greetings and thanks for your time,
> Mene
> 
> -- 
> 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.



[sqlalchemy] Mapping via multiple joined tables

2010-11-19 Thread Mene
Hi,

I want an voting application, for a huge building. Each user has one
room at each floor of the building. The room is shared with other
users and may change over time. Each user has one vote for each room
he is in (so also one per level) to vote for things like the color the
room shall be painted in, his favorite canvas, preferred music etc.
The number of rooms and the number of users is expected to be giant,
but the number of votes is probably relatively low, also in the future
there will be other voting options.


I want the following database layout

// Simple user table
user_table = Table('user', metadata,
Column('user_id', Integer, primary_key=True)
)

// Simple room table
room_table = Table('room', metadata,
Column('room_name', Unicode(8), primary_key=True)
)

// Table associating a user with a room.
// The surrogate key is needed, because users may move to other rooms
and I want the database to be
// referential integer
room_user_table = Table('room_user', metadata,
Column('room_user_id', Integer, primary_key=True),
Column('room_name', Unicode(8), ForeignKey('room.room_name')),
Column('user_id', Integer, ForeignKey('user.user_id',
ondelete='CASCADE'))
)

// Defines which photos are available for this room
room_photo_table = Table('room_photo', metadata,
Column('room_name', Unicode(8), ForeignKey('room.room_name'),
primary_key=True),
Column('photo', Integer, ForeignKey('photo.id',
ondelete='CASCADE'), primary_key=True)
)

// Stores the votes of each user
photo_vote_table = Table('photo_vote', metadata,
Column('tile_user', Integer, ForeignKey('tile_user.id',
ondelete='CASCADE'), primary_key=True),
Column('photo_id', Integer, ForeignKey('photo.id',
ondelete='CASCADE'))
)


I need to access the votes from python like this:
user.photo_votes[room_name] = photo
This should work for read and write access.

I'd get it working if I'd drop the photo_vote_table and store the
photo_id in the room_user_table, however, this would lead to a very
sparse database and I'll be in need to add more and more fields to
room_user_table, as more voting options become available.

The mapping for this looked like:

user_mapper = mapper(User, user_table,
'_photos_votes_dict':relation(RoomUser, lazy=True, cascade="all,
delete" \
   ,
collection_class=column_mapped_collection(room_user_table.c.room_name)
   )
})

class User(object):
photos_votes = association_proxy('_photos_votes_dict', 'photo')

(the rest is trivial)

I hope I mentioned everything and you understood me ;D

Greetings and thanks for your time,
Mene

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