diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-06-16 18:41:54 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-06-16 18:41:54 -0400 |
commit | 3ebb7d8e942ded31efbdb052ae3a1f08a6fc3d3d (patch) | |
tree | 6f5494b3732689c822a6b77891c6cc622322e206 | |
parent | 2528595802a7900e1a61570d7228bd445b5b7f3f (diff) | |
download | sqlalchemy-3ebb7d8e942ded31efbdb052ae3a1f08a6fc3d3d.tar.gz |
- [bug] The ResultProxy methods inserted_primary_key,
last_updated_params(), last_inserted_params(),
postfetch_cols(), prefetch_cols() all
assert that the given statement is a compiled
construct, and is an insert() or update()
statement as is appropriate, else
raise InvalidRequestError. [ticket:2498]
- ResultProxy.last_inserted_ids is removed,
replaced by inserted_primary_key.
-rw-r--r-- | CHANGES | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 97 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/default.py | 2 | ||||
-rw-r--r-- | test/engine/test_execute.py | 54 | ||||
-rw-r--r-- | test/lib/requires.py | 8 |
5 files changed, 156 insertions, 16 deletions
@@ -247,6 +247,17 @@ CHANGES the task of specifying in-Python ForeignKey or ForeignKeyConstraint declarations. + - [bug] The ResultProxy methods inserted_primary_key, + last_updated_params(), last_inserted_params(), + postfetch_cols(), prefetch_cols() all + assert that the given statement is a compiled + construct, and is an insert() or update() + statement as is appropriate, else + raise InvalidRequestError. [ticket:2498] + + - ResultProxy.last_inserted_ids is removed, + replaced by inserted_primary_key. + - sql - [feature] The Inspector object can now be acquired using the new inspect() service, diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 8d819f5c6..d13344ff6 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -601,6 +601,10 @@ class ExecutionContext(object): should_autocommit True if the statement is a "committable" statement. + prefetch_cols + a list of Column objects for which a client-side default + was fired off. Applies to inserts and updates. + postfetch_cols a list of Column objects for which a server-side default or inline SQL expression value was fired off. Applies to inserts @@ -3005,7 +3009,8 @@ class ResultProxy(object): where it is appropriate. It's behavior is not consistent across backends. - Usage of this method is normally unnecessary; the + Usage of this method is normally unnecessary when + using insert() expression constructs; the :attr:`~ResultProxy.inserted_primary_key` attribute provides a tuple of primary key values for a newly inserted row, regardless of database backend. @@ -3107,30 +3112,46 @@ class ResultProxy(object): supports "returning" and the insert statement executed with the "implicit returning" enabled. + Raises :class:`.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() construct. + """ - if not self.context.isinsert: + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert: raise exc.InvalidRequestError( - "Statement is not an insert() expression construct.") + "Statement is not an insert() " + "expression construct.") elif self.context._is_explicit_returning: raise exc.InvalidRequestError( - "Can't call inserted_primary_key when returning() " + "Can't call inserted_primary_key " + "when returning() " "is used.") return self.context.inserted_primary_key - @util.deprecated("0.6", "Use :attr:`.ResultProxy.inserted_primary_key`") - def last_inserted_ids(self): - """Return the primary key for the row just inserted.""" - - return self.inserted_primary_key - def last_updated_params(self): """Return the collection of updated parameters from this execution. + Raises :class:`.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an update() construct. + """ - if self.context.executemany: + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isupdate: + raise exc.InvalidRequestError( + "Statement is not an update() " + "expression construct.") + elif self.context.executemany: return self.context.compiled_parameters else: return self.context.compiled_parameters[0] @@ -3139,30 +3160,74 @@ class ResultProxy(object): """Return the collection of inserted parameters from this execution. + Raises :class:`.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() construct. + """ - if self.context.executemany: + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert: + raise exc.InvalidRequestError( + "Statement is not an insert() " + "expression construct.") + elif self.context.executemany: return self.context.compiled_parameters else: return self.context.compiled_parameters[0] def lastrow_has_defaults(self): """Return ``lastrow_has_defaults()`` from the underlying - ExecutionContext. + :class:`.ExecutionContext`. - See ExecutionContext for details. + See :class:`.ExecutionContext` for details. + """ return self.context.lastrow_has_defaults() def postfetch_cols(self): - """Return ``postfetch_cols()`` from the underlying ExecutionContext. + """Return ``postfetch_cols()`` from the underlying :class:`.ExecutionContext`. - See ExecutionContext for details. + See :class:`.ExecutionContext` for details. + + Raises :class:`.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() or update() construct. + """ + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert and not self.context.isupdate: + raise exc.InvalidRequestError( + "Statement is not an insert() or update() " + "expression construct.") return self.context.postfetch_cols def prefetch_cols(self): + """Return ``prefetch_cols()`` from the underlying :class:`.ExecutionContext`. + + See :class:`.ExecutionContext` for details. + + Raises :class:`.InvalidRequestError` if the executed + statement is not a compiled expression construct + or is not an insert() or update() construct. + + """ + + if not self.context.compiled: + raise exc.InvalidRequestError( + "Statement is not a compiled " + "expression construct.") + elif not self.context.isinsert and not self.context.isupdate: + raise exc.InvalidRequestError( + "Statement is not an insert() or update() " + "expression construct.") return self.context.prefetch_cols def supports_sane_rowcount(self): diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index b0d2ab946..7b115b805 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -354,6 +354,8 @@ class DefaultExecutionContext(base.ExecutionContext): result_map = None compiled = None statement = None + postfetch_cols = None + prefetch_cols = None _is_implicit_returning = False _is_explicit_returning = False diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index 7ccd42b73..2b6dd1c09 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -896,6 +896,60 @@ class ResultProxyTest(fixtures.TestBase): writer.writerow(row) assert s.getvalue().strip() == '1,Test' + @testing.requires.selectone + def test_empty_accessors(self): + statements = [ + ( + "select 1", + [ + lambda r: r.last_inserted_params(), + lambda r: r.last_updated_params(), + lambda r: r.prefetch_cols(), + lambda r: r.postfetch_cols(), + lambda r : r.inserted_primary_key + ], + "Statement is not a compiled expression construct." + ), + ( + select([1]), + [ + lambda r: r.last_inserted_params(), + lambda r : r.inserted_primary_key + ], + r"Statement is not an insert\(\) expression construct." + ), + ( + select([1]), + [ + lambda r: r.last_updated_params(), + ], + r"Statement is not an update\(\) expression construct." + ), + ( + select([1]), + [ + lambda r: r.prefetch_cols(), + lambda r : r.postfetch_cols() + ], + r"Statement is not an insert\(\) " + r"or update\(\) expression construct." + ), + ] + + for stmt, meths, msg in statements: + r = testing.db.execute(stmt) + try: + for meth in meths: + assert_raises_message( + tsa.exc.InvalidRequestError, + msg, + meth, r + ) + + finally: + r.close() + + class AlternateResultProxyTest(fixtures.TestBase): __requires__ = ('sqlite', ) diff --git a/test/lib/requires.py b/test/lib/requires.py index 2ac939112..9b9244eae 100644 --- a/test/lib/requires.py +++ b/test/lib/requires.py @@ -436,3 +436,11 @@ def english_locale_on_postgresql(fn): skip_if(lambda: testing.against('postgresql') \ and not testing.db.scalar('SHOW LC_COLLATE').startswith('en')) ) + +def selectone(fn): + """target driver must support the literal statement 'select 1'""" + return _chain_decorators_on( + fn, + skip_if(lambda: testing.against('oracle'), + "non-standard SELECT scalar syntax") + ) |