]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Change autoincrement compileerror to a warning
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 7 Oct 2016 13:05:34 +0000 (09:05 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 7 Oct 2016 13:54:39 +0000 (09:54 -0400)
Users are complaining that IntegrityError is no longer
raised.

Change-Id: I0855d5b7a98d4338f0910501b6e6d404ba33634d
Fixes: #3216
doc/build/changelog/changelog_11.rst
doc/build/changelog/migration_11.rst
lib/sqlalchemy/sql/crud.py
test/dialect/postgresql/test_query.py
test/dialect/test_sqlite.py
test/sql/test_defaults.py
test/sql/test_insert.py

index 4b4c5204becf079ef6c52237a40782bd814eadd3..20f006eeb5ec5d9db577a47d9826f12265ddfaa7 100644 (file)
         was anticipated, the exception catch was not broad enough so it now
         catches all forms of pyodbc.Error.
 
+    .. change::
+        :tags: bug, core
+        :tickets: 3216
+
+        Changed the CompileError raised when various primary key missing
+        situations are detected to a warning.  The statement is again
+        passed to the database where it will fail and the DBAPI error (usually
+        IntegrityError) raises as usual.
+
+        .. seealso::
+
+            :ref:`change_3216`
 
 .. changelog::
     :version: 1.1.0
index 560872fc92cb1d4cf75cfd9994511030b3a1101f..3485b0e83c41ff3245132cf7cf19e0948ff414f9 100644 (file)
@@ -1299,9 +1299,9 @@ have autoincrement set up; given a table such as::
         Column('y', Integer, primary_key=True)
     )
 
-An INSERT emitted with no values for this table will produce the exception::
+An INSERT emitted with no values for this table will produce this warning::
 
-    CompileError: Column 'b.x' is marked as a member of the primary
+    SAWarning: Column 'b.x' is marked as a member of the primary
     key for table 'b', but has no Python-side or server-side default
     generator indicated, nor does it indicate 'autoincrement=True',
     and no explicit value is passed.  Primary key columns may not
index f770fc5134ef204dd70ffab0c2fe0a24bdc3c556..642659c84f77073b3fe60ede9c210546ef4a6f56 100644 (file)
@@ -301,7 +301,7 @@ def _scan_cols(
             elif c.primary_key and \
                     c is not stmt.table._autoincrement_column and \
                     not c.nullable:
-                _raise_pk_with_no_anticipated_value(c)
+                _warn_pk_with_no_anticipated_value(c)
 
         elif compiler.isupdate:
             _append_param_update(
@@ -379,7 +379,7 @@ def _append_param_insert_pk_returning(compiler, stmt, c, values, kw):
     elif not c.nullable:
         # no .default, no .server_default, not autoincrement, we have
         # no indication this primary key column will have any value
-        _raise_pk_with_no_anticipated_value(c)
+        _warn_pk_with_no_anticipated_value(c)
 
 
 def _create_insert_prefetch_bind_param(compiler, c, process=True, name=None):
@@ -464,7 +464,7 @@ def _append_param_insert_pk(compiler, stmt, c, values, kw):
     elif c.default is None and c.server_default is None and not c.nullable:
         # no .default, no .server_default, not autoincrement, we have
         # no indication this primary key column will have any value
-        _raise_pk_with_no_anticipated_value(c)
+        _warn_pk_with_no_anticipated_value(c)
 
 
 def _append_param_insert_hasdefault(
@@ -671,7 +671,7 @@ def _get_returning_modifiers(compiler, stmt):
         implicit_return_defaults, postfetch_lastrowid
 
 
-def _raise_pk_with_no_anticipated_value(c):
+def _warn_pk_with_no_anticipated_value(c):
     msg = (
         "Column '%s.%s' is marked as a member of the "
         "primary key for table '%s', "
@@ -689,4 +689,4 @@ def _raise_pk_with_no_anticipated_value(c):
             "behavior is expected for one of the columns in the primary key. "
             "CREATE TABLE statements are impacted by this change as well on "
             "most backends.")
-    raise exc.CompileError(msg)
+    util.warn(msg)
index 538312a6a97b63d3959c1e632bb04632a29711de..b8129f1e36a94cac5e5dc31cbbc3942858e23756 100644 (file)
@@ -1,7 +1,7 @@
 # coding: utf-8
 
 from sqlalchemy.testing import AssertsExecutionResults, eq_, \
-    assert_raises_message, AssertsCompiledSQL
+    assert_raises_message, AssertsCompiledSQL, expect_warnings, assert_raises
 from sqlalchemy import Table, Column, MetaData, Integer, String, bindparam, \
     Sequence, ForeignKey, text, select, func, extract, literal_column, \
     tuple_, DateTime, Time, literal, and_, Date, or_
@@ -69,11 +69,13 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
             engines.testing_engine(options={'implicit_returning': False}),
             engines.testing_engine(options={'implicit_returning': True})
         ]:
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                eng.execute, t2.insert()
-            )
+            with expect_warnings(
+                ".*has no Python-side or server-side default.*"
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    eng.execute, t2.insert()
+                )
 
     def test_sequence_insert(self):
         table = Table(
@@ -523,24 +525,32 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
         with engine.connect() as conn:
             conn.execute(table.insert(), {'id': 30, 'data': 'd1'})
 
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                conn.execute, table.insert(), {'data': 'd2'})
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                conn.execute, table.insert(), {'data': 'd2'},
-                {'data': 'd3'})
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                conn.execute, table.insert(), {'data': 'd2'})
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                conn.execute, table.insert(), {'data': 'd2'},
-                {'data': 'd3'})
+            with expect_warnings(
+                    ".*has no Python-side or server-side default.*",
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    conn.execute, table.insert(), {'data': 'd2'})
+            with expect_warnings(
+                    ".*has no Python-side or server-side default.*",
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    conn.execute, table.insert(), {'data': 'd2'},
+                    {'data': 'd3'})
+            with expect_warnings(
+                    ".*has no Python-side or server-side default.*",
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    conn.execute, table.insert(), {'data': 'd2'})
+            with expect_warnings(
+                    ".*has no Python-side or server-side default.*",
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    conn.execute, table.insert(), {'data': 'd2'},
+                    {'data': 'd3'})
 
             conn.execute(
                 table.insert(),
@@ -560,15 +570,20 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
         table = Table(table.name, m2, autoload=True)
         with engine.connect() as conn:
             conn.execute(table.insert(), {'id': 30, 'data': 'd1'})
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                conn.execute, table.insert(), {'data': 'd2'})
-            assert_raises_message(
-                exc.CompileError,
-                ".*has no Python-side or server-side default.*",
-                conn.execute, table.insert(), {'data': 'd2'},
-                {'data': 'd3'})
+
+            with expect_warnings(
+                    ".*has no Python-side or server-side default.*",
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    conn.execute, table.insert(), {'data': 'd2'})
+            with expect_warnings(
+                    ".*has no Python-side or server-side default.*",
+            ):
+                assert_raises(
+                    (exc.IntegrityError, exc.ProgrammingError),
+                    conn.execute, table.insert(), {'data': 'd2'},
+                    {'data': 'd3'})
             conn.execute(
                 table.insert(),
                 {'id': 31, 'data': 'd2'}, {'id': 32, 'data': 'd3'})
index 473f4f46251b0cb677c88149c523f4d83e53fe79..1ac67bd3199063f6069cdd5bde1bc398c5477460 100644 (file)
@@ -5,7 +5,7 @@ import os
 import datetime
 
 from sqlalchemy.testing import eq_, assert_raises, \
-    assert_raises_message, is_
+    assert_raises_message, is_, expect_warnings
 from sqlalchemy import Table, select, bindparam, Column,\
     MetaData, func, extract, ForeignKey, text, DefaultClause, and_, \
     create_engine, UniqueConstraint, Index, PrimaryKeyConstraint
@@ -803,13 +803,20 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
 
     @testing.exclude('sqlite', '<', (3, 3, 8), 'no database support')
     def test_empty_insert_pk2(self):
-        # now raises CompileError due to [ticket:3216]
-        assert_raises(
-            exc.CompileError, self._test_empty_insert,
-            Table(
-                'b', MetaData(testing.db),
-                Column('x', Integer, primary_key=True),
-                Column('y', Integer, primary_key=True)))
+        # now warns due to [ticket:3216]
+
+        with expect_warnings(
+            "Column 'b.x' is marked as a member of the "
+            "primary key for table 'b'",
+            "Column 'b.y' is marked as a member of the "
+            "primary key for table 'b'",
+        ):
+            assert_raises(
+                exc.IntegrityError, self._test_empty_insert,
+                Table(
+                    'b', MetaData(testing.db),
+                    Column('x', Integer, primary_key=True),
+                    Column('y', Integer, primary_key=True)))
 
     @testing.exclude('sqlite', '<', (3, 3, 8), 'no database support')
     def test_empty_insert_pk2_fv(self):
@@ -824,13 +831,19 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults):
 
     @testing.exclude('sqlite', '<', (3, 3, 8), 'no database support')
     def test_empty_insert_pk3(self):
-        # now raises CompileError due to [ticket:3216]
-        assert_raises(
-            exc.CompileError, self._test_empty_insert,
-            Table(
-                'c', MetaData(testing.db),
-                Column('x', Integer, primary_key=True),
-                Column('y', Integer, DefaultClause('123'), primary_key=True)))
+        # now warns due to [ticket:3216]
+        with expect_warnings(
+            "Column 'c.x' is marked as a member of the primary key for table"
+        ):
+            assert_raises(
+                exc.IntegrityError,
+                self._test_empty_insert,
+                Table(
+                    'c', MetaData(testing.db),
+                    Column('x', Integer, primary_key=True),
+                    Column('y', Integer,
+                           DefaultClause('123'), primary_key=True))
+            )
 
     @testing.exclude('sqlite', '<', (3, 3, 8), 'no database support')
     def test_empty_insert_pk3_fv(self):
index 1b033fce8f8b9b6fc54a8a2577ce11a372cd6484..b294158f45d22386eef820baef354dfcfd68c9f9 100644 (file)
@@ -1,5 +1,5 @@
 from sqlalchemy.testing import eq_, assert_raises_message, \
-    assert_raises, AssertsCompiledSQL
+    assert_raises, AssertsCompiledSQL, expect_warnings
 import datetime
 from sqlalchemy.schema import CreateSequence, DropSequence, CreateTable
 from sqlalchemy.sql import select, text, literal_column
@@ -848,6 +848,7 @@ class AutoIncrementTest(fixtures.TablesTest):
         )
         assert x._autoincrement_column is None
 
+    @testing.only_on("sqlite")
     def test_non_autoincrement(self):
         # sqlite INT primary keys can be non-unique! (only for ints)
         nonai = Table(
@@ -861,13 +862,12 @@ class AutoIncrementTest(fixtures.TablesTest):
             # mysql in legacy mode fails on second row
             nonai.insert().execute(data='row 1')
             nonai.insert().execute(data='row 2')
-        assert_raises_message(
-            sa.exc.CompileError,
-            ".*has no Python-side or server-side default.*",
-            go
-        )
 
-        nonai.insert().execute(id=1, data='row 1')
+        # just testing SQLite for now, it passes
+        with expect_warnings(
+            ".*has no Python-side or server-side default.*",
+        ):
+            go()
 
     def test_col_w_sequence_non_autoinc_no_firing(self):
         metadata = self.metadata
index f2515c4eba1fd627666a8255e7011e1265ef0121..3a884643bea6014560df82f8c2d7df7e53fbee3b 100644 (file)
@@ -5,7 +5,7 @@ from sqlalchemy import Column, Integer, MetaData, String, Table,\
 from sqlalchemy.dialects import mysql, postgresql
 from sqlalchemy.engine import default
 from sqlalchemy.testing import AssertsCompiledSQL,\
-    assert_raises_message, fixtures, eq_
+    assert_raises_message, fixtures, eq_, expect_warnings
 from sqlalchemy.sql import crud
 
 class _InsertTestBase(object):
@@ -484,13 +484,16 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             't', MetaData(), Column('x', Integer, primary_key=True),
             Column('y', Integer, primary_key=True)
         )
-        assert_raises_message(
-            exc.CompileError,
+
+        with expect_warnings(
             "Column 't.y' is marked as a member.*"
             "Note that as of SQLAlchemy 1.1,",
-            t.insert().compile, column_keys=['x']
-
-        )
+        ):
+            self.assert_compile(
+                t.insert(),
+                "INSERT INTO t (x) VALUES (:x)",
+                params={'x': 5},
+            )
 
     def test_anticipate_no_pk_composite_pk_implicit_returning(self):
         t = Table(
@@ -499,13 +502,17 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
         )
         d = postgresql.dialect()
         d.implicit_returning = True
-        assert_raises_message(
-            exc.CompileError,
+
+        with expect_warnings(
             "Column 't.y' is marked as a member.*"
             "Note that as of SQLAlchemy 1.1,",
-            t.insert().compile, dialect=d, column_keys=['x']
-
-        )
+        ):
+            self.assert_compile(
+                t.insert(),
+                "INSERT INTO t (x) VALUES (%(x)s)",
+                params={"x": 5},
+                dialect=d
+            )
 
     def test_anticipate_no_pk_composite_pk_prefetch(self):
         t = Table(
@@ -514,13 +521,16 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
         )
         d = postgresql.dialect()
         d.implicit_returning = False
-        assert_raises_message(
-            exc.CompileError,
+        with expect_warnings(
             "Column 't.y' is marked as a member.*"
-            "Note that as of SQLAlchemy 1.1,",
-            t.insert().compile, dialect=d, column_keys=['x']
-
-        )
+            "Note that as of SQLAlchemy 1.1,"
+        ):
+            self.assert_compile(
+                t.insert(),
+                "INSERT INTO t (x) VALUES (%(x)s)",
+                params={'x': 5},
+                dialect=d
+            )
 
     def test_anticipate_nullable_composite_pk(self):
         t = Table(
@@ -539,13 +549,15 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             Column('x', Integer, primary_key=True, autoincrement=False),
             Column('q', Integer)
         )
-        assert_raises_message(
-            exc.CompileError,
+        with expect_warnings(
             "Column 't.x' is marked as a member.*"
-            "may not store NULL.$",
-            t.insert().compile, column_keys=['q']
-
-        )
+            "may not store NULL.$"
+        ):
+            self.assert_compile(
+                t.insert(),
+                "INSERT INTO t (q) VALUES (:q)",
+                params={"q": 5}
+            )
 
     def test_anticipate_no_pk_non_composite_pk_implicit_returning(self):
         t = Table(
@@ -555,13 +567,16 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
         )
         d = postgresql.dialect()
         d.implicit_returning = True
-        assert_raises_message(
-            exc.CompileError,
+        with expect_warnings(
             "Column 't.x' is marked as a member.*"
             "may not store NULL.$",
-            t.insert().compile, dialect=d, column_keys=['q']
-
-        )
+        ):
+            self.assert_compile(
+                t.insert(),
+                "INSERT INTO t (q) VALUES (%(q)s)",
+                params={"q": 5},
+                dialect=d
+            )
 
     def test_anticipate_no_pk_non_composite_pk_prefetch(self):
         t = Table(
@@ -571,13 +586,17 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
         )
         d = postgresql.dialect()
         d.implicit_returning = False
-        assert_raises_message(
-            exc.CompileError,
-            "Column 't.x' is marked as a member.*"
-            "may not store NULL.$",
-            t.insert().compile, dialect=d, column_keys=['q']
 
-        )
+        with expect_warnings(
+            "Column 't.x' is marked as a member.*"
+            "may not store NULL.$"
+        ):
+            self.assert_compile(
+                t.insert(),
+                "INSERT INTO t (q) VALUES (%(q)s)",
+                params={"q": 5},
+                dialect=d
+            )
 
 
 class InsertImplicitReturningTest(