summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-06-16 18:41:54 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-06-16 18:41:54 -0400
commit3ebb7d8e942ded31efbdb052ae3a1f08a6fc3d3d (patch)
tree6f5494b3732689c822a6b77891c6cc622322e206
parent2528595802a7900e1a61570d7228bd445b5b7f3f (diff)
downloadsqlalchemy-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--CHANGES11
-rw-r--r--lib/sqlalchemy/engine/base.py97
-rw-r--r--lib/sqlalchemy/engine/default.py2
-rw-r--r--test/engine/test_execute.py54
-rw-r--r--test/lib/requires.py8
5 files changed, 156 insertions, 16 deletions
diff --git a/CHANGES b/CHANGES
index ceeb06d8f..0f906eee7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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")
+ )