Hi. I'm trying to use ext.versioned, and it seems to malfunction when
working against an Oracle Database. Here's a testcase for Oracle that
fails:
from elixir import Entity, Unicode, Integer, using_options, Field,
ManyToOne, metadata, session, setup_all, create_all
from elixir.ext.versioned import acts_as_versioned
from sqlalchemy import Sequence
class Book(Entity):
using_options(tablename='books', auto_primarykey=False)
acts_as_versioned()
book_id = Field(Integer, Sequence('books_seq'), primary_key=True)
title = Field(Unicode(120))
category = Field(Unicode(120))
author = ManyToOne('Author')
class Author(Entity):
using_options(tablename='authors', auto_primarykey=False)
id = Field(Integer, Sequence('authors_seq'), primary_key=True)
name = Field(Unicode(120))
metadata.bind = "oracle://:@pearl/"
metadata.bind.echo = True
setup_all()
metadata.drop_all()
metadata.create_all()
a = Author(name='Itamar Ravid')
b = Book(title='A Test', category='Category', author=a)
session.commit()
b.category = 'A Change'
session.commit()
Here are the relevant error messages:
2008-09-29 15:05:27,205 INFO sqlalchemy.engine.base.Engine.0x..0c
BEGIN
2008-09-29 15:05:27,207 INFO sqlalchemy.engine.base.Engine.0x..0c
SELECT authors_seq.nextval FROM DUAL
2008-09-29 15:05:27,208 INFO sqlalchemy.engine.base.Engine.0x..0c {}
2008-09-29 15:05:27,215 INFO sqlalchemy.engine.base.Engine.0x..0c
INSERT INTO authors (id, name) VALUES (:id, :name)
2008-09-29 15:05:27,216 INFO sqlalchemy.engine.base.Engine.0x..0c
{'name': 'Itamar Ravid', 'id': 1}
2008-09-29 15:05:27,225 INFO sqlalchemy.engine.base.Engine.0x..0c
SELECT books_seq.nextval FROM DUAL
2008-09-29 15:05:27,226 INFO sqlalchemy.engine.base.Engine.0x..0c {}
2008-09-29 15:05:27,233 INFO sqlalchemy.engine.base.Engine.0x..0c
INSERT INTO books (book_id, title, category, author_id, version,
timestamp) VALUES
(:book_id, :title, :category, :author_id, :version, :timest
amp)
2008-09-29 15:05:27,234 INFO sqlalchemy.engine.base.Engine.0x..0c
{'category': 'Category', 'title': 'A Test', 'timestamp':
datetime.datetime(2008, 9, 29, 15, 5, 27, 224406), 'version': 1,
'book_id': 1, 'autho
r_id': 1}
2008-09-29 15:05:27,239 INFO sqlalchemy.engine.base.Engine.0x..0c
COMMIT
2008-09-29 15:05:27,244 INFO sqlalchemy.engine.base.Engine.0x..0c
BEGIN
2008-09-29 15:05:27,246 INFO sqlalchemy.engine.base.Engine.0x..0c
SELECT books.book_id, books.title, books.category, books.author_id,
books.version, books.timestamp
FROM books
WHERE books.book_id = :book_id_1
2008-09-29 15:05:27,248 INFO sqlalchemy.engine.base.Engine.0x..0c
{'book_id_1': 1}
2008-09-29 15:05:27,254 INFO sqlalchemy.engine.base.Engine.0x..0c
SELECT books_seq.nextval FROM DUAL
2008-09-29 15:05:27,255 INFO sqlalchemy.engine.base.Engine.0x..0c {}
2008-09-29 15:05:27,258 INFO sqlalchemy.engine.base.Engine.0x..0c
INSERT INTO books_history (book_id, version) VALUES
(:book_id, :version)
2008-09-29 15:05:27,259 INFO sqlalchemy.engine.base.Engine.0x..0c
{'version': None, 'book_id': 2}
2008-09-29 15:05:27,271 INFO sqlalchemy.engine.base.Engine.0x..0c
ROLLBACK
...
---------------------------------------------------------------------------
DatabaseError Traceback (most recent call
last)
/home/iravid/bla.py in <module>()
34 b.category = 'A Change'
35
---> 36 session.commit()
37
38
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/scoping.pyc in do(self, *args, **kwargs)
96 def instrument(name):
97 def do(self, *args, **kwargs):
---> 98 return getattr(self.registry(), name)(*args, **kwargs)
99 return do
100 for meth in ('add', 'add_all', 'get', 'load', 'close', 'save',
'commit', 'update', 'save_or_update', 'flush', 'query', 'delete',
'merge', 'clear', 'refresh', 'expire', 'expunge', 'rollback', 'begin',
'begin_nested', 'connection', 'execute', 'scalar', 'get_bind',
'is_modified', '__contains__', '__iter__'):
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/session.pyc in commit(self)
555 raise exceptions.InvalidRequestError("No
transaction is begun.")
556
--> 557 self.transaction.commit()
558 if self.transaction is None and self.transactional:
559 self.begin()
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/session.pyc in commit(self)
260 self._assert_is_open()
261 if not self._prepared:
--> 262 self._prepare_impl()
263
264 if self._parent is None or self.nested:
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/session.pyc in _prepare_impl(self)
244
245 if self.autoflush:
--> 246 self.session.flush()
247
248 if self._parent is None and self.session.twophase:
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/session.pyc in flush(self, objects)
787 if not objects:
788 return
--> 789 self.uow.flush(self, objects)
790
791 def get(self, class_, ident, **kwargs):
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/unitofwork.pyc in flush(self, session,
objects)
231 flush_context.transaction = session.transaction
232 try:
--> 233 flush_context.execute()
234
235 if session.extension is not None:
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/unitofwork.pyc in execute(self)
443 if self._should_log_info:
444 self.logger.info("Task dump:\n" +
self._dump(tasks))
--> 445 UOWExecutor().execute(self, tasks)
446 if self._should_log_info:
447 self.logger.info("Execute Complete")
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/unitofwork.pyc in execute(self, trans, tasks,
isdelete)
928 if isdelete is not True:
929 for task in tasks:
--> 930 self.execute_save_steps(trans, task)
931 if isdelete is not False:
932 for task in util.reversed(tasks):
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/unitofwork.pyc in execute_save_steps(self,
trans, task)
943
944 def execute_save_steps(self, trans, task):
--> 945 self.save_objects(trans, task)
946 self.execute_cyclical_dependencies(trans, task, False)
947 self.execute_per_element_childtasks(trans, task,
False)
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/unitofwork.pyc in save_objects(self, trans,
task)
934
935 def save_objects(self, trans, task):
--> 936 task.mapper._save_obj(task.polymorphic_tosave_objects,
trans)
937
938 def delete_objects(self, trans, task):
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/mapper.pyc in _save_obj(self, states,
uowtransaction, postupdate, post_update_cols, single)
1018 else:
1019 if 'before_update' in
mapper.extension.methods:
-> 1020 mapper.extension.before_update(mapper,
connection, state.obj())
1021
1022 for state, connection, has_identity in tups:
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/orm/util.pyc in _do(*args, **kwargs)
121 def _do(*args, **kwargs):
122 for elem in self.__elements:
--> 123 ret = getattr(elem, funcname)(*args, **kwargs)
124 if ret is not EXT_CONTINUE:
125 return ret
/home/iravid/pythonenv/lib/python2.5/site-packages/Elixir-0.6.1-
py2.5.egg/elixir/ext/versioned.py in before_update(self, mapper,
connection, instance)
109 dict_values = dict(old_values.items())
110 connection.execute(
--> 111
instance.__class__.__history_table__.insert(), dict_values)
112 instance.version = instance.version + 1
113 instance.timestamp = datetime.now()
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/engine/base.pyc in execute(self, object,
*multiparams, **params)
842 for c in type(object).__mro__:
843 if c in Connection.executors:
--> 844 return Connection.executors[c](self, object,
multiparams, params)
845 else:
846 raise exceptions.InvalidRequestError("Unexecutable
object type: " + str(type(object)))
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/engine/base.pyc in execute_clauseelement(self,
elem, multiparams, params)
893 else:
894 keys = None
--> 895 return
self._execute_compiled(elem.compile(dialect=self.dialect,
column_keys=keys, inline=len(params) > 1), distilled_params=params)
896
897 def _execute_compiled(self, compiled, multiparams=None,
params=None, distilled_params=None):
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/engine/base.pyc in _execute_compiled(self,
compiled, multiparams, params, distilled_params)
905
906 context.pre_execution()
--> 907 self.__execute_raw(context)
908 context.post_execution()
909 self._autocommit(context)
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/engine/base.pyc in __execute_raw(self, context)
914 self._cursor_executemany(context.cursor,
context.statement, context.parameters, context=context)
915 else:
--> 916 self._cursor_execute(context.cursor,
context.statement, context.parameters[0], context=context)
917
918 def _execute_ddl(self, ddl, params, multiparams):
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/engine/base.pyc in _cursor_execute(self, cursor,
statement, parameters, context)
958 self.dialect.do_execute(cursor, statement,
parameters, context=context)
959 except Exception, e:
--> 960 self._handle_dbapi_exception(e, statement,
parameters, cursor)
961 raise
962
/home/iravid/pythonenv/lib/python2.5/site-packages/SQLAlchemy-0.4.7p1-
py2.5.egg/sqlalchemy/engine/base.pyc in _handle_dbapi_exception(self,
e, statement, parameters, cursor)
940 if self.__close_with_result:
941 self.close()
--> 942 raise exceptions.DBAPIError.instance(statement,
parameters, e, connection_invalidated=is_disconnect)
943 finally:
944 del self._reentrant_error
DatabaseError: (DatabaseError) ORA-01400: cannot insert NULL into
("IRAVID"."BOOKS_HISTORY"."VERSION")
'INSERT INTO books_history (book_id, version) VALUES
(:book_id, :version)' {'version': None, 'book_id': 2}
WARNING: Failure executing file: <bla.py>
If you look closely at the SQL statements being sent to Oracle
preceding the stack trace, you'll notice that for some reason SA is
fetching another book_id from the relevant sequence in order to insert
into books_history, which is clearly wrong behavior.
The following, equivalent testcase, modified for MySQL, succeeds:
from elixir import Entity, Unicode, Integer, using_options, Field,
ManyToOne, metadata, session, setup_all, create_all
from elixir.ext.versioned import acts_as_versioned
class Book(Entity):
using_options(tablename='books')
acts_as_versioned()
title = Field(Unicode(120))
category = Field(Unicode(120))
author = ManyToOne('Author')
class Author(Entity):
using_options(tablename='authors')
name = Field(Unicode(120))
metadata.bind = "mysql://iravid:@localhost/books"
metadata.bind.echo = True
setup_all()
metadata.drop_all()
metadata.create_all()
a = Author(name='Itamar Ravid')
b = Book(title='A Test', category='Category', author=a)
session.commit()
b.category = 'A Change'
session.commit()
I was wondering if anyone could spot a mistake in my code, before I
open a bug in Elixir's trac.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"SQLElixir" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/sqlelixir?hl=en
-~----------~----~----~----~------~----~------~--~---