diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-03-20 12:49:28 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-03-20 12:49:28 -0400 |
commit | 90335a89a98df23db7a3ae1233eb4fbb5743d2e8 (patch) | |
tree | 9a4ac236f83696709bd355dcac22552aeb177694 /lib/sqlalchemy/sql | |
parent | 75c78aa714ca55818f0ba12a67cf2f77927b68f7 (diff) | |
download | sqlalchemy-90335a89a98df23db7a3ae1233eb4fbb5743d2e8.tar.gz |
- Added new generic function "next_value()", accepts
a Sequence object as its argument and renders the
appropriate "next value" generation string on the
target platform, if supported. Also provides
".next_value()" method on Sequence itself.
[ticket:2085]
- added tests for all the conditions described
in [ticket:2085]
- postgresql dialect will exec/compile a Sequence
that has "optional=True". the optional flag is now only
checked specifically in the context of a Table primary key
evaulation.
- func.next_value() or other SQL expression can
be embedded directly into an insert() construct,
and if implicit or explicit "returning" is used
in conjunction with a primary key column,
the newly generated value will be present in
result.inserted_primary_key. [ticket:2084]
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 26 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/expression.py | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/functions.py | 26 |
3 files changed, 50 insertions, 12 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index d6a020bdc..7547e1662 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -492,6 +492,14 @@ class SQLCompiler(engine.Compiled): return ".".join(func.packagenames + [name]) % \ {'expr':self.function_argspec(func, **kwargs)} + def visit_next_value_func(self, next_value, **kw): + return self.visit_sequence(next_value.sequence) + + def visit_sequence(self, sequence): + raise NotImplementedError( + "Dialect '%s' does not support sequence increments." % self.dialect.name + ) + def function_argspec(self, func, **kwargs): return func.clause_expr._compiler_dispatch(self, **kwargs) @@ -926,9 +934,6 @@ class SQLCompiler(engine.Compiled): join.onclause._compiler_dispatch(self, **kwargs) ) - def visit_sequence(self, seq): - return None - def visit_insert(self, insert_stmt): self.isinsert = True colparams = self._get_colparams(insert_stmt) @@ -1075,6 +1080,9 @@ class SQLCompiler(engine.Compiled): if sql._is_literal(value): value = self._create_crud_bind_param( c, value, required=value is required) + elif c.primary_key and implicit_returning: + self.returning.append(c) + value = self.process(value.self_group()) else: self.postfetch.append(c) value = self.process(value.self_group()) @@ -1092,8 +1100,10 @@ class SQLCompiler(engine.Compiled): if implicit_returning: if c.default is not None: if c.default.is_sequence: - proc = self.process(c.default) - if proc is not None: + if self.dialect.supports_sequences and \ + (not c.default.optional or \ + not self.dialect.sequences_optional): + proc = self.process(c.default) values.append((c, proc)) self.returning.append(c) elif c.default.is_clause_element: @@ -1124,8 +1134,10 @@ class SQLCompiler(engine.Compiled): elif c.default is not None: if c.default.is_sequence: - proc = self.process(c.default) - if proc is not None: + if self.dialect.supports_sequences and \ + (not c.default.optional or \ + not self.dialect.sequences_optional): + proc = self.process(c.default) values.append((c, proc)) if not c.primary_key: self.postfetch.append(c) diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 9aed957d2..d49f12150 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1178,14 +1178,16 @@ def _column_as_key(element): return element.key def _literal_as_text(element): - if hasattr(element, '__clause_element__'): + if isinstance(element, Visitable): + return element + elif hasattr(element, '__clause_element__'): return element.__clause_element__() elif isinstance(element, basestring): return _TextClause(unicode(element)) - elif not isinstance(element, Visitable): - raise exc.ArgumentError("SQL expression object or string expected.") else: - return element + raise exc.ArgumentError( + "SQL expression object or string expected." + ) def _clause_element_as_expr(element): if hasattr(element, '__clause_element__'): diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py index 10eaa577b..717816656 100644 --- a/lib/sqlalchemy/sql/functions.py +++ b/lib/sqlalchemy/sql/functions.py @@ -4,7 +4,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -from sqlalchemy import types as sqltypes +from sqlalchemy import types as sqltypes, schema from sqlalchemy.sql.expression import ( ClauseList, Function, _literal_as_binds, text, _type_from_args ) @@ -29,6 +29,29 @@ class GenericFunction(Function): self.type = sqltypes.to_instance( type_ or getattr(self, '__return_type__', None)) + +class next_value(Function): + """Represent the 'next value', given a :class:`.Sequence` + as it's single argument. + + Compiles into the appropriate function on each backend, + or will raise NotImplementedError if used on a backend + that does not provide support for sequences. + + """ + type = sqltypes.Integer() + name = "next_value" + + def __init__(self, seq, **kw): + assert isinstance(seq, schema.Sequence), \ + "next_value() accepts a Sequence object as input." + self._bind = kw.get('bind', None) + self.sequence = seq + + @property + def _from_objects(self): + return [] + class AnsiFunction(GenericFunction): def __init__(self, **kwargs): GenericFunction.__init__(self, **kwargs) @@ -52,6 +75,7 @@ class min(ReturnTypeFromArgs): class sum(ReturnTypeFromArgs): pass + class now(GenericFunction): __return_type__ = sqltypes.DateTime |