summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-10-06 23:38:43 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-10-06 23:43:33 -0400
commitba47b3e756b20d632db50acc2b30819c06801d9f (patch)
tree97b9ce85844b902f3ecc648693e04717d9009216
parent0a34070d9274d3dda05628543560bb42e8588dde (diff)
downloadsqlalchemy-ba47b3e756b20d632db50acc2b30819c06801d9f.tar.gz
- get tests working
- re-support the use case of composite PK where some cols are *supposed* to be NULL, check nullable flag
-rw-r--r--doc/build/changelog/migration_11.rst10
-rw-r--r--lib/sqlalchemy/sql/crud.py12
-rw-r--r--lib/sqlalchemy/testing/schema.py2
-rw-r--r--test/ext/declarative/test_basic.py3
-rw-r--r--test/orm/test_composites.py3
-rw-r--r--test/orm/test_query.py3
-rw-r--r--test/orm/test_relationships.py11
-rw-r--r--test/orm/test_unitofwork.py2
-rw-r--r--test/sql/test_defaults.py1
-rw-r--r--test/sql/test_insert.py11
10 files changed, 37 insertions, 21 deletions
diff --git a/doc/build/changelog/migration_11.rst b/doc/build/changelog/migration_11.rst
index 56fb5cff6..12f1af721 100644
--- a/doc/build/changelog/migration_11.rst
+++ b/doc/build/changelog/migration_11.rst
@@ -333,6 +333,16 @@ value generator can be indicated using :class:`.FetchedValue`::
Column('y', Integer, primary_key=True, server_default=FetchedValue())
)
+For the very unlikely case where a composite primary key is actually intended
+to store NULL in one or more of its columns (only supported on SQLite and MySQL),
+specify the column with ``nullable=True``::
+
+ Table(
+ 'b', metadata,
+ Column('x', Integer, primary_key=True),
+ Column('y', Integer, primary_key=True, nullable=True)
+ )
+
.. seealso::
diff --git a/lib/sqlalchemy/sql/crud.py b/lib/sqlalchemy/sql/crud.py
index eaad680d6..72b66c036 100644
--- a/lib/sqlalchemy/sql/crud.py
+++ b/lib/sqlalchemy/sql/crud.py
@@ -249,7 +249,9 @@ def _scan_cols(
elif implicit_return_defaults and \
c in implicit_return_defaults:
compiler.returning.append(c)
- elif c.primary_key and c is not stmt.table._autoincrement_column:
+ elif c.primary_key and \
+ c is not stmt.table._autoincrement_column and \
+ not c.nullable:
_raise_pk_with_no_anticipated_value(c)
elif compiler.isupdate:
@@ -324,7 +326,7 @@ def _append_param_insert_pk_returning(compiler, stmt, c, values, kw):
)
elif c is stmt.table._autoincrement_column or c.server_default is not None:
compiler.returning.append(c)
- else:
+ 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)
@@ -400,7 +402,7 @@ def _append_param_insert_pk(compiler, stmt, c, values, kw):
values.append(
(c, _create_prefetch_bind_param(compiler, c))
)
- elif c.default is None and c.server_default is 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)
@@ -612,9 +614,9 @@ def _raise_pk_with_no_anticipated_value(c):
"Column '%s.%s' is marked as a member of the "
"primary key for table '%s', "
"but has no Python-side or server-side default generator indicated, "
- "nor does it indicate 'autoincrement=True', "
+ "nor does it indicate 'autoincrement=True' or 'nullable=True', "
"and no explicit value is passed. "
- "Primary key columns may not store NULL."
+ "Primary key columns typically may not store NULL."
%
(c.table.fullname, c.name, c.table.fullname))
if len(c.table.primary_key.columns) > 1:
diff --git a/lib/sqlalchemy/testing/schema.py b/lib/sqlalchemy/testing/schema.py
index 517e31870..257578668 100644
--- a/lib/sqlalchemy/testing/schema.py
+++ b/lib/sqlalchemy/testing/schema.py
@@ -71,7 +71,7 @@ def Column(*args, **kw):
args = [arg for arg in args if not isinstance(arg, schema.ForeignKey)]
col = schema.Column(*args, **kw)
- if 'test_needs_autoincrement' in test_opts and \
+ if test_opts.get('test_needs_autoincrement', False) and \
kw.get('primary_key', False):
if col.default is None and col.server_default is None:
diff --git a/test/ext/declarative/test_basic.py b/test/ext/declarative/test_basic.py
index ab0de801c..5165d9cc9 100644
--- a/test/ext/declarative/test_basic.py
+++ b/test/ext/declarative/test_basic.py
@@ -1570,8 +1570,7 @@ class DeclarativeTest(DeclarativeTestBase):
meta = MetaData(testing.db)
t1 = Table(
't1', meta,
- Column('id', String(50),
- primary_key=True, test_needs_autoincrement=True),
+ Column('id', String(50), primary_key=True),
Column('data', String(50)))
meta.create_all()
try:
diff --git a/test/orm/test_composites.py b/test/orm/test_composites.py
index 8b777dcdf..48027ec2d 100644
--- a/test/orm/test_composites.py
+++ b/test/orm/test_composites.py
@@ -313,8 +313,7 @@ class PrimaryKeyTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
Table('graphs', metadata,
- Column('id', Integer, primary_key=True,
- test_needs_autoincrement=True),
+ Column('id', Integer, primary_key=True),
Column('version_id', Integer, primary_key=True,
nullable=True),
Column('name', String(30)))
diff --git a/test/orm/test_query.py b/test/orm/test_query.py
index 4ae0b010a..833613ec6 100644
--- a/test/orm/test_query.py
+++ b/test/orm/test_query.py
@@ -579,8 +579,7 @@ class GetTest(QueryTest):
table = Table(
'unicode_data', metadata,
Column(
- 'id', Unicode(40), primary_key=True,
- test_needs_autoincrement=True),
+ 'id', Unicode(40), primary_key=True),
Column('data', Unicode(40)))
metadata.create_all()
ustring = util.b('petit voix m\xe2\x80\x99a').decode('utf-8')
diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py
index 9e4b38a90..061187330 100644
--- a/test/orm/test_relationships.py
+++ b/test/orm/test_relationships.py
@@ -931,14 +931,12 @@ class SynonymsAsFKsTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
Table("tableA", metadata,
- Column("id", Integer, primary_key=True,
- test_needs_autoincrement=True),
+ Column("id", Integer, primary_key=True),
Column("foo", Integer,),
test_needs_fk=True)
Table("tableB", metadata,
- Column("id", Integer, primary_key=True,
- test_needs_autoincrement=True),
+ Column("id", Integer, primary_key=True),
Column("_a_id", Integer, key='a_id', primary_key=True),
test_needs_fk=True)
@@ -1093,7 +1091,7 @@ class FKsAsPksTest(fixtures.MappedTest):
'tablec', tableA.metadata,
Column('id', Integer, primary_key=True),
Column('a_id', Integer, ForeignKey('tableA.id'),
- primary_key=True, autoincrement=False, nullable=True))
+ primary_key=True, nullable=True))
tableC.create()
class C(fixtures.BasicEntity):
@@ -2703,8 +2701,7 @@ class ExplicitLocalRemoteTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
Table('t1', metadata,
- Column('id', String(50), primary_key=True,
- test_needs_autoincrement=True),
+ Column('id', String(50), primary_key=True),
Column('data', String(50)))
Table('t2', metadata,
Column('id', Integer, primary_key=True,
diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py
index 5a47903f0..2f67943f1 100644
--- a/test/orm/test_unitofwork.py
+++ b/test/orm/test_unitofwork.py
@@ -260,7 +260,7 @@ class PKTest(fixtures.MappedTest):
def define_tables(cls, metadata):
Table('multipk1', metadata,
Column('multi_id', Integer, primary_key=True,
- test_needs_autoincrement=True),
+ test_needs_autoincrement=not testing.against('sqlite')),
Column('multi_rev', Integer, primary_key=True),
Column('name', String(50), nullable=False),
Column('value', String(100)))
diff --git a/test/sql/test_defaults.py b/test/sql/test_defaults.py
index c17920af1..8a0e27ebd 100644
--- a/test/sql/test_defaults.py
+++ b/test/sql/test_defaults.py
@@ -732,7 +732,6 @@ class AutoIncrementTest(fixtures.TablesTest):
)
assert x._autoincrement_column is None
- @testing.fails_on('sqlite', 'FIXME: unknown')
def test_non_autoincrement(self):
# sqlite INT primary keys can be non-unique! (only for ints)
nonai = Table(
diff --git a/test/sql/test_insert.py b/test/sql/test_insert.py
index 7e5e1699e..bdaf4f38c 100644
--- a/test/sql/test_insert.py
+++ b/test/sql/test_insert.py
@@ -433,6 +433,17 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
)
+ def test_anticipate_nullable_composite_pk(self):
+ t = Table(
+ 't', MetaData(), Column('x', Integer, primary_key=True),
+ Column('y', Integer, primary_key=True, nullable=True)
+ )
+ self.assert_compile(
+ t.insert(),
+ "INSERT INTO t (x) VALUES (:x)",
+ params={'x': 5},
+ )
+
def test_anticipate_no_pk_non_composite_pk(self):
t = Table(
't', MetaData(),