]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- open up autoincrement for columns that have a default; autoinc is usually
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 8 Oct 2015 21:02:55 +0000 (17:02 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 8 Oct 2015 21:02:55 +0000 (17:02 -0400)
"auto" now so True can indicate the dialect would support this

doc/build/changelog/migration_11.rst
lib/sqlalchemy/sql/schema.py
test/sql/test_defaults.py
test/sql/test_metadata.py

index c57c917615b6436c3bb08e91b63fbc678079f9f1..5b7c8321a0f733b9e1b7ecbd1656a223d0a18745 100644 (file)
@@ -343,6 +343,9 @@ specify the column with ``nullable=True``::
         Column('y', Integer, primary_key=True, nullable=True)
     )
 
+In a related change, the ``autoincrement`` flag may be set to True
+on a column that has a client-side or server-side default.  This typically
+will not have much impact on the behavior of the column during an INSERT.
 
 
 .. seealso::
index 210c6338caaf1e0fe05e39d0d8314c0df6cc7cf1..e20545962792c80ddd503da1bb3c36fe13d4fe4e 100644 (file)
@@ -907,7 +907,8 @@ class Column(SchemaItem, ColumnClause):
         :param autoincrement: Set up "auto increment" semantics for an integer
           primary key column.  The default value is the string ``"auto"``
           which indicates that a single-column primary key that is of
-          an INTEGER type should receive auto increment semantics automatically;
+          an INTEGER type with no stated client-side or python-side defaults
+          should receive auto increment semantics automatically;
           all other varieties of primary key columns will not.  This
           includes that :term:`DDL` such as Postgresql SERIAL or MySQL
           AUTO_INCREMENT will be emitted for this column during a table
@@ -918,7 +919,11 @@ class Column(SchemaItem, ColumnClause):
           The flag may be set to ``True`` to indicate that a column which
           is part of a composite (e.g. multi-column) primary key should
           have autoincrement semantics, though note that only one column
-          within a primary key may have this setting.   It can also be
+          within a primary key may have this setting.    It can also
+          be set to ``True`` to indicate autoincrement semantics on a
+          column that has a client-side or server-side default configured,
+          however note that not all dialects can accommodate all styles
+          of default as an "autoincrement".  It can also be
           set to ``False`` on a single-column primary key that has a
           datatype of INTEGER in order to disable auto increment semantics
           for that column.
@@ -929,6 +934,10 @@ class Column(SchemaItem, ColumnClause):
              (multi-column) primary keys, autoincrement is never implicitly
              enabled; as always, ``autoincrement=True`` will allow for
              at most one of those columns to be an "autoincrement" column.
+             ``autoincrement=True`` may also be set on a :class:`.Column`
+             that has an explicit client-side or server-side default,
+             subject to limitations of the backend database and dialect.
+
 
           The setting *only* has an effect for columns which are:
 
@@ -948,9 +957,6 @@ class Column(SchemaItem, ColumnClause):
             on a column that refers to another via foreign key, as such a column
             is required to refer to a value that originates from elsewhere.
 
-          * have no server side or client side defaults (with the exception
-            of Postgresql SERIAL).
-
           The setting has these two effects on columns that meet the
           above criteria:
 
@@ -966,13 +972,14 @@ class Column(SchemaItem, ColumnClause):
 
                 :ref:`sqlite_autoincrement`
 
-          * The column will be considered to be available as
-            cursor.lastrowid or equivalent, for those dialects which
-            "post fetch" newly inserted identifiers after a row has
-            been inserted (SQLite, MySQL, MS-SQL).  It does not have
-            any effect in this regard for databases that use sequences
-            to generate primary key identifiers (i.e. Firebird, Postgresql,
-            Oracle).
+          * The column will be considered to be available using an
+            "autoincrement" method specific to the backend database, such
+            as calling upon ``cursor.lastrowid``, using RETURNING in an
+            INSERT statement to get at a sequence-generated value, or using
+            special functions such as "SELECT scope_identity()".
+            These methods are highly specific to the DBAPIs and databases in
+            use and vary greatly, so care should be taken when associating
+            ``autoincrement=True`` with a custom default generation function.
 
 
         :param default: A scalar, Python callable, or
@@ -3043,11 +3050,11 @@ class PrimaryKeyConstraint(ColumnCollectionConstraint):
     @util.memoized_property
     def _autoincrement_column(self):
 
-        def _validate_autoinc(col, raise_):
+        def _validate_autoinc(col, autoinc_true):
             if col.type._type_affinity is None or not issubclass(
                 col.type._type_affinity,
                     type_api.INTEGERTYPE._type_affinity):
-                if raise_:
+                if autoinc_true:
                     raise exc.ArgumentError(
                         "Column type %s on column '%s' is not "
                         "compatible with autoincrement=True" % (
@@ -3056,30 +3063,11 @@ class PrimaryKeyConstraint(ColumnCollectionConstraint):
                         ))
                 else:
                     return False
-            elif not isinstance(col.default, (type(None), Sequence)):
-                if raise_:
-                    raise exc.ArgumentError(
-                        "Column default %s on column %s.%s is not "
-                        "compatible with autoincrement=True" % (
-                            col.default,
-                            col.table.fullname, col.name
-                        )
-                    )
-                else:
-                    return False
-            elif (
-                col.server_default is not None and
-                    not col.server_default.reflected):
-                if raise_:
-                    raise exc.ArgumentError(
-                        "Column server default %s on column %s.%s is not "
-                        "compatible with autoincrement=True" % (
-                            col.server_default,
-                            col.table.fullname, col.name
-                        )
-                    )
-                else:
+            elif not isinstance(col.default, (type(None), Sequence)) and \
+                    not autoinc_true:
                     return False
+            elif col.server_default is not None and not autoinc_true:
+                return False
             elif (
                     col.foreign_keys and col.autoincrement
                     not in (True, 'ignore_fk')):
index 8a0e27ebdf8f4c5bc34344aeaa41a3a3058c66f1..fd4c77555d212cb8011f84f1ca8c227a05a6200f 100644 (file)
@@ -1080,6 +1080,23 @@ class SequenceTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         assert not self._has_sequence('s1')
         assert not self._has_sequence('s2')
 
+    @testing.requires.returning
+    @testing.provide_metadata
+    def test_freestanding_sequence_via_autoinc(self):
+        t = Table(
+            'some_table', self.metadata,
+            Column(
+                'id', Integer,
+                autoincrement=True,
+                primary_key=True,
+                server_default=Sequence(
+                    'my_sequence', metadata=self.metadata).next_value())
+        )
+        self.metadata.create_all(testing.db)
+
+        result = testing.db.execute(t.insert())
+        eq_(result.inserted_primary_key, [1])
+
 cartitems = sometable = metadata = None
 
 
index 24f41643915092ce20e3036364278e01e6ecf7ae..501df4671107aa8d3e8e41f35ccd03a90df1ccce 100644 (file)
@@ -1417,7 +1417,7 @@ class PKAutoIncrementTest(fixtures.TestBase):
             lambda: pk._autoincrement_column
         )
 
-    def test_single_integer_illegal_default(self):
+    def test_single_integer_default(self):
         t = Table(
             't', MetaData(),
             Column('a', Integer, autoincrement=True, default=lambda: 1))
@@ -1426,13 +1426,12 @@ class PKAutoIncrementTest(fixtures.TestBase):
         )
         t.append_constraint(pk)
 
-        assert_raises_message(
-            exc.ArgumentError,
-            "Column default.*on column t.a is not compatible",
-            lambda: pk._autoincrement_column
-        )
+        is_(pk._autoincrement_column, t.c.a)
 
-    def test_single_integer_illegal_server_default(self):
+    def test_single_integer_server_default(self):
+        # new as of 1.1; now that we have three states for autoincrement,
+        # if the user puts autoincrement=True with a server_default, trust
+        # them on it
         t = Table(
             't', MetaData(),
             Column('a', Integer,
@@ -1442,11 +1441,7 @@ class PKAutoIncrementTest(fixtures.TestBase):
         )
         t.append_constraint(pk)
 
-        assert_raises_message(
-            exc.ArgumentError,
-            "Column server default.*on column t.a is not compatible",
-            lambda: pk._autoincrement_column
-        )
+        is_(pk._autoincrement_column, t.c.a)
 
     def test_implicit_autoinc_but_fks(self):
         m = MetaData()