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
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
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(
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):
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(
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', "
"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)
# 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_
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(
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(),
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'})
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
@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):
@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):
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
)
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(
# 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
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):
'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(
)
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(
)
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(
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(
)
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(
)
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(