--- /dev/null
+.. change::
+ :tags: bug, schema
+ :tickets: 11811
+
+ The :class:`.Float` and :class:`.Numeric` types are no longer automatically
+ considered as auto-incrementing columns when the
+ :paramref:`_schema.Column.autoincrement` parameter is left at its default
+ of ``"auto"`` on a :class:`_schema.Column` that is part of the primary key.
+ When the parameter is set to ``True``, a :class:`.Numeric` type will be
+ accepted as an auto-incrementing datatype for primary key columns, but only
+ if its scale is explicitly given as zero; otherwise, an error is raised.
+ This is a change from 2.0 where all numeric types including floats were
+ automatically considered as "autoincrement" for primary key columns.
@util.ro_memoized_property
def _autoincrement_column(self) -> Optional[Column[int]]:
def _validate_autoinc(col: Column[Any], autoinc_true: bool) -> bool:
- if col.type._type_affinity is None or not issubclass(
- col.type._type_affinity,
- (
- type_api.INTEGERTYPE._type_affinity,
- type_api.NUMERICTYPE._type_affinity,
- ),
+ if col.type._type_affinity is not None and issubclass(
+ col.type._type_affinity, type_api.NUMERICTYPE._type_affinity
+ ):
+ scale = col.type.scale # type: ignore[attr-defined]
+ if scale != 0 and autoinc_true:
+ raise exc.ArgumentError(
+ f"Column type {col.type} with non-zero scale "
+ f"{scale} on column '{col}' is not "
+ f"compatible with autoincrement=True"
+ )
+ elif not autoinc_true:
+ return False
+ elif col.type._type_affinity is None or not issubclass(
+ col.type._type_affinity, type_api.INTEGERTYPE._type_affinity
):
if autoinc_true:
raise exc.ArgumentError(
else:
return False
elif (
- not isinstance(col.default, (type(None), Sequence))
+ col.default is not None
+ and not isinstance(col.default, Sequence)
and not autoinc_true
):
return False
1,
)
+ @testing.combinations(
+ sa.Float, sa.DOUBLE_PRECISION, sa.Numeric, sa.Numeric(asdecimal=False)
+ )
+ def test_autoincrement_not_float(self, type_):
+ t = Table(
+ "table", sa.MetaData(), Column("col", type_, primary_key=True)
+ )
+
+ eq_(t.autoincrement_column, None)
+ eq_(t.primary_key._autoincrement_column, None)
+ eq_(t.c.col.autoincrement, "auto")
+
class SpecialTypePKTest(fixtures.TestBase):
"""test process_result_value in conjunction with primary key columns.
from sqlalchemy import Enum
from sqlalchemy import event
from sqlalchemy import exc
+from sqlalchemy import Float
from sqlalchemy import ForeignKey
from sqlalchemy import ForeignKeyConstraint
from sqlalchemy import func
lambda: pk._autoincrement_column,
)
+ def test_float_illegal_autoinc(self):
+ """test that Float is not acceptable if autoincrement=True"""
+ t = Table("t", MetaData(), Column("a", Float, autoincrement=True))
+ pk = PrimaryKeyConstraint(t.c.a)
+ t.append_constraint(pk)
+
+ with expect_raises_message(
+ exc.ArgumentError, "Column type FLOAT with non-zero scale "
+ ):
+ pk._autoincrement_column,
+
+ def test_numeric_nonzero_scale_illegal_autoinc(self):
+ """test that Numeric() with non-zero scale is not acceptable if
+ autoincrement=True"""
+ t = Table(
+ "t", MetaData(), Column("a", Numeric(10, 5), autoincrement=True)
+ )
+ pk = PrimaryKeyConstraint(t.c.a)
+ t.append_constraint(pk)
+
+ with expect_raises_message(
+ exc.ArgumentError,
+ r"Column type NUMERIC\(10, 5\) with non-zero scale 5",
+ ):
+ pk._autoincrement_column,
+
+ def test_numeric_zero_scale_autoinc_not_auto(self):
+ """test that Numeric() is not automatically assigned to
+ autoincrement"""
+ t = Table(
+ "t", MetaData(), Column("a", Numeric(10, 0), primary_key=True)
+ )
+
+ is_(t.autoincrement_column, None)
+
+ def test_integer_autoinc_is_auto(self):
+ """test that Integer() is automatically assigned to autoincrement"""
+ t = Table("t", MetaData(), Column("a", Integer, primary_key=True))
+
+ is_(t.autoincrement_column, t.c.a)
+
+ def test_numeric_zero_scale_autoinc_explicit_ok(self):
+ """test that Numeric() with zero scale is acceptable if
+ autoincrement=True"""
+ t = Table(
+ "t",
+ MetaData(),
+ Column("a", Numeric(10, 0), autoincrement=True, primary_key=True),
+ )
+
+ is_(t.autoincrement_column, t.c.a)
+
def test_single_integer_default(self):
t = Table(
"t",