I've narrowed the whole thing down to a bug in SA. The following
fails:
booksTable = Table("books", meta, autoload=True)
booksTable.insert({'BOOK_ID': 200}).execute()
...whilst the following doesn't:
booksTable.insert({'book_id': 200}).execute()
I'll forward this to the SA mailinglist.
On Sep 29, 3:10 pm, Itamar Ravid <[EMAIL PROTECTED]> wrote:
> 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
-~----------~----~----~----~------~----~------~--~---