|
7 | 7 | from tests.utils.fake_client import FakeClient |
8 | 8 | from tortoise import fields |
9 | 9 | from tortoise.contrib.postgres.fields import TSVectorField |
10 | | -from tortoise.contrib.postgres.indexes import GinIndex |
| 10 | +from tortoise.contrib.postgres.indexes import GinIndex, PostgreSQLIndex |
11 | 11 | from tortoise.indexes import Index |
12 | 12 | from tortoise.migrations.schema_editor.base import BaseSchemaEditor |
13 | 13 | from tortoise.migrations.schema_editor.base_postgres import BasePostgresSchemaEditor |
@@ -192,6 +192,73 @@ class Meta: |
192 | 192 | ) == client.executed[0] |
193 | 193 |
|
194 | 194 |
|
| 195 | +@pytest.mark.asyncio |
| 196 | +async def test_add_unique_index_generates_nulls_not_distinct_sql() -> None: |
| 197 | + class Customer(Model): |
| 198 | + id = fields.IntField(pk=True) |
| 199 | + shop_id = fields.IntField() |
| 200 | + phone_number = fields.CharField(max_length=20) |
| 201 | + deleted_at = fields.DatetimeField(null=True) |
| 202 | + |
| 203 | + class Meta: |
| 204 | + table = "customer" |
| 205 | + app = "models" |
| 206 | + indexes = [ |
| 207 | + PostgreSQLIndex( |
| 208 | + fields=("shop_id", "phone_number", "deleted_at"), |
| 209 | + unique=True, |
| 210 | + nulls_not_distinct=True, |
| 211 | + ) |
| 212 | + ] |
| 213 | + |
| 214 | + client = FakeClient("postgres", inline_comment=False) |
| 215 | + editor = BasePostgresSchemaEditor(client) |
| 216 | + |
| 217 | + index = cast(Index, Customer._meta.indexes[0]) |
| 218 | + await editor.add_index(Customer, index) |
| 219 | + |
| 220 | + assert client.executed |
| 221 | + expected_name = editor._generate_index_name("uidx", Customer, ["shop_id", "phone_number", "deleted_at"]) |
| 222 | + assert ( |
| 223 | + f'CREATE UNIQUE INDEX "{expected_name}" ON "customer"' |
| 224 | + f' ("shop_id", "phone_number", "deleted_at") NULLS NOT DISTINCT;' |
| 225 | + ) == client.executed[0] |
| 226 | + |
| 227 | + |
| 228 | +@pytest.mark.asyncio |
| 229 | +async def test_add_partial_unique_index_generates_nulls_not_distinct_before_where() -> None: |
| 230 | + class Customer(Model): |
| 231 | + id = fields.IntField(pk=True) |
| 232 | + shop_id = fields.IntField() |
| 233 | + phone_number = fields.CharField(max_length=20) |
| 234 | + deleted_at = fields.DatetimeField(null=True) |
| 235 | + |
| 236 | + class Meta: |
| 237 | + table = "customer" |
| 238 | + app = "models" |
| 239 | + indexes = [ |
| 240 | + PostgreSQLIndex( |
| 241 | + fields=("shop_id", "phone_number", "deleted_at"), |
| 242 | + unique=True, |
| 243 | + nulls_not_distinct=True, |
| 244 | + condition={"shop_id": 1}, |
| 245 | + ) |
| 246 | + ] |
| 247 | + |
| 248 | + client = FakeClient("postgres", inline_comment=False) |
| 249 | + editor = BasePostgresSchemaEditor(client) |
| 250 | + |
| 251 | + index = cast(Index, Customer._meta.indexes[0]) |
| 252 | + await editor.add_index(Customer, index) |
| 253 | + |
| 254 | + assert client.executed |
| 255 | + expected_name = editor._generate_index_name("uidx", Customer, ["shop_id", "phone_number", "deleted_at"]) |
| 256 | + assert ( |
| 257 | + f'CREATE UNIQUE INDEX "{expected_name}" ON "customer"' |
| 258 | + f' ("shop_id", "phone_number", "deleted_at") NULLS NOT DISTINCT WHERE shop_id = 1;' |
| 259 | + ) == client.executed[0] |
| 260 | + |
| 261 | + |
195 | 262 | @pytest.mark.asyncio |
196 | 263 | async def test_alter_generated_field_raises() -> None: |
197 | 264 | class OldSearchDocument(Model): |
|
0 commit comments