[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: tbicr\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\n<!--A clear and concise description of what the bug is.-->\n\n**To Reproduce**\n1. What model did you have?\n2. How did you change the model?\n3. What migration were generated?\n4. What SQL was executed?\n5. What issue did you get?\n\n**Expected behavior**\n<!--A clear and concise description of what you expected to happen.-->\n\n**Versions:**\n - Postgres:\n - Python:\n - Django:\n - django-pg-zero-downtime-migrations library:\n"
  },
  {
    "path": ".github/workflows/check.yml",
    "content": "name: Check\n\non: [push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n\n      - name: build and push\n        uses: docker/build-push-action@v6\n        with:\n          tags: django-pg-zero-downtime-migrations:latest\n          outputs: type=docker,dest=${{ runner.temp }}/django-pg-zero-downtime-migrations-image.tar\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n\n      - name: Upload artifact\n        uses: actions/upload-artifact@v4\n        with:\n          name: django-pg-zero-downtime-migrations-image\n          path: ${{ runner.temp }}/django-pg-zero-downtime-migrations-image.tar\n\n  check:\n    needs: build\n    strategy:\n      fail-fast: false\n      matrix:\n        tox-filter:\n          - \"py3.8\"\n          - \"py3.9\"\n          - \"py3.10\"\n          - \"py3.11\"\n          - \"py3.12\"\n          - \"py3.13\"\n    name: run checks ${{ matrix.tox-filter }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: checkout code\n        uses: actions/checkout@v2\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n\n      - name: Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: django-pg-zero-downtime-migrations-image\n          path: ${{ runner.temp }}\n\n      - name: Load Docker image\n        run: |\n          docker load --input ${{ runner.temp }}/django-pg-zero-downtime-migrations-image.tar\n          docker image ls -a\n\n      - name: pull DB images\n        run: docker compose pull --quiet --ignore-buildable\n\n      - name: run checks\n        run: docker compose run --rm django-pg-zero-downtime-migrations-tests tox -f ${{ matrix.tox-filter }}\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Build and Publish\n\non: workflow_dispatch\n\njobs:\n  build-and-publish:\n    name: build and publish package\n    runs-on: ubuntu-latest\n    steps:\n      - name: checkout code\n        uses: actions/checkout@v2\n      - name: set up python\n        uses: actions/setup-python@v2\n        with:\n          python-version: '3.x'\n      - name: install pypa/build\n        run: python -m pip install build --user\n      - name: build a binary wheel and a source tarball\n        run: python -m build --sdist --wheel --outdir dist/\n      - name: publish distribution to PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n        with:\n          password: ${{ secrets.PYPI_API_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "- repo: https://github.com/pre-commit/mirrors-prettier\n  rev: \"\" # Use the sha or tag you want to point at\n  hooks:\n    - id: prettier\n"
  },
  {
    "path": "AUTHORS",
    "content": "maintainer: Paveł Tyślacki <pavel.tyslacki@gmail.com>\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# django-pg-zero-downtime-migrations Changelog\n\n## 0.19\n- added django 5.2 support\n- parallelize CI to run one job per Python version\n\n## 0.18\n- fixed unique constraint creation with the `deferrable` parameter\n- split CI into smaller jobs\n\n## 0.17\n- added django 5.1 support\n- added python 3.13 support\n- added postgres 17 support\n- marked postgres 12 support as deprecated\n- marked postgres 13 support as deprecated\n- dropped django 3.2 support\n- dropped django 4.0 support\n- dropped django 4.1 support\n- dropped python 3.6 support\n- dropped python 3.7 support\n- dropped `migrate_isnotnull_check_constraints` command\n\n## 0.16\n- changed `ADD COLUMN DEFAULT NULL` to a safe operation for code defaults\n- changed `ADD COLUMN DEFAULT NOT NULL` to a safe operation for `db_default` in django 5.0+\n- added the `ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT` setting and changed `ADD COLUMN DEFAULT NOT NULL` with this setting to a safe operation for django < 5.0\n- added the `ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP` setting and enabled dropping constraints and indexes before dropping a column or table\n- fixed `sqlmigrate` in idempotent mode\n- fixed unique constraint creation with the `include` parameter\n- fixed idempotent mode tests\n- updated unsafe migration links to the documentation\n- updated patched code to the latest django version\n- updated test image to ubuntu 24.04\n- improved README\n\n## 0.15\n- added idempotent mode and the `ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL` setting\n- fixed django 3.2 degradation due to the missing `skip_default_on_alter` method\n- improved README\n- updated the release github action\n\n## 0.14\n- fixed deferred sql errors\n- added django 5.0 support\n- added python 3.12 support\n- added postgres 16 support\n- dropped postgres 11 support\n- removed the `ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL` setting\n- marked the `migrate_isnotnull_check_constraints` command as deprecated\n\n## 0.13\n- added django 4.2 support\n- marked django 3.2 support as deprecated\n- marked django 4.0 support as deprecated\n- marked django 4.1 support as deprecated\n- marked postgres 11 support as deprecated\n- dropped postgres 10 support\n- updated the test docker image to ubuntu 22.04\n\n## 0.12\n- added support for `serial` and `integer`, `bigserial` and `bigint`, as well as `smallserial` and `smallint`, implementing the same type changes as safe migrations\n- fixed the `AutoField` type change and concurrent insertion issue for django < 4.1\n- added sequence dropping and creation timeouts, as they can be used with the `CASCADE` keyword and may affect other tables\n- added django 4.1 support\n- added python 3.11 support\n- added postgres 15 support\n- marked postgres 10 support as deprecated\n- dropped django 2.2 support\n- dropped django 3.0 support\n- dropped django 3.1 support\n- dropped postgres 9.5 support\n- dropped postgres 9.6 support\n- added github actions checks for pull requests\n\n## 0.11\n- fixed an issue where renaming a model while keeping `db_table` raised an `ALTER_TABLE_RENAME` error (#26)\n- added django 3.2 support\n- added django 4.0 support\n- added python 3.9 support\n- added python 3.10 support\n- added postgres 14 support\n- marked django 2.2 support as deprecated\n- marked django 3.0 support as deprecated\n- marked django 3.1 support as deprecated\n- marked python 3.6 support as deprecated\n- marked python 3.7 support as deprecated\n- marked postgres 9.5 support as deprecated\n- marked postgres 9.6 support as deprecated\n- switched to github actions for testing\n\n## 0.10\n- added django 3.1 support\n- added postgres 13 support\n- dropped python 3.5 support\n- updated the test environment\n\n## 0.9\n- fixed the decimal-to-float migration error\n- fixed tests for django 3.0.2 and later\n\n## 0.8\n- added django 3.0 support\n- added support for concurrent index creation and removal operations\n- added support for exclude constraints as an unsafe operation\n- dropped postgres 9.4 support\n- dropped django 2.0 support\n- dropped django 2.1 support\n- removed the deprecated `django_zero_downtime_migrations_postgres_backend` module\n\n## 0.7\n- added python 3.8 support\n- added support for postgres-specific indexes\n- improved test clarity\n- fixed regexp escaping warnings in the management command\n- fixed style checks\n- improved README\n- marked python 3.5 support as deprecated\n- marked postgres 9.4 support as deprecated\n- marked django 2.0 support as deprecated\n- marked django 2.1 support as deprecated\n\n## 0.6\n- marked the `ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL` option as deprecated for postgres 12+\n- added a management command for migrating from a `CHECK IS NOT NULL` constraint to a real `NOT NULL` constraint\n- added integration tests for postgres 12, postgres 11 (root), postgres 11 with compatible not null constraints, postgres 11 with standard not null constraints, as well as postgres 10, 9.6, 9.5, 9.4, and postgis databases\n- fixed bugs related to the deletion and creation of compatible check not null constraints via `pg_attribute`\n- minimized side effects with deferred sql execution between operations in one migration module\n- added safe `NOT NULL` constraint creation for postgres 12\n- added safe `NOT NULL` constraint creation for extra permissions for `pg_catalog.pg_attribute` when the `ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL=USE_PG_ATTRIBUTE_UPDATE_FOR_SUPERUSER` option is enabled\n- marked `AddField` with the `null=False` parameter and the compatible `CHECK IS NOT NULL` constraint option as an unsafe operation, ignoring the `ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL` value in this case\n- added versioning to the package\n- fixed pypi README image links\n- improved README\n\n## 0.5\n- extracted zero-downtime-schema logic into a mixin to allow using it with other backends\n- moved the module from `django_zero_downtime_migrations_postgres_backend` to `django_zero_downtime_migrations.backends.postgres`\n- marked the `django_zero_downtime_migrations_postgres_backend` module as deprecated\n- added support for the postgis backend\n- improved README\n\n## 0.4\n- changed the defaults for `ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT` and `ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT` from `0ms` to `None` to match the default django behavior that respects postgres timeouts\n- updated the documentation with option defaults\n- updated the documentation with best practices for option usage\n- fixed the issue where adding a nullable field with a default did not raise an error or warning\n- added links to the documentation describing the issue and safe alternative usage for errors and warnings\n- updated the documentation with type casting workarounds\n\n## 0.3\n- added django 2.2 support with the `Meta.indexes` and `Meta.constraints` attributes\n- fixed python deprecation warnings for regular expressions\n- removed the unused `TimeoutException`\n- improved README and PYPI description\n\n## 0.2\n- added an option to disable `statement_timeout` for long operations, such as index creation and constraint validation, when `statement_timeout` is set globally\n\n## 0.1.1\n- added long description content type\n\n## 0.1\n- replaced default sql queries with safer alternatives\n- added options for `statement_timeout` and `lock_timeout`\n- added an option for `NOT NULL` constraint behavior\n- added an option for restricting unsafe operations\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM --platform=linux/amd64 ubuntu:24.04\n\nENV LC_ALL=C.UTF-8\nENV LANG=C.UTF-8\nENV DEBIAN_FRONTEND=noninteractive\n\nRUN apt-get update && \\\n    apt-get install -q -y --no-install-recommends software-properties-common git gpg-agent curl && \\\n    add-apt-repository ppa:deadsnakes/ppa && \\\n    echo \"deb http://apt.postgresql.org/pub/repos/apt noble-pgdg main\" > /etc/apt/sources.list.d/pgdg.list && \\\n    curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc > /etc/apt/trusted.gpg.d/apt.postgresql.org.asc && \\\n    apt-get update && \\\n    apt-get install -q -y --no-install-recommends \\\n    python3.8 python3.8-distutils \\\n    python3.9 python3.9-distutils \\\n    python3.10 python3.10-distutils \\\n    python3.11 python3.11-distutils \\\n    python3.12 \\\n    python3.13 \\\n    python3-pip \\\n    libgdal34 \\\n    postgresql-client-17 && \\\n    rm -rf /var/lib/apt/lists/* && \\\n    pip3 install --break-system-packages setuptools tox\n\nADD . /app\nWORKDIR /app\nCMD [\"/bin/bash\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Paveł Tyślacki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include README.md\ninclude CHANGES.md\ninclude AUTHORS\ninclude LICENSE\n\nglobal-exclude __pycache__\nglobal-exclude *.py[co]\n"
  },
  {
    "path": "README.md",
    "content": "[![PyPI](https://img.shields.io/pypi/v/django-pg-zero-downtime-migrations.svg)](https://pypi.org/project/django-pg-zero-downtime-migrations/)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-pg-zero-downtime-migrations.svg)\n![PyPI - Django Version](https://img.shields.io/pypi/djversions/django-pg-zero-downtime-migrations.svg?label=django)\n![Postgres Version](https://img.shields.io/badge/postgres-12%20|%2013%20|%2014%20|%2015%20|%2016%20|%2017%20-blue.svg)\n[![PyPI - License](https://img.shields.io/pypi/l/django-pg-zero-downtime-migrations.svg)](https://raw.githubusercontent.com/tbicr/django-pg-zero-downtime-migrations/master/LICENSE)\n\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/django-pg-zero-downtime-migrations.svg)](https://pypistats.org/packages/django-pg-zero-downtime-migrations)\n[![GitHub last commit](https://img.shields.io/github/last-commit/tbicr/django-pg-zero-downtime-migrations/master.svg)](https://github.com/tbicr/django-pg-zero-downtime-migrations/commits/master)\n[![Build Status](https://github.com/tbicr/django-pg-zero-downtime-migrations/actions/workflows/check.yml/badge.svg?branch=master)](https://github.com/tbicr/django-pg-zero-downtime-migrations/actions)\n\n# django-pg-zero-downtime-migrations\n\nDjango postgresql backend that apply migrations with respect to database locks.\n\n## Installation\n\n    pip install django-pg-zero-downtime-migrations\n\n## Usage\n\nTo enable zero downtime migrations for postgres just setup django backend provided by this package and add most safe settings:\n\n    DATABASES = {\n        'default': {\n            'ENGINE': 'django_zero_downtime_migrations.backends.postgres',\n            #'ENGINE': 'django_zero_downtime_migrations.backends.postgis',\n            ...\n        }\n    }\n    ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT = '2s'\n    ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT = '2s'\n    ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT = True\n    ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE = True\n\n> _NOTE:_ this backend brings zero downtime improvements only for migrations (schema and `RunSQL` operations, but not for `RunPython` operation), for other purpose it works the same as standard django backend.\n\n> _NOTE:_ this package is in beta, please check your migrations SQL before applying on production and submit issue for any question.\n\n### Differences with standard django backend\n\nThis backend provides same result state (except of `ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT=True` usage for django < 5.0), but different way and with additional guarantees for avoiding stuck table locks.\n\nThis backend doesn't use transactions for migrations (except `RunPython` operation), because not all SQL fixes can be run in transaction and it allows to avoid deadlocks for complex migration. So when your migration will down in the middle of migration file operations you need to fix db state manually (instead potential downtime). For that reason good practice to make migration modules small as possible. Also `ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True` allows to automate manual db state fixing.\n\n### Deployment flow\n\nThere are requirements for zero downtime deployment:\n\n1. We have one database;\n1. We have several instances with application - application always should be available, even you restart one of instances;\n1. We have balancer before instances;\n1. Our application works fine before, on and after migration - old application works fine with old and new database schema version;\n1. Our application works fine before, on and after instance updating - old and new application versions work fine with new database schema version.\n\n![deployment timeline](images/timeline.png \"deployment timeline\")\n\nFlow:\n\n1. apply migrations\n1. disconnect instance form balancer, restart it and back to balancer - repeat this operation one by one for all instances\n\nIf our deployment don't satisfy zero downtime deployment rules, then we split it to smaller deployments.\n\n![deployment flow](images/deployment.gif \"deployment flow\")\n\n### Settings\n\n#### ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT\n\nApply [`lock_timeout`](https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-LOCK-TIMEOUT) for SQL statements that require `ACCESS EXCLUSIVE` lock, default `None`:\n\n    ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT = '2s'\n\nAllowed values:\n\n- `None` - current postgres setting used\n- other - timeout will be applied, `0` and equivalents mean that timeout will be disabled\n\n#### ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT\n\nApply [`statement_timeout`](https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-STATEMENT-TIMEOUT) for SQL statements that require `ACCESS EXCLUSIVE` lock, default `None`:\n\n    ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT = '2s'\n\nAllowed values:\n\n- `None` - current postgres setting used\n- other - timeout will be applied, `0` and equivalents mean that timeout will be disabled\n\n#### ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT\n\nSet [`statement_timeout`](https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-STATEMENT-TIMEOUT) to `0ms` for SQL statements that require `SHARE UPDATE EXCLUSIVE` lock that useful in case when `statement_timeout` enabled globally and you try run long-running operations like index creation or constraint validation, default `False`:\n\n    ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT = True\n\n#### ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE\n\nEnabled option doesn't allow run potential unsafe migration, default `False`:\n\n    ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE = True\n\n#### ZERO_DOWNTIME_DEFERRED_SQL\n\nDefine way to apply deferred sql, default `True`:\n\n    ZERO_DOWNTIME_DEFERRED_SQL = True\n\nAllowed values:\n- `True` - run deferred sql similar to default django way\n- `False` - run deferred sql as soon as possible\n\n#### ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL\n\nDefine idempotent mode, default `False`:\n\n    ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL = False\n\nAllowed values:\n- `True` - skip already applied sql migrations\n- `False` - standard non atomic django behaviour\n\nAs this backend doesn't use transactions for migrations any failed migration can be cause of stopped process in intermediate state.\nTo avoid manual schema manipulation idempotent mode allows to rerun failed migration after fixed issue (eg. data issue or long running CRUD queries).\n\n> _NOTE:_ idempotent mode checks rely only on name and index and constraint valid state, so it can ignore name collisions and recommended do not use it for CI checks.\n\n#### ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP\n\nDefine way to drop foreign key, unique constraints and indexes before drop table or column, default `True`:\n\n    ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP = True\n\nAllowed values:\n- `True` - before dropping table drop all foreign keys related to this table and before dropping column drop all foreign keys related to this column, unique constraints on this column and indexes used this column.\n- `False` - standard django behaviour that will drop constraints with `CASCADE` mode (some constraints can be dropped explicitly too).\n\nExplicitly dropping constraints and indexes before dropping tables or columns allows for splitting schema-only changes with an `ACCESS EXCLUSIVE` lock and the deletion of physical files, which can take significant time and cause downtime.\n\n#### ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT\n\nDefine way keep or drop code defaults on database level when adding new column, default `False`:\n\n    ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT = False\n\nAllowed values:\n- `True` - after adding column with code default this default will not be dropped, this option allows to use `ALTER TABLE ADD COLUMN SET DEFAULT NOT NULL` as safe operation that much more simple and efficient than creating column without default on database level and populating column next\n- `False` - after adding column with code default this default will be dropped, this is standard django behaviour\n\n> _NOTE:_ this option works only for django < 5.0, in django 5.0+ explicit [`db_default`](https://docs.djangoproject.com/en/dev/ref/models/fields/#db-default) should be used instead.\n\n#### PgBouncer and timeouts\n\nIn case you using [PgBouncer](https://www.pgbouncer.org/) and expect timeouts will work as expected you need make sure that run migrations using [session pool_mode](https://www.pgbouncer.org/config.html#pool_mode) or use direct database connection.\n\n## How it works\n\n### Postgres table level locks\n\nPostgres has different locks on table level that can conflict with each other https://www.postgresql.org/docs/current/static/explicit-locking.html#LOCKING-TABLES:\n\n|                          | `ACCESS SHARE` | `ROW SHARE` | `ROW EXCLUSIVE` | `SHARE UPDATE EXCLUSIVE` | `SHARE` | `SHARE ROW EXCLUSIVE` | `EXCLUSIVE` | `ACCESS EXCLUSIVE` |\n| ------------------------ | :------------: | :---------: | :-------------: | :----------------------: | :-----: | :-------------------: | :---------: | :----------------: |\n| `ACCESS SHARE`           |                |             |                 |                          |         |                       |             |         X          |\n| `ROW SHARE`              |                |             |                 |                          |         |                       |      X      |         X          |\n| `ROW EXCLUSIVE`          |                |             |                 |                          |    X    |           X           |      X      |         X          |\n| `SHARE UPDATE EXCLUSIVE` |                |             |                 |            X             |    X    |           X           |      X      |         X          |\n| `SHARE`                  |                |             |        X        |            X             |         |           X           |      X      |         X          |\n| `SHARE ROW EXCLUSIVE`    |                |             |        X        |            X             |    X    |           X           |      X      |         X          |\n| `EXCLUSIVE`              |                |      X      |        X        |            X             |    X    |           X           |      X      |         X          |\n| `ACCESS EXCLUSIVE`       |       X        |      X      |        X        |            X             |    X    |           X           |      X      |         X          |\n\n### Migration and business logic locks\n\nLets split this lock to migration and business logic operations.\n\n- Migration operations work synchronously in one thread and cover schema migrations (data migrations conflict with business logic operations same as business logic conflict concurrently).\n- Business logic operations work concurrently.\n\n#### Migration locks\n\n| lock                     | operations                                                                                            |\n| ------------------------ | ----------------------------------------------------------------------------------------------------- |\n| `ACCESS EXCLUSIVE`       | `CREATE SEQUENCE`, `DROP SEQUENCE`, `CREATE TABLE`, `DROP TABLE` \\*, `ALTER TABLE` \\*\\*, `DROP INDEX` |\n| `SHARE`                  | `CREATE INDEX`                                                                                        |\n| `SHARE UPDATE EXCLUSIVE` | `CREATE INDEX CONCURRENTLY`, `DROP INDEX CONCURRENTLY`, `ALTER TABLE VALIDATE CONSTRAINT` \\*\\*\\*      |\n\n\\*: `CREATE SEQUENCE`, `DROP SEQUENCE`, `CREATE TABLE`, `DROP TABLE` shouldn't have conflicts, because your business logic shouldn't yet operate with created tables and shouldn't already operate with deleted tables.\n\n\\*\\*: Not all `ALTER TABLE` operations take `ACCESS EXCLUSIVE` lock, but all current django's migrations take it https://github.com/django/django/blob/master/django/db/backends/base/schema.py, https://github.com/django/django/blob/master/django/db/backends/postgresql/schema.py and https://www.postgresql.org/docs/current/static/sql-altertable.html.\n\n\\*\\*\\*: Django doesn't have `VALIDATE CONSTRAINT` logic, but we will use it for some cases.\n\n#### Business logic locks\n\n| lock            | operations                   | conflict with lock                                              | conflict with operations                    |\n| --------------- | ---------------------------- | --------------------------------------------------------------- | ------------------------------------------- |\n| `ACCESS SHARE`  | `SELECT`                     | `ACCESS EXCLUSIVE`                                              | `ALTER TABLE`, `DROP INDEX`                 |\n| `ROW SHARE`     | `SELECT FOR UPDATE`          | `ACCESS EXCLUSIVE`, `EXCLUSIVE`                                 | `ALTER TABLE`, `DROP INDEX`                 |\n| `ROW EXCLUSIVE` | `INSERT`, `UPDATE`, `DELETE` | `ACCESS EXCLUSIVE`, `EXCLUSIVE`, `SHARE ROW EXCLUSIVE`, `SHARE` | `ALTER TABLE`, `DROP INDEX`, `CREATE INDEX` |\n\nSo you can find that all django schema changes for exist table conflicts with business logic, but fortunately they are safe or has safe alternative in general.\n\n### Postgres row level locks\n\nAs business logic mostly works with table rows it's also important to understand lock conflicts on row level https://www.postgresql.org/docs/current/static/explicit-locking.html#LOCKING-ROWS:\n\n| lock                | `FOR KEY SHARE` | `FOR SHARE` | `FOR NO KEY UPDATE` | `FOR UPDATE` |\n| ------------------- | :-------------: | :---------: | :-----------------: | :----------: |\n| `FOR KEY SHARE`     |                 |             |                     |      X       |\n| `FOR SHARE`         |                 |             |          X          |      X       |\n| `FOR NO KEY UPDATE` |                 |      X      |          X          |      X       |\n| `FOR UPDATE`        |        X        |      X      |          X          |      X       |\n\nMain point there is if you have two transactions that update one row, then second transaction will wait until first will be completed. So for business logic and data migrations better to avoid updates for whole table and use batch operations instead.\n\n> _NOTE:_ batch operations also can work faster because postgres can use more optimal execution plan with indexes for small data range.\n\n### Transactions FIFO waiting\n\n![postgres FIFO](images/fifo-diagram.png \"postgres FIFO\")\n\nFound same diagram in interesting article http://pankrat.github.io/2015/django-migrations-without-downtimes/.\n\nIn this diagram we can extract several metrics:\n\n1. operation time - time spent changing schema, in the case of long running operations on many rows tables like `CREATE INDEX` or `ALTER TABLE ADD CONSTRAINT`, so you need a safe equivalent.\n2. waiting time - your migration will wait until all transactions complete, so there is issue for long running operations/transactions like analytic, so you need avoid it or disable during migration.\n3. queries per second + execution time and connections pool - if executing many queries, especially long running ones, they can consume all available database connections until the lock is released, so you need different optimizations there: run migrations when least busy, decrease query count and execution time, split data.\n4. too many operations in one transaction - you have issues in all previous points for one operation so if you have many operations in one transaction then you have more likelihood to get this issue, so you need avoid too many simultaneous operations in a single transaction (or even not run it in a transaction at all but being careful when an operation fails).\n\n### Dealing with timeouts\n\nPostgres has two settings to dealing with `waiting time` and `operation time` presented in diagram: `lock_timeout` and `statement_timeout`.\n\n`SET lock_timeout TO '2s'` allow you to avoid downtime when you have long running query/transaction before run migration (https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-LOCK-TIMEOUT).\n\n`SET statement_timeout TO '2s'` allow you to avoid downtime when you have long running migration query (https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-STATEMENT-TIMEOUT).\n\n### Deadlocks\n\nThere no downtime issues for deadlocks, but too many operations in one transaction can take most conflicted lock and release it only after transaction commit or rollback. So it's a good idea to avoid `ACCESS EXCLUSIVE` lock operations and long time operations in one transaction. Deadlocks also can make you migration stuck on production deployment when different tables will be locked, for example, for FOREIGN KEY that take `ACCESS EXCLUSIVE` lock for two tables.\n\n### Rows and values storing\n\nPostgres store values of different types different ways. If you try to convert one type to another and it stored different way then postgres will rewrite all values. Fortunately some types stored same way and postgres need to do nothing to change type, but in some cases postgres need to check that all values have same with new type limitations, for example string length.\n\n### Multiversion Concurrency Control\n\nRegarding documentation https://www.postgresql.org/docs/current/static/mvcc-intro.html data consistency in postgres is maintained by using a multiversion model. This means that each SQL statement sees a snapshot of data. It has advantage for adding and deleting columns without any indexes, CONSTRAINTS and defaults do not change exist data, new version of data will be created on `INSERT` and `UPDATE`, delete just mark you record expired. All garbage will be collected later by `VACUUM` or `AUTO VACUUM`.\n\n### Django migrations hacks\n\nAny schema changes can be processed with creation of new table and copy data to it, but it can take significant time.\n\n|   # | name                                          | safe |       safe alternative        | description                                                                                                                                                                                                                                                                                                                                     |\n| --: |-----------------------------------------------|:----:|:-----------------------------:|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n|   1 | `CREATE SEQUENCE`                             |  X   |                               | safe operation, because your business logic shouldn't operate with new sequence on migration time \\*                                                                                                                                                                                                                                            |\n|   2 | `DROP SEQUENCE`                               |  X   |                               | safe operation, because your business logic shouldn't operate with this sequence on migration time \\*                                                                                                                                                                                                                                           |\n|   3 | `CREATE TABLE`                                |  X   |                               | safe operation, because your business logic shouldn't operate with new table on migration time \\*                                                                                                                                                                                                                                               |\n|   4 | `DROP TABLE`                                  |  X   |                               | safe operation, because your business logic shouldn't operate with this table on migration time \\*                                                                                                                                                                                                                                              |\n|   5 | `ALTER TABLE RENAME TO`                       |      |      use updatable view       | **unsafe operation**, because it's too hard write business logic that operate with two tables simultaneously, so propose to use temporary updatable view and switch names in transaction \\*                                                                                                                                                     |\n|   6 | `ALTER TABLE SET TABLESPACE`                  |      |  add new table and copy data  | **unsafe operation**, but probably you don't need it at all or often \\*                                                                                                                                                                                                                                                                         |\n|   7 | `ALTER TABLE ADD COLUMN`                      |  X   |                               | safe operation if without `SET NOT NULL`, `PRIMARY KEY`, `UNIQUE` \\*                                                                                                                                                                                                                                                                            |\n|   8 | `ALTER TABLE ADD COLUMN SET DEFAULT`          |  X   |                               | safe operation, however it can be unsafe if code default used within `NOT NULL`, for `db_default` or `NULL` there are no issue \\*                                                                                                                                                                                                               |\n|   9 | `ALTER TABLE ADD COLUMN SET NOT NULL`         |      |              +/-              | **unsafe operation**, because doesn't work without `SET DEFAULT` or after migration old code can insert rows without new column and raise exception, so propose to use `ALTER TABLE ADD COLUMN SET DEFAULT` with `db_default` or `ALTER TABLE ADD COLUMN` and then populate column and then `ALTER TABLE ALTER COLUMN SET NOT NULL` \\* and \\*\\* |\n|  10 | `ALTER TABLE ADD COLUMN PRIMARY KEY`          |      | add index and add constraint  | **unsafe operation**, because you spend time in migration to `CREATE INDEX`, so propose `ALTER TABLE ADD COLUMN` and then `CREATE INDEX CONCURRENTLY` and then `ALTER TABLE ADD CONSTRAINT PRIMARY KEY USING INDEX` \\*\\*\\*                                                                                                                      |\n|  11 | `ALTER TABLE ADD COLUMN UNIQUE`               |      | add index and add constraint  | **unsafe operation**, because you spend time in migration to `CREATE INDEX`, so propose `ALTER TABLE ADD COLUMN` and then `CREATE INDEX CONCURRENTLY` and then `ALTER TABLE ADD CONSTRAINT UNIQUE USING INDEX` \\*\\*\\*                                                                                                                           |\n|  12 | `ALTER TABLE ALTER COLUMN TYPE`               |      |              +/-              | **unsafe operation**, because you spend time in migration to check that all items in column valid or to change type, but some operations can be safe \\*\\*\\*\\*                                                                                                                                                                                   |\n|  13 | `ALTER TABLE ALTER COLUMN SET NOT NULL`       |      |  add check constraint before  | **unsafe operation**, because you spend time in migration to check that all items in column `NOT NULL`, so propose `ALTER TABLE ADD CONSTRAINT CHECK` and then `ALTER TABLE VALIDATE CONSTRAINT` and then `ALTER TABLE ALTER COLUMN SET NOT NULL` *\\*                                                                                           |\n|  14 | `ALTER TABLE ALTER COLUMN DROP NOT NULL`      |  X   |                               | safe operation                                                                                                                                                                                                                                                                                                                                  |\n|  15 | `ALTER TABLE ALTER COLUMN SET DEFAULT`        |  X   |                               | safe operation                                                                                                                                                                                                                                                                                                                                  |\n|  16 | `ALTER TABLE ALTER COLUMN DROP DEFAULT`       |  X   |                               | safe operation                                                                                                                                                                                                                                                                                                                                  |\n|  17 | `ALTER TABLE DROP COLUMN`                     |  X   |                               | safe operation, because your business logic shouldn't operate with this column on migration time, however better `ALTER TABLE ALTER COLUMN DROP NOT NULL`, `ALTER TABLE DROP CONSTRAINT` and `DROP INDEX` before \\* and \\*\\*\\*\\*\\*                                                                                                              |\n|  18 | `ALTER TABLE RENAME COLUMN`                   |      |      use updatable view       | **unsafe operation**, because it's too hard write business logic that operate with two columns simultaneously, so propose to use temporary updatable view and switch names in transaction \\*                                                                                                                                                    |\n|  19 | `ALTER TABLE ADD CONSTRAINT CHECK`            |      | add as not valid and validate | **unsafe operation**, because you spend time in migration to check constraint                                                                                                                                                                                                                                                                   |\n|  20 | `ALTER TABLE DROP CONSTRAINT` (`CHECK`)       |  X   |                               | safe operation                                                                                                                                                                                                                                                                                                                                  |\n|  21 | `ALTER TABLE ADD CONSTRAINT FOREIGN KEY`      |      | add as not valid and validate | **unsafe operation**, because you spend time in migration to check constraint, lock two tables                                                                                                                                                                                                                                                  |\n|  22 | `ALTER TABLE DROP CONSTRAINT` (`FOREIGN KEY`) |  X   |                               | safe operation, lock two tables                                                                                                                                                                                                                                                                                                                 |\n|  23 | `ALTER TABLE ADD CONSTRAINT PRIMARY KEY`      |      | add index and add constraint  | **unsafe operation**, because you spend time in migration to create index \\*\\*\\*                                                                                                                                                                                                                                                                |\n|  24 | `ALTER TABLE DROP CONSTRAINT` (`PRIMARY KEY`) |  X   |                               | safe operation \\*\\*\\*                                                                                                                                                                                                                                                                                                                           |\n|  25 | `ALTER TABLE ADD CONSTRAINT UNIQUE`           |      | add index and add constraint  | **unsafe operation**, because you spend time in migration to create index \\*\\*\\*                                                                                                                                                                                                                                                                |\n|  26 | `ALTER TABLE DROP CONSTRAINT` (`UNIQUE`)      |  X   |                               | safe operation \\*\\*\\*                                                                                                                                                                                                                                                                                                                           |\n|  27 | `ALTER TABLE ADD CONSTRAINT EXCLUDE`          |      |  add new table and copy data  |                                                                                                                                                                                                                                                                                                                                                 |\n|  28 | `ALTER TABLE DROP CONSTRAINT (EXCLUDE)`       |  X   |                               |                                                                                                                                                                                                                                                                                                                                                 |\n|  29 | `CREATE INDEX`                                |      |  `CREATE INDEX CONCURRENTLY`  | **unsafe operation**, because you spend time in migration to create index                                                                                                                                                                                                                                                                       |\n|  30 | `DROP INDEX`                                  |  X   |   `DROP INDEX CONCURRENTLY`   | safe operation \\*\\*\\*                                                                                                                                                                                                                                                                                                                           |\n|  31 | `CREATE INDEX CONCURRENTLY`                   |  X   |                               | safe operation                                                                                                                                                                                                                                                                                                                                  |\n|  32 | `DROP INDEX CONCURRENTLY`                     |  X   |                               | safe operation \\*\\*\\*                                                                                                                                                                                                                                                                                                                           |\n\n\\*: main point with migration on production without downtime that your old and new code should correctly work before and after migration, lets look this point closely in [Dealing with logic that should work before and after migration](#dealing-with-logic-that-should-work-before-and-after-migration) section.\n\n\\*\\*: postgres will check that all items in column `NOT NULL` that take time, lets look this point closely in [Dealing with `NOT NULL` constraint](#dealing-with-not-null-constraint) section.\n\n\\*\\*\\*: postgres will have same behaviour when you skip `ALTER TABLE ADD CONSTRAINT UNIQUE USING INDEX` and still unclear difference with `CONCURRENTLY` except difference in locks, lets look this point closely in [Dealing with `UNIQUE` constraint](#dealing-with-unique-constraint).\n\n\\*\\*\\*\\*: lets look this point closely in [Dealing with `ALTER TABLE ALTER COLUMN TYPE`](#dealing-with-alter-table-alter-column-type) section.\n\n\\*\\*\\*\\*\\*: if you check migration on CI with `python manage.py makemigrations --check` you can't drop column in code without migration creation, so in this case you can be useful _back migration flow_: apply code on all instances and then migrate database\n\n#### Dealing with logic that should work before and after migration\n\n##### Adding and removing models and columns\n\nMigrations: `CREATE SEQUENCE`, `DROP SEQUENCE`, `CREATE TABLE`, `DROP TABLE`, `ALTER TABLE ADD COLUMN`, `ALTER TABLE DROP COLUMN`.\n\nThis migrations are pretty safe, because your logic doesn't work with this data before migration\n\n##### Rename models\n\nMigrations: `ALTER TABLE RENAME TO`.\n\nStandard django's approach does not allow to operate simultaneously for old and new code with old and new table name, hopefully next workaround allows to rename table by splitting migration to few steps:\n1. provide code changes but replace standard migration with [SeparateDatabaseAndState](https://docs.djangoproject.com/en/dev/ref/migration-operations/#separatedatabaseandstate) sql operation that **in transaction** rename table and create [updatable view](https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS) that has old table name \n   - old code can work with [updatable view](https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS) by old name\n   - new code can work with table by new name \n2. after new code deployment old code is not used anymore, so we can drop view\n   - new code can work with renamed table\n\n##### Rename columns\n\nMigrations: `ALTER TABLE RENAME COLUMN`.\n\nStandard django's approach does not allow to operate simultaneously for old and new code with old and new column name, hopefully next workaround allows to rename column by splitting migration to few steps:\n1. provide code changes but replace standard migration with [SeparateDatabaseAndState](https://docs.djangoproject.com/en/dev/ref/migration-operations/#separatedatabaseandstate) sql operation that **in transaction** rename column, rename table to temporary and create [updatable view](https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS) that has old table name with both old and new columns \n   - old code can work with new [updatable view](https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS) and use old column\n   - new code can work with new [updatable view](https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS) and use new column \n2. after new code deployment old code is not used anymore, so **in transaction** we can drop view and rename table back\n   - new code can work with renamed column\n\n##### Changes for working logic\n\nMigrations: `ALTER TABLE SET TABLESPACE`, `ALTER TABLE ADD CONSTRAINT EXCLUDE`.\n\nFor this migration too hard implement logic that will work correctly for all instances, so there are two ways to dealing with it:\n\n1. create new table, copy exist data, drop old table\n2. downtime\n\n##### Create column not null\n\nMigrations: `ALTER TABLE ADD COLUMN NOT NULL`.\n\nPostgres doesn't allow to create column with `NOT NULL` if table not empty and `DEFAULT` is not provided. So you want to `ALTER TABLE ADD COLUMN DEFAULT NOT NULL`.\nDjango has two ways to create column default: [code `default`](https://docs.djangoproject.com/en/dev/ref/models/fields/#default) and [`db_default` for django 5.0+](https://docs.djangoproject.com/en/dev/ref/models/fields/#db-default).\nMain difference between them for us in operations they do for migration and old code inserts handling after migration:\n\nCode `default` migration and business logic SQL:\n```sql\n-- migration\nALTER TABLE tbl ADD COLUMN new_col integer DEFAULT 0 NOT NULL;\nALTER TABLE tbl ALTER COLUMN new_col DROP DEFAULT;\n\n-- business logic\nINSERT INTO tbl (old_col) VALUES (1);  -- old code inserts fail\nINSERT INTO tbl (old_col, new_col) VALUES (1, 1);  -- new code inserts work fine\n```\n\n`db_default` migration and business logic SQL:\n```sql\n-- migration\nALTER TABLE tbl ADD COLUMN new_col integer DEFAULT 0 NOT NULL;\n\n-- business logic\nINSERT INTO tbl (old_col) VALUES (1);  -- old code inserts work fine with default\nINSERT INTO tbl (old_col, new_col) VALUES (1, 1);  -- new code inserts work fine\n```\n\n`db_default` is most robust way to apply default and it's works fine with `NOT NULL` constraints too.\nIn django<5.0 you can use `ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT=True` to emulate `db_default` behaviour for `default` field.\n\n#### Dealing with `NOT NULL` column constraint\n\nPostgres checks that all column values `NOT NULL` (full table scan) when you are applying `ALTER TABLE ALTER COLUMN SET NOT NULL`, this check skipped if appropriate valid `CHECK CONSTRAINT` exists for postgres 12+. So to make existing column `NOT NULL` safe way you can follow next steps:\n- `ALTER TABLE ADD CONSTRAINT CHECK (column IS NOT NULL) NOT VALID` - create invalid check constraint for column, this operation takes `ACCESS EXCLUSIVE` lock only for table metadata update\n- `ALTER TABLE VALIDATE CONSTRAINT` - validate constraint, at this moment all column values should be `NOT NULL`, this operation takes `SHARE UPDATE EXCLUSIVE` lock until full table scan will be completed\n- `ALTER TABLE ALTER COLUMN SET NOT NULL` - set column `NOT NULL` don't check column values if appropriate valid `CHECK CONSTRAINT` exists, in this case this operation takes `ACCESS EXCLUSIVE` lock only for table metadata update\n- `ALTER TABLE DROP CONSTRAINT` - clean up `CHECK CONSTRAINT` that duplicates column `NOT NULL`, this operation takes `ACCESS EXCLUSIVE` lock only for table metadata update\n\n#### Dealing with `UNIQUE` constraint\n\nPostgres has two approaches for uniqueness: `CREATE UNIQUE INDEX` and `ALTER TABLE ADD CONSTRAINT UNIQUE` - both use unique index inside. Difference that we can find that we cannot apply `DROP INDEX CONCURRENTLY` for constraint. However it still unclear what difference for `DROP INDEX` and `DROP INDEX CONCURRENTLY` except difference in locks, but as we seen before both marked as safe - we don't spend time in `DROP INDEX`, just wait for lock. So as django use constraint for uniqueness we also have a hacks to use constraint safely.\n\n#### Dealing with `ALTER TABLE ALTER COLUMN TYPE`\n\nNext operations are safe:\n\n1. `varchar(LESS)` to `varchar(MORE)` where LESS < MORE\n2. `varchar(ANY)` to `text`\n3. `numeric(LESS, SAME)` to `numeric(MORE, SAME)` where LESS < MORE and SAME == SAME\n\nFor other operations propose to create new column and copy data to it. Eg. some types can be also safe, but you should check yourself.\n"
  },
  {
    "path": "django_zero_downtime_migrations/__init__.py",
    "content": "__version__ = \"0.19\"\n"
  },
  {
    "path": "django_zero_downtime_migrations/backends/__init__.py",
    "content": ""
  },
  {
    "path": "django_zero_downtime_migrations/backends/postgis/__init__.py",
    "content": ""
  },
  {
    "path": "django_zero_downtime_migrations/backends/postgis/base.py",
    "content": "from django.contrib.gis.db.backends.postgis.base import (\n    DatabaseWrapper as PostGISDatabaseWrapper\n)\n\nfrom .schema import DatabaseSchemaEditor\n\n\nclass DatabaseWrapper(PostGISDatabaseWrapper):\n    SchemaEditorClass = DatabaseSchemaEditor\n"
  },
  {
    "path": "django_zero_downtime_migrations/backends/postgis/schema.py",
    "content": "from django.contrib.gis.db.backends.postgis.schema import PostGISSchemaEditor\n\nfrom django_zero_downtime_migrations.backends.postgres.schema import (\n    DatabaseSchemaEditorMixin\n)\n\n\nclass DatabaseSchemaEditor(DatabaseSchemaEditorMixin, PostGISSchemaEditor):\n    pass\n"
  },
  {
    "path": "django_zero_downtime_migrations/backends/postgres/__init__.py",
    "content": ""
  },
  {
    "path": "django_zero_downtime_migrations/backends/postgres/base.py",
    "content": "from django.db.backends.postgresql.base import (\n    DatabaseWrapper as PostgresDatabaseWrapper\n)\n\nfrom .schema import DatabaseSchemaEditor\n\n\nclass DatabaseWrapper(PostgresDatabaseWrapper):\n    SchemaEditorClass = DatabaseSchemaEditor\n"
  },
  {
    "path": "django_zero_downtime_migrations/backends/postgres/schema.py",
    "content": "import re\nimport warnings\nfrom contextlib import contextmanager\n\nimport django\nfrom django.conf import settings\nfrom django.contrib.postgres.constraints import ExclusionConstraint\nfrom django.db.backends.ddl_references import Statement, Table\nfrom django.db.backends.postgresql.schema import (\n    DatabaseSchemaEditor as PostgresDatabaseSchemaEditor\n)\nfrom django.db.models import NOT_PROVIDED\n\n\nclass Unsafe:\n    ADD_COLUMN_NOT_NULL = (\n        \"ADD COLUMN NOT NULL is unsafe operation\\n\"\n        \"See details for safe alternative \"\n        \"https://github.com/tbicr/django-pg-zero-downtime-migrations#create-column-not-null\"\n    )\n    ALTER_COLUMN_TYPE = (\n        \"ALTER COLUMN TYPE is unsafe operation\\n\"\n        \"See details for safe alternative \"\n        \"https://github.com/tbicr/django-pg-zero-downtime-migrations#dealing-with-alter-table-alter-column-type\"\n    )\n    ADD_CONSTRAINT_EXCLUDE = (\n        \"ADD CONSTRAINT EXCLUDE is unsafe operation\\n\"\n        \"See details for safe alternative \"\n        \"https://github.com/tbicr/django-pg-zero-downtime-migrations#changes-for-working-logic\"\n    )\n    ALTER_TABLE_RENAME = (\n        \"ALTER TABLE RENAME is unsafe operation\\n\"\n        \"See details for save alternative \"\n        \"https://github.com/tbicr/django-pg-zero-downtime-migrations#rename-models\"\n    )\n    ALTER_TABLE_SET_TABLESPACE = (\n        \"ALTER TABLE SET TABLESPACE is unsafe operation\\n\"\n        \"See details for save alternative \"\n        \"https://github.com/tbicr/django-pg-zero-downtime-migrations#changes-for-working-logic\"\n    )\n    ALTER_TABLE_RENAME_COLUMN = (\n        \"ALTER TABLE RENAME COLUMN is unsafe operation\\n\"\n        \"See details for save alternative \"\n        \"https://github.com/tbicr/django-pg-zero-downtime-migrations#rename-columns\"\n    )\n\n\nclass UnsafeOperationWarning(Warning):\n    pass\n\n\nclass UnsafeOperationException(Exception):\n    pass\n\n\nclass DummySQL:\n    def __mod__(self, other):\n        return DUMMY_SQL\n\n    def format(self, *args, **kwargs):\n        return DUMMY_SQL\n\n\nDUMMY_SQL = DummySQL()\n\n\nclass Condition:\n    def __init__(self, sql, exists, idempotent_mode_only=False):\n        self.sql = sql\n        self.exists = exists\n        self.idempotent_mode_only = idempotent_mode_only\n\n    def __str__(self):\n        return self.sql\n\n    def __repr__(self):\n        return str(self)\n\n    def __mod__(self, other):\n        return self.__class__(\n            sql=self.sql % other,\n            exists=self.exists,\n            idempotent_mode_only=self.idempotent_mode_only,\n        )\n\n    def format(self, *args, **kwargs):\n        return self.__class__(\n            sql=self.sql.format(*args, **kwargs),\n            exists=self.exists,\n            idempotent_mode_only=self.idempotent_mode_only,\n        )\n\n\nclass MultiStatementSQL(list):\n\n    def __init__(self, obj, *args):\n        if args:\n            obj = [obj] + list(args)\n        super().__init__(obj)\n\n    def __str__(self):\n        return '\\n'.join(s.rstrip(';') + ';' for s in self)\n\n    def __repr__(self):\n        return str(self)\n\n    def __mod__(self, other):\n        if other is DUMMY_SQL:\n            return DUMMY_SQL\n        if isinstance(other, (list, tuple)) and any(arg is DUMMY_SQL for arg in other):\n            return DUMMY_SQL\n        if isinstance(other, dict) and any(val is DUMMY_SQL for val in other.values()):\n            return DUMMY_SQL\n        return MultiStatementSQL(s % other for s in self)\n\n    def format(self, *args, **kwargs):\n        if any(arg is DUMMY_SQL for arg in args) or any(val is DUMMY_SQL for val in kwargs.values()):\n            return DUMMY_SQL\n        return MultiStatementSQL(s.format(*args, **kwargs) for s in self)\n\n\nclass PGLock:\n\n    def __init__(\n        self,\n        sql,\n        *,\n        use_timeouts=False,\n        disable_statement_timeout=False,\n        idempotent_condition=None,\n    ):\n        self.sql = sql\n        if use_timeouts and disable_statement_timeout:\n            raise ValueError(\"Can't apply use_timeouts and disable_statement_timeout simultaneously.\")\n        self.use_timeouts = use_timeouts\n        self.disable_statement_timeout = disable_statement_timeout\n        self.idempotent_condition = idempotent_condition\n\n    def __str__(self):\n        return self.sql\n\n    def __repr__(self):\n        return str(self)\n\n    def __mod__(self, other):\n        if other is DUMMY_SQL:\n            return DUMMY_SQL\n        if isinstance(other, (list, tuple)) and any(arg is DUMMY_SQL for arg in other):\n            return DUMMY_SQL\n        if isinstance(other, dict) and any(val is DUMMY_SQL for val in other.values()):\n            return DUMMY_SQL\n        return self.__class__(\n            self.sql % other,\n            use_timeouts=self.use_timeouts,\n            disable_statement_timeout=self.disable_statement_timeout,\n            idempotent_condition=self.idempotent_condition % other\n            if self.idempotent_condition is not None else None,\n        )\n\n    def format(self, *args, **kwargs):\n        if any(arg is DUMMY_SQL for arg in args) or any(val is DUMMY_SQL for val in kwargs.values()):\n            return DUMMY_SQL\n        return self.__class__(\n            self.sql.format(*args, **kwargs),\n            use_timeouts=self.use_timeouts,\n            disable_statement_timeout=self.disable_statement_timeout,\n            idempotent_condition=self.idempotent_condition.format(*args, **kwargs)\n            if self.idempotent_condition is not None else None,\n        )\n\n\nclass PGAccessExclusive(PGLock):\n\n    def __init__(\n        self,\n        sql,\n        *,\n        use_timeouts=True,\n        disable_statement_timeout=False,\n        idempotent_condition=None,\n    ):\n        super().__init__(\n            sql,\n            use_timeouts=use_timeouts,\n            disable_statement_timeout=disable_statement_timeout,\n            idempotent_condition=idempotent_condition,\n        )\n\n\nclass PGShareUpdateExclusive(PGLock):\n\n    def __init__(\n        self,\n        sql,\n        *,\n        use_timeouts=False,\n        disable_statement_timeout=True,\n        idempotent_condition=None,\n    ):\n        super().__init__(\n            sql,\n            use_timeouts=use_timeouts,\n            disable_statement_timeout=disable_statement_timeout,\n            idempotent_condition=idempotent_condition,\n        )\n\n\nclass DatabaseSchemaEditorMixin:\n    ZERO_TIMEOUT = '0ms'\n\n    _sql_get_lock_timeout = \"SELECT setting || unit FROM pg_settings WHERE name = 'lock_timeout'\"\n    _sql_get_statement_timeout = \"SELECT setting || unit FROM pg_settings WHERE name = 'statement_timeout'\"\n    _sql_set_lock_timeout = \"SET lock_timeout TO '%(lock_timeout)s'\"\n    _sql_set_statement_timeout = \"SET statement_timeout TO '%(statement_timeout)s'\"\n\n    _sql_identity_exists = (\n        \"SELECT 1 FROM information_schema.columns \"\n        \"WHERE table_name = TRIM('\\\"' FROM '%(table)s') \"\n        \"AND column_name = TRIM('\\\"' FROM '%(column)s')\"\n        \"AND is_identity = 'YES'\"\n    )\n    _sql_sequence_exists = \"SELECT 1 FROM pg_class WHERE relname = TRIM('\\\"' FROM '%(name)s')\"\n    _sql_index_exists = \"SELECT 1 FROM pg_class WHERE relname = TRIM('\\\"' FROM '%(name)s')\"\n    _sql_table_exists = \"SELECT 1 FROM pg_class WHERE relname = TRIM('\\\"' FROM '%(table)s')\"\n    _sql_new_table_exists = \"SELECT 1 FROM pg_class WHERE relname = TRIM('\\\"' FROM '%(new_table)s')\"\n    _sql_column_exists = (\n        \"SELECT 1 FROM information_schema.columns \"\n        \"WHERE table_name = TRIM('\\\"' FROM '%(table)s') \"\n        \"AND column_name = TRIM('\\\"' FROM '%(column)s')\"\n    )\n    _sql_new_column_exists = (\n        \"SELECT 1 FROM information_schema.columns \"\n        \"WHERE table_name = TRIM('\\\"' FROM '%(table)s') \"\n        \"AND column_name = TRIM('\\\"' FROM '%(new_column)s')\"\n    )\n    _sql_constraint_exists = (\n        \"SELECT 1 FROM information_schema.table_constraints \"\n        \"WHERE table_name = TRIM('\\\"' FROM '%(table)s') \"\n        \"AND constraint_name = TRIM('\\\"' FROM '%(name)s')\"\n    )\n    _sql_index_valid = (\n        \"SELECT 1 \"\n        \"FROM pg_index \"\n        \"WHERE indrelid = TRIM('\\\"' FROM '%(table)s')::regclass::oid \"\n        \"AND indexrelid = TRIM('\\\"' FROM '%(name)s')::regclass::oid \"\n        \"AND indisvalid\"\n    )\n    _sql_constraint_valid = (\n        \"SELECT 1 \"\n        \"FROM pg_constraint \"\n        \"WHERE conrelid = TRIM('\\\"' FROM '%(table)s')::regclass::oid \"\n        \"AND conname = TRIM('\\\"' FROM '%(name)s') \"\n        \"AND convalidated\"\n    )\n\n    sql_alter_sequence_type = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_alter_sequence_type)\n    sql_add_identity = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_add_identity,\n        idempotent_condition=Condition(_sql_identity_exists, False),\n    )\n    sql_drop_indentity = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_drop_indentity)\n    sql_delete_sequence = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_delete_sequence)\n    sql_create_table = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_create_table,\n        idempotent_condition=Condition(_sql_table_exists, False),\n        use_timeouts=False,\n    )\n\n    sql_delete_table = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_table,\n        idempotent_condition=Condition(_sql_table_exists, True),\n        use_timeouts=False,\n    )\n\n    sql_rename_table = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_rename_table,\n        idempotent_condition=Condition(_sql_new_table_exists, False),\n    )\n    sql_retablespace_table = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_retablespace_table)\n\n    sql_create_column_inline_fk = None\n    sql_create_column = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_create_column,\n        idempotent_condition=Condition(_sql_column_exists, False),\n    )\n    sql_alter_column = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_alter_column)\n    sql_delete_column = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_column,\n        idempotent_condition=Condition(_sql_column_exists, True),\n    )\n    sql_rename_column = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_rename_column,\n        idempotent_condition=Condition(_sql_new_column_exists, False),\n    )\n\n    sql_create_check = MultiStatementSQL(\n        PGAccessExclusive(\n            \"ALTER TABLE %(table)s ADD CONSTRAINT %(name)s CHECK (%(check)s) NOT VALID\",\n            idempotent_condition=Condition(_sql_constraint_exists, False),\n        ),\n        PGShareUpdateExclusive(\n            \"ALTER TABLE %(table)s VALIDATE CONSTRAINT %(name)s\",\n            disable_statement_timeout=True,\n        ),\n    )\n    sql_delete_check = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_check,\n        idempotent_condition=Condition(_sql_constraint_exists, True),\n    )\n\n    if django.VERSION[:2] >= (5, 0):\n        sql_create_unique = MultiStatementSQL(\n            PGShareUpdateExclusive(\n                \"CREATE UNIQUE INDEX CONCURRENTLY %(name)s ON %(table)s (%(columns)s)%(nulls_distinct)s\",\n                idempotent_condition=Condition(_sql_index_exists, False),\n                disable_statement_timeout=True,\n            ),\n            PGShareUpdateExclusive(\n                \"REINDEX INDEX CONCURRENTLY %(name)s\",\n                idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n                disable_statement_timeout=True,\n            ),\n            PGAccessExclusive(\n                \"ALTER TABLE %(table)s ADD CONSTRAINT %(name)s UNIQUE USING INDEX %(name)s%(deferrable)s\",\n                idempotent_condition=Condition(_sql_constraint_exists, False),\n            ),\n        )\n    else:\n        sql_create_unique = MultiStatementSQL(\n            PGShareUpdateExclusive(\n                \"CREATE UNIQUE INDEX CONCURRENTLY %(name)s ON %(table)s (%(columns)s)\",\n                idempotent_condition=Condition(_sql_index_exists, False),\n                disable_statement_timeout=True,\n            ),\n            PGShareUpdateExclusive(\n                \"REINDEX INDEX CONCURRENTLY %(name)s\",\n                idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n                disable_statement_timeout=True,\n            ),\n            PGAccessExclusive(\n                \"ALTER TABLE %(table)s ADD CONSTRAINT %(name)s UNIQUE USING INDEX %(name)s%(deferrable)s\",\n                idempotent_condition=Condition(_sql_constraint_exists, False),\n            ),\n        )\n    sql_delete_unique = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_unique,\n        idempotent_condition=Condition(_sql_constraint_exists, True),\n    )\n\n    sql_create_fk = MultiStatementSQL(\n        PGAccessExclusive(\n            \"ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) \"\n            \"REFERENCES %(to_table)s (%(to_column)s)%(deferrable)s NOT VALID\",\n            idempotent_condition=Condition(_sql_constraint_exists, False),\n        ),\n        PGShareUpdateExclusive(\n            \"ALTER TABLE %(table)s VALIDATE CONSTRAINT %(name)s\",\n            disable_statement_timeout=True,\n        ),\n    )\n    sql_delete_fk = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_fk,\n        idempotent_condition=Condition(_sql_constraint_exists, True),\n    )\n\n    sql_create_pk = MultiStatementSQL(\n        PGShareUpdateExclusive(\n            \"CREATE UNIQUE INDEX CONCURRENTLY %(name)s ON %(table)s (%(columns)s)\",\n            idempotent_condition=Condition(_sql_index_exists, False),\n            disable_statement_timeout=True,\n        ),\n        PGShareUpdateExclusive(\n            \"REINDEX INDEX CONCURRENTLY %(name)s\",\n            idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n            disable_statement_timeout=True,\n        ),\n        PGAccessExclusive(\n            \"ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY USING INDEX %(name)s\",\n            idempotent_condition=Condition(_sql_constraint_exists, False),\n        ),\n    )\n    sql_delete_pk = PGAccessExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_pk,\n        idempotent_condition=Condition(_sql_constraint_exists, True),\n    )\n\n    sql_create_index = MultiStatementSQL(\n        PGShareUpdateExclusive(\n            PostgresDatabaseSchemaEditor.sql_create_index_concurrently,\n            idempotent_condition=Condition(_sql_index_exists, False),\n            disable_statement_timeout=True,\n        ),\n        PGShareUpdateExclusive(\n            \"REINDEX INDEX CONCURRENTLY %(name)s\",\n            idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n            disable_statement_timeout=True,\n        ),\n    )\n    sql_create_index_concurrently = MultiStatementSQL(\n        PGShareUpdateExclusive(\n            PostgresDatabaseSchemaEditor.sql_create_index_concurrently,\n            idempotent_condition=Condition(_sql_index_exists, False),\n            disable_statement_timeout=True,\n        ),\n        PGShareUpdateExclusive(\n            \"REINDEX INDEX CONCURRENTLY %(name)s\",\n            idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n            disable_statement_timeout=True,\n        ),\n    )\n    if django.VERSION[:2] >= (5, 0):\n        sql_create_unique_index = MultiStatementSQL(\n            PGShareUpdateExclusive(\n                \"CREATE UNIQUE INDEX CONCURRENTLY %(name)s ON %(table)s \"\n                \"(%(columns)s)%(include)s%(nulls_distinct)s%(condition)s\",\n                idempotent_condition=Condition(_sql_index_exists, False),\n                disable_statement_timeout=True,\n            ),\n            PGShareUpdateExclusive(\n                \"REINDEX INDEX CONCURRENTLY %(name)s\",\n                idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n                disable_statement_timeout=True,\n            ),\n        )\n    else:\n        sql_create_unique_index = MultiStatementSQL(\n            PGShareUpdateExclusive(\n                \"CREATE UNIQUE INDEX CONCURRENTLY %(name)s ON %(table)s \"\n                \"(%(columns)s)%(include)s%(condition)s\",\n                idempotent_condition=Condition(_sql_index_exists, False),\n                disable_statement_timeout=True,\n            ),\n            PGShareUpdateExclusive(\n                \"REINDEX INDEX CONCURRENTLY %(name)s\",\n                idempotent_condition=Condition(_sql_index_valid, False, idempotent_mode_only=True),\n                disable_statement_timeout=True,\n            ),\n        )\n    sql_delete_index = PGShareUpdateExclusive(\"DROP INDEX CONCURRENTLY IF EXISTS %(name)s\")\n    sql_delete_index_concurrently = PGShareUpdateExclusive(\n        PostgresDatabaseSchemaEditor.sql_delete_index_concurrently\n    )\n\n    sql_alter_table_comment = PGShareUpdateExclusive(PostgresDatabaseSchemaEditor.sql_alter_table_comment)\n    sql_alter_column_comment = PGShareUpdateExclusive(PostgresDatabaseSchemaEditor.sql_alter_column_comment)\n\n    _sql_column_not_null = MultiStatementSQL(\n        PGAccessExclusive(\n            \"ALTER TABLE %(table)s ADD CONSTRAINT %(name)s CHECK (%(column)s IS NOT NULL) NOT VALID\",\n            idempotent_condition=Condition(_sql_constraint_exists, False),\n        ),\n        PGShareUpdateExclusive(\n            \"ALTER TABLE %(table)s VALIDATE CONSTRAINT %(name)s\",\n            disable_statement_timeout=True,\n        ),\n        PGAccessExclusive(\"ALTER TABLE %(table)s ALTER COLUMN %(column)s SET NOT NULL\"),\n        PGAccessExclusive(\n            \"ALTER TABLE %(table)s DROP CONSTRAINT %(name)s\",\n            idempotent_condition=Condition(_sql_constraint_exists, True),\n        ),\n    )\n\n    _sql_get_table_constraints_introspection = r\"\"\"\n        SELECT\n            c.conname,\n            c.contype,\n            c.conrelid::regclass::text,\n            c.confrelid::regclass::text,\n            array(\n                SELECT attname\n                FROM unnest(c.conkey) WITH ORDINALITY cols(colid, arridx)\n                JOIN pg_attribute AS ca ON cols.colid = ca.attnum\n                WHERE ca.attrelid = c.conrelid\n                ORDER BY cols.arridx\n            ),\n            array(\n                SELECT attname\n                FROM unnest(c.confkey) WITH ORDINALITY cols(colid, arridx)\n                JOIN pg_attribute AS ca ON cols.colid = ca.attnum\n                WHERE ca.attrelid = c.confrelid\n                ORDER BY cols.arridx\n            )\n        FROM pg_constraint AS c\n        WHERE c.conrelid::regclass::text = %s\n        OR c.confrelid::regclass::text = %s\n        ORDER BY c.conrelid::regclass::text, c.conname\n    \"\"\"\n    _sql_get_index_introspection = r\"\"\"\n        SELECT\n            i.indexrelid::regclass::text,\n            i.indrelid::regclass::text,\n            array(\n                SELECT a.attname\n                FROM (\n                    SELECT unnest(i.indkey)\n                    UNION\n                    SELECT unnest(regexp_matches(i.indexprs::text, ':varattno (\\d+)', 'g'))::int\n                    UNION\n                    SELECT unnest(regexp_matches(i.indpred::text, ':varattno (\\d+)', 'g'))::int\n                ) cols(varattno)\n                INNER JOIN pg_attribute AS a ON cols.varattno = a.attnum\n                WHERE a.attrelid = i.indrelid\n            )\n        FROM pg_index i\n        LEFT JOIN pg_constraint c ON i.indexrelid = c.conindid\n        WHERE indrelid::regclass::text = %s\n        AND c.conindid IS NULL\n        ORDER BY i.indrelid::regclass::text, i.indexrelid::regclass::text\n    \"\"\"\n\n    _varchar_type_regexp = re.compile(r'^varchar\\((?P<max_length>\\d+)\\)$')\n    _numeric_type_regexp = re.compile(r'^numeric\\((?P<precision>\\d+), *(?P<scale>\\d+)\\)$')\n\n    @property\n    def sql_alter_column_no_default_null(self):\n        if self.KEEP_DEFAULT:\n            return DUMMY_SQL\n        return super().sql_alter_column_no_default_null\n\n    @property\n    def sql_alter_column_no_default(self):\n        if self.KEEP_DEFAULT:\n            return DUMMY_SQL\n        return super().sql_alter_column_no_default\n\n    def __init__(self, connection, collect_sql=False, atomic=True):\n        # Disable atomic transactions as it can be reason of downtime or deadlock\n        # in case if you combine many operation in one migration module.\n        super().__init__(connection, collect_sql=collect_sql, atomic=False)\n\n        # Avoid using DUMMY_SQL in combined alters\n        connection.features.supports_combined_alters = False\n\n        # Get settings with defaults\n        self.LOCK_TIMEOUT = getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT\", None)\n        self.STATEMENT_TIMEOUT = getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT\", None)\n        self.FLEXIBLE_STATEMENT_TIMEOUT = getattr(\n            settings, \"ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT\", False)\n        self.RAISE_FOR_UNSAFE = getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE\", False)\n        self.DEFERRED_SQL = getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_DEFERRED_SQL\", True)\n        self.IDEMPOTENT_SQL = (\n            getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL\", False)\n            if not collect_sql else\n            False  # disable idempotent mode for sqlmigrate\n        )\n        self.KEEP_DEFAULT = getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT\", False)\n        if django.VERSION[:2] >= (5, 0) and hasattr(settings, 'ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT'):\n            warnings.warn(\n                'settings.ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT is not applicable for django 5.0+. '\n                'Please remove this setting.',\n                DeprecationWarning,\n            )\n            self.KEEP_DEFAULT = False\n        self.EXPLICIT_CONSTRAINTS_DROP = getattr(settings, \"ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP\", True)\n\n    def execute(self, sql, params=()):\n        if sql is DUMMY_SQL:\n            return\n        statements = []\n        if isinstance(sql, MultiStatementSQL):\n            statements.extend(sql)\n        elif isinstance(sql, Statement) and isinstance(sql.template, MultiStatementSQL):\n            statements.extend(Statement(s, **sql.parts) for s in sql.template)\n        else:\n            statements.append(sql)\n        for statement in statements:\n            idempotent_condition = None\n            if isinstance(statement, PGLock):\n                use_timeouts = statement.use_timeouts\n                disable_statement_timeout = statement.disable_statement_timeout\n                idempotent_condition = statement.idempotent_condition\n                statement = statement.sql\n            elif isinstance(statement, Statement) and isinstance(statement.template, PGLock):\n                use_timeouts = statement.template.use_timeouts\n                disable_statement_timeout = statement.template.disable_statement_timeout\n                if statement.template.idempotent_condition is not None:\n                    idempotent_condition = statement.template.idempotent_condition % statement.parts\n                statement = Statement(statement.template.sql, **statement.parts)\n            else:\n                use_timeouts = False\n                disable_statement_timeout = False\n\n            if not self._skip_applied(idempotent_condition):\n                if use_timeouts:\n                    with self._set_operation_timeout(self.STATEMENT_TIMEOUT, self.LOCK_TIMEOUT):\n                        super().execute(statement, params)\n                elif disable_statement_timeout and self.FLEXIBLE_STATEMENT_TIMEOUT:\n                    with self._set_operation_timeout(self.ZERO_TIMEOUT):\n                        super().execute(statement, params)\n                else:\n                    super().execute(statement, params)\n\n    def _skip_applied(self, idempotent_condition: Condition) -> bool:\n        if idempotent_condition is None:\n            return False\n\n        if not self.IDEMPOTENT_SQL:\n            # in case of failure of creating indexes concurrently index will be created but will be invalid\n            # for this case reindex statement added to recreate valid index in IDEMPOTENT_SQL mode\n            # but if IDEMPOTENT_SQL mode is disabled we need to skip this extra reindex sql\n            return idempotent_condition.idempotent_mode_only\n\n        with self.connection.cursor() as cursor:\n            cursor.execute(idempotent_condition.sql)\n            exists = cursor.fetchone() is not None\n            if idempotent_condition.exists:\n                return not exists\n            return exists\n\n    @contextmanager\n    def _set_operation_timeout(self, statement_timeout=None, lock_timeout=None):\n        if self.collect_sql:\n            previous_statement_timeout = self.ZERO_TIMEOUT\n            previous_lock_timeout = self.ZERO_TIMEOUT\n        else:\n            with self.connection.cursor() as cursor:\n                cursor.execute(self._sql_get_statement_timeout)\n                previous_statement_timeout, = cursor.fetchone()\n                cursor.execute(self._sql_get_lock_timeout)\n                previous_lock_timeout, = cursor.fetchone()\n        if statement_timeout is not None:\n            self.execute(self._sql_set_statement_timeout % {\"statement_timeout\": statement_timeout})\n        if lock_timeout is not None:\n            self.execute(self._sql_set_lock_timeout % {\"lock_timeout\": lock_timeout})\n        yield\n        if statement_timeout is not None:\n            self.execute(self._sql_set_statement_timeout % {\"statement_timeout\": previous_statement_timeout})\n        if lock_timeout is not None:\n            self.execute(self._sql_set_lock_timeout % {\"lock_timeout\": previous_lock_timeout})\n\n    def _flush_deferred_sql(self):\n        \"\"\"As some alternative sql use deferred sql and deferred sql run after all operations in migration module\n         so good idea to run deferred sql as soon as possible to provide similar as possible state\n         between operations in migration module. But this approach can be reason of errors for some migrations.\n\n         As only constraints creation placed in deferred sql\n         it looks safe to keep standard django deferred sql run approach.\n\n         # TODO: drop option to run deferred sql as soon as possible in future\n         \"\"\"\n        if not self.DEFERRED_SQL:\n            for sql in self.deferred_sql:\n                self.execute(sql)\n            self.deferred_sql.clear()\n\n    def _get_constraints(self, cursor, model):\n        cursor.execute(self._sql_get_table_constraints_introspection, [model._meta.db_table, model._meta.db_table])\n        for constraint, kind, table, table_ref, columns, columns_ref in cursor.fetchall():\n            yield constraint, kind, table, table_ref, columns, columns_ref\n\n    def _get_indexes(self, cursor, model):\n        cursor.execute(self._sql_get_index_introspection, [model._meta.db_table])\n        for index, table, columns in cursor.fetchall():\n            yield index, table, columns\n\n    def _drop_collect_sql_introspection_related_duplicates(self, drop_constraint_queries):\n        \"\"\"\n            django internals use introspection to find related constraints and perform action if constraint exists\n            dropping constraints before dropping table or column can duplicate same logic in django internals\n            in this case for sqlmigrate drop constraint sql can be duplicated as no physical constraint drop perform\n            so just remove constraint drop duplicates for sqlmigrate\n        \"\"\"\n        if self.collect_sql:\n            handled_queries = set()\n            drops = set()\n            for i in range(len(self.collected_sql)):\n                for j in range(len(drop_constraint_queries)):\n                    if all(\n                        self.collected_sql[i + k] == drop_constraint_queries[j][k]\n                        for k in range(len(drop_constraint_queries[j]))\n                    ):\n                        if j in handled_queries:\n                            drops |= {i + k for k in range(len(drop_constraint_queries[j]))}\n                        handled_queries.add(j)\n            self.collected_sql = [query for i, query in enumerate(self.collected_sql) if i not in drops]\n\n    def create_model(self, model):\n        super().create_model(model)\n        self._flush_deferred_sql()\n\n    def delete_model(self, model):\n        drop_constraint_queries = []\n        if self.EXPLICIT_CONSTRAINTS_DROP:\n            with self.connection.cursor() as cursor:\n                for constraint, kind, table, table_ref, columns, columns_ref in self._get_constraints(cursor, model):\n                    if kind == \"f\":\n                        last_collected_sql = len(self.collected_sql) if self.collect_sql else None\n                        self.execute(Statement(\n                            self.sql_delete_fk,\n                            table=Table(table, self.quote_name),\n                            name=self.quote_name(constraint),\n                        ))\n                        if self.collect_sql:\n                            drop_constraint_queries.append(self.collected_sql[last_collected_sql:])\n        super().delete_model(model)\n        self._flush_deferred_sql()\n        self._drop_collect_sql_introspection_related_duplicates(drop_constraint_queries)\n\n    def alter_index_together(self, model, old_index_together, new_index_together):\n        super().alter_index_together(model, old_index_together, new_index_together)\n        self._flush_deferred_sql()\n\n    def alter_unique_together(self, model, old_unique_together, new_unique_together):\n        super().alter_unique_together(model, old_unique_together, new_unique_together)\n        self._flush_deferred_sql()\n\n    def add_index(self, model, index, concurrently=False):\n        super().add_index(model, index, concurrently=concurrently)\n        self._flush_deferred_sql()\n\n    def remove_index(self, model, index, concurrently=False):\n        super().remove_index(model, index, concurrently=concurrently)\n        self._flush_deferred_sql()\n\n    def add_constraint(self, model, constraint):\n        if isinstance(constraint, ExclusionConstraint):\n            if self.RAISE_FOR_UNSAFE:\n                raise UnsafeOperationException(Unsafe.ADD_CONSTRAINT_EXCLUDE)\n            else:\n                warnings.warn(UnsafeOperationWarning(Unsafe.ADD_CONSTRAINT_EXCLUDE))\n        super().add_constraint(model, constraint)\n        self._flush_deferred_sql()\n\n    def remove_constraint(self, model, constraint):\n        super().remove_constraint(model, constraint)\n        self._flush_deferred_sql()\n\n    def add_field(self, model, field):\n        super().add_field(model, field)\n        self._flush_deferred_sql()\n\n    def remove_field(self, model, field):\n        drop_constraint_queries = []\n        if self.EXPLICIT_CONSTRAINTS_DROP:\n            with self.connection.cursor() as cursor:\n                # as foreign key can have index as dependent object it important to drop all foreign keys first\n                for constraint, kind, table, table_ref, columns, columns_ref in self._get_constraints(cursor, model):\n                    # drop foreign key for current model columns\n                    if kind == \"f\" and table == model._meta.db_table and field.column in columns:\n                        last_collected_sql = len(self.collected_sql) if self.collect_sql else None\n                        self.execute(Statement(\n                            self.sql_delete_fk,\n                            table=Table(table, self.quote_name),\n                            name=self.quote_name(constraint),\n                        ))\n                        if self.collect_sql:\n                            drop_constraint_queries.append(self.collected_sql[last_collected_sql:])\n                    # drop foreign key for target model columns, i.e. backrefs\n                    if kind == \"f\" and table_ref == model._meta.db_table and field.column in columns_ref:\n                        last_collected_sql = len(self.collected_sql) if self.collect_sql else None\n                        self.execute(Statement(\n                            self.sql_delete_fk,\n                            table=Table(table, self.quote_name),\n                            name=self.quote_name(constraint),\n                        ))\n                        if self.collect_sql:\n                            drop_constraint_queries.append(self.collected_sql[last_collected_sql:])\n                for constraint, kind, table, table_ref, columns, columns_ref in self._get_constraints(cursor, model):\n                    # drop unique constraints for current model columns\n                    if kind == \"u\" and table == model._meta.db_table and field.column in columns:\n                        last_collected_sql = len(self.collected_sql) if self.collect_sql else None\n                        self.execute(Statement(\n                            self.sql_delete_unique,\n                            table=Table(table, self.quote_name),\n                            name=self.quote_name(constraint),\n                        ))\n                        if self.collect_sql:\n                            drop_constraint_queries.append(self.collected_sql[last_collected_sql:])\n                for index, table, columns in self._get_indexes(cursor, model):\n                    # drop indexes for current model columns\n                    if table == model._meta.db_table and field.column in columns:\n                        last_collected_sql = len(self.collected_sql) if self.collect_sql else None\n                        self.execute(Statement(\n                            self.sql_delete_index_concurrently,\n                            table=Table(table, self.quote_name),\n                            name=self.quote_name(index),\n                        ))\n                        if self.collect_sql:\n                            drop_constraint_queries.append(self.collected_sql[last_collected_sql:])\n        super().remove_field(model, field)\n        self._flush_deferred_sql()\n        self._drop_collect_sql_introspection_related_duplicates(drop_constraint_queries)\n\n    def alter_field(self, model, old_field, new_field, strict=False):\n        super().alter_field(model, old_field, new_field, strict)\n        self._flush_deferred_sql()\n\n    def alter_db_table(self, model, old_db_table, new_db_table):\n        # Disregard cases where db_table is unchanged\n        if old_db_table != new_db_table:\n            if self.RAISE_FOR_UNSAFE:\n                raise UnsafeOperationException(Unsafe.ALTER_TABLE_RENAME)\n            else:\n                warnings.warn(UnsafeOperationWarning(Unsafe.ALTER_TABLE_RENAME))\n        super().alter_db_table(model, old_db_table, new_db_table)\n        self._flush_deferred_sql()\n\n    def alter_db_tablespace(self, model, old_db_tablespace, new_db_tablespace):\n        if self.RAISE_FOR_UNSAFE:\n            raise UnsafeOperationException(Unsafe.ALTER_TABLE_SET_TABLESPACE)\n        else:\n            warnings.warn(UnsafeOperationWarning(Unsafe.ALTER_TABLE_SET_TABLESPACE))\n        super().alter_db_tablespace(model, old_db_tablespace, new_db_tablespace)\n        self._flush_deferred_sql()\n\n    def alter_db_table_comment(self, model, old_db_table_comment, new_db_table_comment):\n        super().alter_db_table_comment(model, old_db_table_comment, new_db_table_comment)\n        self._flush_deferred_sql()\n\n    def _rename_field_sql(self, table, old_field, new_field, new_type):\n        if self.RAISE_FOR_UNSAFE:\n            raise UnsafeOperationException(Unsafe.ALTER_TABLE_RENAME_COLUMN)\n        else:\n            warnings.warn(UnsafeOperationWarning(Unsafe.ALTER_TABLE_RENAME_COLUMN))\n        return super()._rename_field_sql(table, old_field, new_field, new_type)\n\n    def _has_db_default(self, field):\n        if django.VERSION < (5, 0):\n            if self.KEEP_DEFAULT:\n                return field.default is not NOT_PROVIDED\n            return False\n        if django.VERSION >= (5, 2):\n            return field.has_db_default()\n        return field.db_default is not NOT_PROVIDED\n\n    def _add_column_not_null(self, model, field):\n        if not self._has_db_default(field):\n            if self.RAISE_FOR_UNSAFE:\n                raise UnsafeOperationException(Unsafe.ADD_COLUMN_NOT_NULL)\n            else:\n                warnings.warn(UnsafeOperationWarning(Unsafe.ADD_COLUMN_NOT_NULL))\n        return \"NOT NULL\"\n\n    def _add_column_primary_key(self, model, field):\n        self.deferred_sql.append(self.sql_create_pk % {\n            \"table\": self.quote_name(model._meta.db_table),\n            \"name\": self.quote_name(self._create_index_name(model._meta.db_table, [field.column], suffix=\"_pk\")),\n            \"columns\": self.quote_name(field.column),\n        })\n        return \"\"\n\n    def _add_column_unique(self, model, field):\n        self.deferred_sql.append(self._create_unique_sql(model, [field]))\n        return \"\"\n\n    def _patched_iter_column_sql(\n        self, column_db_type, params, model, field, field_db_params, include_default\n    ):\n        yield column_db_type\n        if collation := field_db_params.get(\"collation\"):\n            yield self._collate_sql(collation)\n        if self.connection.features.supports_comments_inline and field.db_comment:\n            yield self._comment_sql(field.db_comment)\n        # Work out nullability.\n        null = field.null\n        # Add database default.\n        if (\n            django.VERSION >= (5, 2) and field.has_db_default()\n            or django.VERSION >= (5, 0) and field.db_default is not NOT_PROVIDED\n        ):\n            default_sql, default_params = self.db_default_sql(field)\n            yield f\"DEFAULT {default_sql}\"\n            params.extend(default_params)\n            include_default = False\n        # Include a default value, if requested.\n        include_default = (\n            include_default\n            and not self.skip_default(field)\n            and\n            # Don't include a default value if it's a nullable field and the\n            # default cannot be dropped in the ALTER COLUMN statement (e.g.\n            # MySQL longtext and longblob).\n            not (null and self.skip_default_on_alter(field))\n        )\n        if include_default:\n            default_value = self.effective_default(field)\n            if default_value is not None:\n                column_default = \"DEFAULT \" + self._column_default_sql(field)\n                if self.connection.features.requires_literal_defaults:\n                    # Some databases can't take defaults as a parameter\n                    # (Oracle, SQLite). If this is the case, the individual\n                    # schema backend should implement prepare_default().\n                    yield column_default % self.prepare_default(default_value)\n                else:\n                    yield column_default\n                    params.append(default_value)\n        # Oracle treats the empty string ('') as null, so coerce the null\n        # option whenever '' is a possible value.\n        if (\n            field.empty_strings_allowed\n            and not field.primary_key\n            and self.connection.features.interprets_empty_strings_as_nulls\n        ):\n            null = True\n        if django.VERSION >= (5, 0) and field.generated:\n            generated_sql, generated_params = self._column_generated_sql(field)\n            params.extend(generated_params)\n            yield generated_sql\n        elif not null:\n            yield self._add_column_not_null(model, field)  # different to origin method\n        elif not self.connection.features.implied_column_null:\n            yield \"NULL\"\n        if field.primary_key:\n            self._add_column_primary_key(model, field)  # different to origin method\n        elif field.unique:\n            self._add_column_unique(model, field)  # different to origin method\n        # Optionally add the tablespace if it's an implicitly indexed column.\n        tablespace = field.db_tablespace or model._meta.db_tablespace\n        if (\n            tablespace\n            and self.connection.features.supports_tablespaces\n            and field.unique\n        ):\n            yield self.connection.ops.tablespace_sql(tablespace, inline=True)\n\n    def _iter_column_sql(\n            self, column_db_type, params, model, field, field_db_params, include_default\n    ):\n        if not include_default:\n            yield from super()._iter_column_sql(\n                column_db_type,\n                params,\n                model,\n                field,\n                field_db_params,\n                include_default,\n            )\n        else:\n            yield from self._patched_iter_column_sql(\n                column_db_type,\n                params,\n                model,\n                field,\n                field_db_params,\n                include_default,\n            )\n\n    def _alter_column_set_not_null(self, model, new_field):\n        self.deferred_sql.append(self._sql_column_not_null % {\n            \"column\": self.quote_name(new_field.column),\n            \"table\": self.quote_name(model._meta.db_table),\n            \"name\": self.quote_name(\n                self._create_index_name(model._meta.db_table, [new_field.column], suffix=\"_notnull\")\n            ),\n        })\n        return DUMMY_SQL, []\n\n    def _alter_column_drop_not_null(self, model, new_field):\n        return self.sql_alter_column_null % {\n            \"column\": self.quote_name(new_field.column),\n        }, []\n\n    def _alter_column_null_sql(self, model, old_field, new_field):\n        if new_field.null:\n            return self._alter_column_drop_not_null(model, new_field)\n        else:\n            return self._alter_column_set_not_null(model, new_field)\n\n    def _immediate_type_cast(self, old_type, new_type):\n        if (\n            (old_type == new_type) or\n            (old_type == 'integer' and new_type == 'serial') or\n            (old_type == 'bigint' and new_type == 'bigserial') or\n            (old_type == 'smallint' and new_type == 'smallserial') or\n            (old_type == 'serial' and new_type == 'integer') or\n            (old_type == 'bigserial' and new_type == 'bigint') or\n            (old_type == 'smallserial' and new_type == 'smallint')\n        ):\n            return True\n        old_type_varchar_match = self._varchar_type_regexp.match(old_type)\n        if old_type_varchar_match:\n            if new_type == \"text\":\n                return True\n            new_type_varchar_match = self._varchar_type_regexp.match(new_type)\n            if new_type_varchar_match:\n                old_type_max_length = int(old_type_varchar_match.group(\"max_length\"))\n                new_type_max_length = int(new_type_varchar_match.group(\"max_length\"))\n                if new_type_max_length >= old_type_max_length:\n                    return True\n                else:\n                    return False\n        old_type_numeric_match = self._numeric_type_regexp.match(old_type)\n        if old_type_numeric_match:\n            new_type_numeric_match = self._numeric_type_regexp.match(new_type)\n            old_type_precision = int(old_type_numeric_match.group(\"precision\"))\n            old_type_scale = int(old_type_numeric_match.group(\"scale\"))\n            try:\n                new_type_precision = int(new_type_numeric_match.group(\"precision\"))\n                new_type_scale = int(new_type_numeric_match.group(\"scale\"))\n            except AttributeError:\n                return False\n            return new_type_precision >= old_type_precision and new_type_scale == old_type_scale\n        return False\n\n    def _alter_column_type_sql(self, model, old_field, new_field, new_type, old_collation, new_collation):\n        old_db_params = old_field.db_parameters(connection=self.connection)\n        old_type = old_db_params[\"type\"]\n        if not self._immediate_type_cast(old_type, new_type):\n            if self.RAISE_FOR_UNSAFE:\n                raise UnsafeOperationException(Unsafe.ALTER_COLUMN_TYPE)\n            else:\n                warnings.warn(UnsafeOperationWarning(Unsafe.ALTER_COLUMN_TYPE))\n        return super()._alter_column_type_sql(model, old_field, new_field, new_type, old_collation, new_collation)\n\n\nclass DatabaseSchemaEditor(DatabaseSchemaEditorMixin, PostgresDatabaseSchemaEditor):\n    pass\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "services:\n  pg17:\n    image: postgres:17-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  pg16:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  pg15:\n    image: postgres:15-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  pg14:\n    image: postgres:14-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  pg13:\n    image: postgres:13-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  pg12:\n    image: postgres:12-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  pg11:\n    image: postgres:11-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  postgis17:\n    image: postgis/postgis:17-3.5-alpine\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n    volumes:\n      - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql\n\n  django-pg-zero-downtime-migrations-tests:\n    image: django-pg-zero-downtime-migrations:latest\n    build: .\n    depends_on:\n      - pg17\n      - pg16\n      - pg15\n      - pg14\n      - pg13\n      - pg12\n      - pg11\n      - postgis17\n    volumes:\n      - .:/app\n"
  },
  {
    "path": "docker_postgres_init.sql",
    "content": "CREATE USER test WITH PASSWORD 'test' CREATEDB;\n"
  },
  {
    "path": "manage.py",
    "content": "#!/usr/bin/env python\n\"\"\"Django's command-line utility for administrative tasks.\"\"\"\nimport os\nimport sys\n\n\ndef main():\n    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tests.settings_make_migrations')\n    try:\n        from django.core.management import execute_from_command_line\n    except ImportError as exc:\n        raise ImportError(\n            \"Couldn't import Django. Are you sure it's installed and \"\n            \"available on your PYTHONPATH environment variable? Did you \"\n            \"forget to activate a virtual environment?\"\n        ) from exc\n    execute_from_command_line(sys.argv)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "setup.cfg",
    "content": "[flake8]\nmax-line-length = 120\nexclude = .tox/,venv/\n\n[isort]\ncombine_as_imports = true\nknown_django = django\nknown_first_party = django_zero_downtime_migrations,tests\nsections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER\ndefault_section = THIRDPARTY\nline_length = 79\nmulti_line_output = 5\nskip = .tox/,venv/\n\n[tool:pytest]\naddopts = --verbose\npython_files = tests/*/test*.py\nDJANGO_SETTINGS_MODULE = tests.settings\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import find_packages, setup\n\nVERSION = __import__('django_zero_downtime_migrations').__version__\n\n\ndef _replace_internal_images_with_external(text):\n    return text.replace(\n        '(images/',\n        '(https://raw.githubusercontent.com/tbicr/django-pg-zero-downtime-migrations/'\n        '{VERSION}/images/'.format(VERSION=VERSION),\n    )\n\n\ndef _get_long_description():\n    with open('README.md') as readme_handle:\n        readme = readme_handle.read()\n    with open('CHANGES.md') as changes_handle:\n        changes = changes_handle.read()\n    return _replace_internal_images_with_external(readme) + '\\n\\n' + changes\n\n\nsetup(\n    name='django-pg-zero-downtime-migrations',\n    version=VERSION,\n    author='Paveł Tyślacki',\n    author_email='pavel.tyslacki@gmail.com',\n    license='MIT',\n    url='https://github.com/tbicr/django-pg-zero-downtime-migrations',\n    description='Django postgresql backend that apply migrations with respect to database locks',\n    long_description=_get_long_description(),\n    long_description_content_type='text/markdown',\n    classifiers=[\n        'Development Status :: 4 - Beta',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Operating System :: OS Independent',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: 3.8',\n        'Programming Language :: Python :: 3.9',\n        'Programming Language :: Python :: 3.10',\n        'Programming Language :: Python :: 3.11',\n        'Programming Language :: Python :: 3.12',\n        'Programming Language :: Python :: 3.13',\n        'Framework :: Django',\n        'Framework :: Django :: 4.2',\n        'Framework :: Django :: 5.0',\n        'Framework :: Django :: 5.1',\n        'Framework :: Django :: 5.2',\n    ],\n    keywords='django postgres postgresql migrations',\n    packages=find_packages(exclude=['manage*', 'tests*']),\n    python_requires='>=3.8',\n    install_requires=[\n        'django>=4.2',\n    ]\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "from django.conf import settings\n\nimport pytest\n\nskip_for_default_django_backend = pytest.mark.skipif(\n    settings.DATABASES['default']['ENGINE'] in (\n        'django.db.backends.postgresql',\n        'django.contrib.gis.db.backends.postgis',\n    ),\n    reason='not actual for default django backends'\n)\n"
  },
  {
    "path": "tests/apps/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_default_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_default_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:47\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_default_app/migrations/0002_add_field_default.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:47\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_flow_add_column_with_default_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(default=0),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_default_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_default_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    field = models.IntegerField(default=0, null=False)\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:45\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_app/migrations/0002_add_field_notnull.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:46\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_flow_add_column_with_notnull_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    field = models.IntegerField()\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_default_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_default_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:41\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_default_app/migrations/0002_add_field_notnull_default.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:43\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_flow_add_column_with_notnull_default_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(default=0),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_default_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_add_column_with_notnull_default_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    field = models.IntegerField(default=0)\n"
  },
  {
    "path": "tests/apps/bad_flow_change_char_type_that_unsafe_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_change_char_type_that_unsafe_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:37\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('field', models.CharField(max_length=120)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_change_char_type_that_unsafe_app/migrations/0002_change_type_from_char120_to_char100.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:38\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_flow_change_char_type_that_unsafe_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name='testtable',\n            name='field',\n            field=models.CharField(max_length=100),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_flow_change_char_type_that_unsafe_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_flow_change_char_type_that_unsafe_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    field = models.CharField(max_length=100)\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_change_char_type_that_safe_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_rollback_flow_change_char_type_that_safe_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:26\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('char_filed', models.CharField(max_length=100)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_change_char_type_that_safe_app/migrations/0002_change_type_safe_from_char100_to_char120.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:27\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_rollback_flow_change_char_type_that_safe_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name='testtable',\n            name='char_filed',\n            field=models.CharField(max_length=120),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_change_char_type_that_safe_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_rollback_flow_change_char_type_that_safe_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    char_filed = models.CharField(max_length=120)\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:00\n\nfrom django.db import migrations, models\n\n\ndef insert_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('bad_rollback_flow_drop_column_with_notnull_app', 'TestTable')\n    TestTable.objects.using(db_alias).create(field=1)\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('field', models.IntegerField()),\n            ],\n        ),\n        migrations.RunPython(insert_objects, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_app/migrations/0002_drop_field_not_null.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:02\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_rollback_flow_drop_column_with_notnull_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    pass\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_default_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_default_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:19\n\nfrom django.db import migrations, models\n\n\ndef insert_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('bad_rollback_flow_drop_column_with_notnull_default_app', 'TestTable')\n    TestTable.objects.using(db_alias).create(field=1)\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('field', models.IntegerField(default=0)),\n            ],\n        ),\n        migrations.RunPython(insert_objects, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_default_app/migrations/0002_drop_field_not_null_default.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 21:20\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('bad_rollback_flow_drop_column_with_notnull_default_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_default_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/bad_rollback_flow_drop_column_with_notnull_default_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    pass\n"
  },
  {
    "path": "tests/apps/decimal_to_float_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/decimal_to_float_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='Value',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('amount', models.DecimalField(blank=True, decimal_places=4, default=None, max_digits=12, null=True)),\n            ],\n            options={\n                'abstract': False,\n            },\n        ),\n\n    ]\n"
  },
  {
    "path": "tests/apps/decimal_to_float_app/migrations/0002_type_conversion.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('decimal_to_float_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name='value',\n            name='amount',\n            field=models.FloatField(blank=True, default=None, null=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/decimal_to_float_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/decimal_to_float_app/models.py",
    "content": "from django.db import models\n\n\nclass Value(models.Model):\n    amount = models.FloatField(\n        null=True,\n        default=None,\n        blank=True,\n    )\n"
  },
  {
    "path": "tests/apps/good_flow_alter_table_with_same_db_table/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_alter_table_with_same_db_table/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2021-12-30 13:37\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('field', models.IntegerField()),\n            ],\n            options={\n                'db_table': 'test_table',\n            },\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_alter_table_with_same_db_table/migrations/0002_rename_model.py",
    "content": "# Generated by Django 3.1 on 2021-12-30 23:59\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_alter_table_with_same_db_table', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.RenameModel(\n            old_name='TestTable',\n            new_name='TestTableRenamed',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_alter_table_with_same_db_table/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_alter_table_with_same_db_table/models.py",
    "content": "from django.db import models\n\n\nclass TestTableRenamed(models.Model):\n\n    class Meta:\n        db_table = 'test_table'\n"
  },
  {
    "path": "tests/apps/good_flow_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:09\n\nimport django.contrib.postgres.search\nfrom django.db import migrations, models\n\n\ndef insert_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    TestTable.objects.using(db_alias).create(test_field_int=1)\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='RelatedTestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n            ],\n        ),\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('test_field_int', models.IntegerField()),\n                ('test_field_str', models.CharField(max_length=10)),\n                ('test_field_tsv', django.contrib.postgres.search.SearchVectorField()),\n            ],\n        ),\n        migrations.RunPython(insert_objects, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0002_add_nullable_field.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:15\n\nfrom django.db import migrations, models\n\n\ndef insert_objects_and_null_check(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1)\n    assert instance.field is None\n    instance.delete()\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0001_initial'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(null=True),\n        ),\n        migrations.RunPython(insert_objects_and_null_check, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0003_set_field_default.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:16\n\nfrom django.db import migrations, models\n\n\ndef insert_objects_and_default_check(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1)\n    assert instance.field == 0\n    instance.delete()\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1, field=None)\n    assert instance.field is None\n    instance.delete()\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0002_add_nullable_field'),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(default=0, null=True),\n        ),\n        migrations.RunPython(insert_objects_and_default_check, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0004_set_field_not_null.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:16\n\nfrom django.db import IntegrityError, migrations, models\n\n\ndef flush_deferred_sql(apps, schema_editor):\n    for sql in schema_editor.deferred_sql:\n        schema_editor.execute(sql)\n\n\ndef update_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    TestTable.objects.using(db_alias).update(field=0)\n\n\ndef insert_objects_and_not_null_check(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1)\n    assert instance.field == 0\n    instance.delete()\n    try:\n        TestTable.objects.using(db_alias).create(test_field_int=1, field=None)\n        assert False\n    except IntegrityError:\n        pass\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False  # avoid transaction issue for default django backend check\n\n    dependencies = [\n        ('good_flow_app', '0003_set_field_default'),\n    ]\n\n    operations = [\n        migrations.RunPython(update_objects, migrations.RunPython.noop),\n        migrations.AlterField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(default=0),\n        ),\n        migrations.RunPython(flush_deferred_sql, migrations.RunPython.noop),\n        migrations.RunPython(insert_objects_and_not_null_check, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0005_drop_field_not_null.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:17\n\nfrom django.db import migrations, models\n\n\ndef insert_objects_and_default_check(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1)\n    assert instance.field == 0\n    instance.delete()\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1, field=None)\n    assert instance.field is None\n    instance.delete()\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0004_set_field_not_null'),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(default=0, null=True),\n        ),\n        migrations.RunPython(insert_objects_and_default_check, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0006_drop_field_default.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:17\n\nfrom django.db import migrations, models\n\n\ndef insert_objects_and_null_check(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app', 'TestTable')\n    instance = TestTable.objects.using(db_alias).create(test_field_int=1)\n    assert instance.field is None\n    instance.delete()\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0005_drop_field_not_null'),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(null=True),\n        ),\n        migrations.RunPython(insert_objects_and_null_check, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0007_drop_field.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:18\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0006_drop_field_default'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0008_add_field_with_check_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:39\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0007_drop_field'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.PositiveIntegerField(null=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0009_drop_field_with_check_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:39\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0008_add_field_with_check_constraint'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0010_add_field_with_foreign_key.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:42\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0009_drop_field_with_check_constraint'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.ForeignKey(\n                null=True, on_delete=django.db.models.deletion.CASCADE, to='good_flow_app.RelatedTestTable'\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0011_drop_field_with_foreign_key.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:42\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0010_add_field_with_foreign_key'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0012_add_field_with_unique_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:43\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0011_drop_field_with_foreign_key'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(null=True, unique=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0013_drop_field_with_unique_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:43\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0012_add_field_with_unique_constraint'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0014_add_field_with_index.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:44\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0013_drop_field_with_unique_constraint'),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name='testtable',\n            name='field',\n            field=models.IntegerField(db_index=True, null=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0015_drop_field_with_index.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:44\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0014_add_field_with_index'),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name='testtable',\n            name='field',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0016_add_check_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:49\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0015_drop_field_with_index'),\n    ]\n\n    operations = [\n        migrations.AddConstraint(\n            model_name='testtable',\n            constraint=models.CheckConstraint(check=models.Q(test_field_int__gt=0), name='test_check_constraint'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0017_drop_check_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:49\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0016_add_check_constraint'),\n    ]\n\n    operations = [\n        migrations.RemoveConstraint(\n            model_name='testtable',\n            name='test_check_constraint',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0018_add_unique_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:51\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0017_drop_check_constraint'),\n    ]\n\n    operations = [\n        migrations.AddConstraint(\n            model_name='testtable',\n            constraint=models.UniqueConstraint(fields=('test_field_int',), name='test_uniq_constraint'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0019_drop_unique_constraint.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:51\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0018_add_unique_constraint'),\n    ]\n\n    operations = [\n        migrations.RemoveConstraint(\n            model_name='testtable',\n            name='test_uniq_constraint',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0020_add_unique_constraint_with_condition.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:52\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0019_drop_unique_constraint'),\n    ]\n\n    operations = [\n        migrations.AddConstraint(\n            model_name='testtable',\n            constraint=models.UniqueConstraint(\n                condition=models.Q(test_field_int__isnull=False),\n                fields=('test_field_int',),\n                name='test_uniq_constraint',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0021_drop_unique_constraint_with_condition.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:52\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0020_add_unique_constraint_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveConstraint(\n            model_name='testtable',\n            name='test_uniq_constraint',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0022_add_index.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:53\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0021_drop_unique_constraint_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=models.Index(fields=['test_field_int'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0023_drop_index.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:53\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0022_add_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0024_add_index_with_condition.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:53\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0023_drop_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=models.Index(\n                condition=models.Q(test_field_int__isnull=False),\n                fields=['test_field_int'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0025_drop_index_with_condition.py",
    "content": "# Generated by Django 3.1 on 2019-09-22 20:54\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0024_add_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0026_add_brin_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:38\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0025_drop_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.BrinIndex(fields=['test_field_int'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0027_drop_brin_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:38\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0026_add_brin_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0028_add_brin_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:39\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0027_drop_brin_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.BrinIndex(\n                condition=models.Q(test_field_int__isnull=False),\n                fields=['test_field_int'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0029_drop_brin_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:39\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0028_add_brin_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0030_add_btree_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:45\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0029_drop_brin_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.BTreeIndex(fields=['test_field_int'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0031_drop_btree_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:46\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0030_add_btree_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0032_add_btree_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:46\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0031_drop_btree_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.BTreeIndex(\n                condition=models.Q(test_field_int__isnull=False),\n                fields=['test_field_int'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0033_drop_btree_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:46\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0032_add_btree_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0034_add_gin_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:46\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0033_drop_btree_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.GinIndex(fields=['test_field_tsv'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0035_drop_gin_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:47\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0034_add_gin_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0036_add_gin_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:47\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0035_drop_gin_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.GinIndex(\n                condition=models.Q(test_field_tsv__isnull=False),\n                fields=['test_field_tsv'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0037_drop_gin_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:47\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0036_add_gin_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0038_add_gist_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:47\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0037_drop_gin_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.GistIndex(fields=['test_field_tsv'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0039_drop_gist_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:47\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0038_add_gist_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0040_add_gist_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:47\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0039_drop_gist_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.GistIndex(\n                condition=models.Q(test_field_tsv__isnull=False),\n                fields=['test_field_tsv'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0041_drop_gist_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:48\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0040_add_gist_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0042_add_hash_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:48\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0041_drop_gist_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.HashIndex(fields=['test_field_int'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0043_drop_hash_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:48\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0042_add_hash_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0044_add_hash_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:48\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0043_drop_hash_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.HashIndex(\n                condition=models.Q(test_field_int__isnull=False),\n                fields=['test_field_int'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0045_drop_hash_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:49\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0044_add_hash_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0046_add_spgist_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:49\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0045_drop_hash_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.SpGistIndex(fields=['test_field_str'], name='test_index'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0047_drop_spgist_index.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:49\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0046_add_spgist_index'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0048_add_spgist_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:49\n\nimport django.contrib.postgres.indexes\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0047_drop_spgist_index'),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name='testtable',\n            index=django.contrib.postgres.indexes.SpGistIndex(\n                condition=models.Q(test_field_str__isnull=False),\n                fields=['test_field_str'],\n                name='test_index',\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0049_drop_spgist_index_with_condition.py",
    "content": "# Generated by Django 3.0a1 on 2019-10-14 19:49\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0048_add_spgist_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.RemoveIndex(\n            model_name='testtable',\n            name='test_index',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0050_add_unique_constraint_deferrable.py",
    "content": "# Generated by Django 4.2.20 on 2025-03-16 16:58\n\nimport django.db.models.constraints\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0049_drop_spgist_index_with_condition'),\n    ]\n\n    operations = [\n        migrations.AddConstraint(\n            model_name='testtable',\n            constraint=models.UniqueConstraint(\n                deferrable=django.db.models.constraints.Deferrable['DEFERRED'],\n                fields=('test_field_int',),\n                name='test_uniq_constraint_deferred'\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name='testtable',\n            constraint=models.UniqueConstraint(\n                deferrable=django.db.models.constraints.Deferrable['IMMEDIATE'],\n                fields=('test_field_str',),\n                name='test_uniq_constraint_immediate'\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/0051_drop_unique_constraint_deferrable.py",
    "content": "# Generated by Django 4.2.20 on 2025-03-16 16:58\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('good_flow_app', '0050_add_unique_constraint_deferrable'),\n    ]\n\n    operations = [\n        migrations.RemoveConstraint(\n            model_name='testtable',\n            name='test_uniq_constraint_deferred',\n        ),\n        migrations.RemoveConstraint(\n            model_name='testtable',\n            name='test_uniq_constraint_immediate',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_app/models.py",
    "content": "from django.contrib.postgres.search import SearchVectorField\nfrom django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n    test_field_tsv = SearchVectorField()\n\n\nclass RelatedTestTable(models.Model):\n    pass\n"
  },
  {
    "path": "tests/apps/good_flow_app_concurrently/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_app_concurrently/migrations/0001_initial.py",
    "content": "# Generated by Django 3.1 on 2019-09-21 20:09\n\nimport django.contrib.postgres.search\nfrom django.db import migrations, models\n\n\ndef insert_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('good_flow_app_concurrently', 'TestTable')\n    TestTable.objects.using(db_alias).create(test_field_int=1)\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='RelatedTestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n            ],\n        ),\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('test_field_int', models.IntegerField()),\n                ('test_field_str', models.CharField(max_length=10)),\n                ('test_field_tsv', django.contrib.postgres.search.SearchVectorField()),\n            ],\n        ),\n        migrations.RunPython(insert_objects, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app_concurrently/migrations/0002_auto_20191210_2147.py",
    "content": "# Generated by Django 3.0a1 on 2019-12-10 21:47\nfrom django.contrib.postgres.operations import AddIndexConcurrently\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n    dependencies = [\n        ('good_flow_app_concurrently', '0001_initial'),\n    ]\n\n    operations = [\n        AddIndexConcurrently(\n            model_name='testtable',\n            index=models.Index(fields=['test_field_int'], name='good_flow_a_test_fi_0b7e6f_idx'),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app_concurrently/migrations/0003_auto_20191210_2148.py",
    "content": "# Generated by Django 3.0a1 on 2019-12-10 21:48\nfrom django.contrib.postgres.operations import RemoveIndexConcurrently\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n    dependencies = [\n        ('good_flow_app_concurrently', '0002_auto_20191210_2147'),\n    ]\n\n    operations = [\n        RemoveIndexConcurrently(\n            model_name='testtable',\n            name='good_flow_a_test_fi_0b7e6f_idx',\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_app_concurrently/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_app_concurrently/models.py",
    "content": "from django.contrib.postgres.search import SearchVectorField\nfrom django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n    test_field_tsv = SearchVectorField()\n\n\nclass RelatedTestTable(models.Model):\n    pass\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0001_initial.py",
    "content": "import django.db.models.deletion\nimport django.db.models.functions.math\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"TestTableMain\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"main_id\", models.IntegerField(null=True, unique=True)),\n                (\"field_u1\", models.IntegerField(null=True)),\n                (\"field_u2\", models.IntegerField(null=True)),\n                (\"field_u3\", models.IntegerField(null=True)),\n                (\"field_u4\", models.IntegerField(null=True)),\n                (\"field_u5\", models.IntegerField(null=True)),\n                (\"field_u6\", models.IntegerField(null=True)),\n                (\"field_u7\", models.IntegerField(null=True)),\n                (\"field_i1\", models.IntegerField(null=True)),\n                (\"field_i2\", models.IntegerField(null=True)),\n                (\"field_i3\", models.IntegerField(null=True)),\n                (\"field_i4\", models.IntegerField(null=True)),\n                (\"field_i5\", models.IntegerField(null=True)),\n                (\"field_i6\", models.IntegerField(null=True)),\n                (\"field_i7\", models.IntegerField(null=True)),\n            ],\n            options={\n                \"db_table\": \"drop_col_test_table_main\",\n            },\n        ),\n        migrations.CreateModel(\n            name=\"TestTableParent\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n            ],\n            options={\n                \"db_table\": \"drop_col_test_table_parent\",\n            },\n        ),\n        migrations.CreateModel(\n            name=\"TestTableChild\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\n                    \"main\",\n                    models.ForeignKey(\n                        null=True,\n                        on_delete=django.db.models.deletion.CASCADE,\n                        to=\"good_flow_drop_column_with_constraints.testtablemain\",\n                        to_field=\"main_id\",\n                    ),\n                ),\n            ],\n            options={\n                \"db_table\": \"drop_col_test_table_child\",\n            },\n        ),\n        migrations.AddField(\n            model_name=\"testtablemain\",\n            name=\"parent\",\n            field=models.OneToOneField(\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"good_flow_drop_column_with_constraints.testtableparent\",\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                models.F(\"parent\"), models.F(\"field_i1\"), name=\"drop_col_i1\"\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(fields=[\"parent\", \"field_i2\"], name=\"drop_col_i2\"),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                django.db.models.functions.math.Abs(\"field_i3\"), name=\"drop_col_i3\"\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                models.F(\"parent\"),\n                condition=models.Q((\"field_i4__gt\", 0)),\n                name=\"drop_col_i4\",\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                condition=models.Q((\"field_i5__gt\", 0)),\n                fields=[\"parent\"],\n                name=\"drop_col_i5\",\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                models.F(\"parent\"), include=(\"field_i6\",), name=\"drop_col_i6\"\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                fields=[\"parent\"], include=(\"field_i7\",), name=\"drop_col_i7\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                models.F(\"parent\"), models.F(\"field_u1\"), name=\"drop_col_u1\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                fields=(\"parent\", \"field_u2\"), name=\"drop_col_u2\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                django.db.models.functions.math.Abs(\"field_u3\"), name=\"drop_col_u3\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                models.F(\"parent\"),\n                condition=models.Q((\"field_u4__gt\", 0)),\n                name=\"drop_col_u4\",\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                condition=models.Q((\"field_u5__gt\", 0)),\n                fields=(\"parent\",),\n                name=\"drop_col_u5\",\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                models.F(\"parent\"), include=(\"field_u6\",), name=\"drop_col_u6\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                fields=(\"parent\",), include=(\"field_u7\",), name=\"drop_col_u7\"\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0002_remove_testtablemain_drop_col_u1_and_more.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        (\"good_flow_drop_column_with_constraints\", \"0001_initial\"),\n    ]\n\n    operations = [\n        # emulate worst case untracked constraints\n        migrations.SeparateDatabaseAndState(\n            database_operations=[\n                # as constraint dropped with cascade or explicitly before cascade\n                # we need to back untracked constraint creation to make migration revert happy\n                migrations.RunSQL(\n                    migrations.RunSQL.noop,\n                    \"\"\"\n                        ALTER TABLE \"drop_col_test_table_main\"\n                        ADD CONSTRAINT \"drop_col_u2\" UNIQUE (\"parent_id\", \"field_u2\");\n                    \"\"\"\n                )\n            ],\n            state_operations=[\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u1\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u2\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u3\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u4\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u5\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u6\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u7\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i1\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i2\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i3\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i4\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i5\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i6\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i7\",\n                ),\n                migrations.RemoveField(\n                    model_name=\"testtablechild\",\n                    name=\"main\",\n                ),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0003_remove_testtablemain_field_i7.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0002_remove_testtablemain_drop_col_u1_and_more\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i7\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0004_remove_testtablemain_field_i6.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0003_remove_testtablemain_field_i7\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i6\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0005_remove_testtablemain_field_i5.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0004_remove_testtablemain_field_i6\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i5\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0006_remove_testtablemain_field_i4.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0005_remove_testtablemain_field_i5\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i4\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0007_remove_testtablemain_field_i3.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0006_remove_testtablemain_field_i4\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i3\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0008_remove_testtablemain_field_i2.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0007_remove_testtablemain_field_i3\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i2\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0009_remove_testtablemain_field_i1.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0008_remove_testtablemain_field_i2\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i1\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0010_remove_testtablemain_field_u7.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0009_remove_testtablemain_field_i1\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u7\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0011_remove_testtablemain_field_u6.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0010_remove_testtablemain_field_u7\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u6\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0012_remove_testtablemain_field_u5.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0011_remove_testtablemain_field_u6\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u5\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0013_remove_testtablemain_field_u4.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0012_remove_testtablemain_field_u5\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u4\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0014_remove_testtablemain_field_u3.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0013_remove_testtablemain_field_u4\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u3\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0015_remove_testtablemain_field_u2.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0014_remove_testtablemain_field_u3\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u2\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0016_remove_testtablemain_field_u1.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0015_remove_testtablemain_field_u2\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u1\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0017_remove_testtablemain_main_id.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints\",\n            \"0016_remove_testtablemain_field_u1\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"main_id\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/0018_remove_testtablemain_parent.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"good_flow_drop_column_with_constraints\", \"0017_remove_testtablemain_main_id\"),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"parent\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints/models.py",
    "content": "from django.db import models\n\n# from django.db.models.functions import Abs\n\n\nclass TestTableParent(models.Model):\n\n    class Meta:\n        db_table = 'drop_col_test_table_parent'\n\n\nclass TestTableMain(models.Model):\n    # parent = models.OneToOneField(TestTableParent, null=True, on_delete=models.CASCADE)\n    # main_id = models.IntegerField(null=True, unique=True)\n    # field_u1 = models.IntegerField(null=True)\n    # field_u2 = models.IntegerField(null=True)\n    # field_u3 = models.IntegerField(null=True)\n    # field_u4 = models.IntegerField(null=True)\n    # field_u5 = models.IntegerField(null=True)\n    # field_u6 = models.IntegerField(null=True)\n    # field_u7 = models.IntegerField(null=True)\n    # field_i1 = models.IntegerField(null=True)\n    # field_i2 = models.IntegerField(null=True)\n    # field_i3 = models.IntegerField(null=True)\n    # field_i4 = models.IntegerField(null=True)\n    # field_i5 = models.IntegerField(null=True)\n    # field_i6 = models.IntegerField(null=True)\n    # field_i7 = models.IntegerField(null=True)\n\n    class Meta:\n        db_table = 'drop_col_test_table_main'\n        # constraints = [\n        #     models.UniqueConstraint(\"parent\", \"field_u1\", name=\"drop_col_u1\"),\n        #     models.UniqueConstraint(fields=[\"parent\", \"field_u2\"], name=\"drop_col_u2\"),\n        #     models.UniqueConstraint(Abs(\"field_u3\"), name=\"drop_col_u3\"),\n        #     models.UniqueConstraint(\"parent\", name=\"drop_col_u4\", condition=models.Q(field_u4__gt=0)),\n        #     models.UniqueConstraint(fields=[\"parent\"], name=\"drop_col_u5\", condition=models.Q(field_u5__gt=0)),\n        #     models.UniqueConstraint(\"parent\", name=\"drop_col_u6\", include=[\"field_u6\"]),\n        #     models.UniqueConstraint(fields=[\"parent\"], name=\"drop_col_u7\", include=[\"field_u7\"]),\n        # ]\n        # indexes = [\n        #     models.Index(\"parent\", \"field_i1\", name=\"drop_col_i1\"),\n        #     models.Index(fields=[\"parent\", \"field_i2\"], name=\"drop_col_i2\"),\n        #     models.Index(Abs(\"field_i3\"), name=\"drop_col_i3\"),\n        #     models.Index(\"parent\", name=\"drop_col_i4\", condition=models.Q(field_i4__gt=0)),\n        #     models.Index(fields=[\"parent\"], name=\"drop_col_i5\", condition=models.Q(field_i5__gt=0)),\n        #     models.Index(\"parent\", name=\"drop_col_i6\", include=[\"field_i6\"]),\n        #     models.Index(fields=[\"parent\"], name=\"drop_col_i7\", include=[\"field_i7\"]),\n        # ]\n\n\nclass TestTableChild(models.Model):\n    # main = models.ForeignKey(TestTableMain, to_field=\"main_id\", null=True, on_delete=models.CASCADE)\n\n    class Meta:\n        db_table = 'drop_col_test_table_child'\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0001_initial.py",
    "content": "import django.db.models.deletion\nimport django.db.models.functions.math\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"TestTableMain\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"main_id\", models.IntegerField(null=True, unique=True)),\n                (\"field_u2\", models.IntegerField(null=True)),\n                (\"field_u5\", models.IntegerField(null=True)),\n                (\"field_u7\", models.IntegerField(null=True)),\n                (\"field_i1\", models.IntegerField(null=True)),\n                (\"field_i2\", models.IntegerField(null=True)),\n                (\"field_i3\", models.IntegerField(null=True)),\n                (\"field_i4\", models.IntegerField(null=True)),\n                (\"field_i5\", models.IntegerField(null=True)),\n                (\"field_i6\", models.IntegerField(null=True)),\n                (\"field_i7\", models.IntegerField(null=True)),\n            ],\n            options={\n                \"db_table\": \"drop_col_test_table_main\",\n            },\n        ),\n        migrations.CreateModel(\n            name=\"TestTableParent\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n            ],\n            options={\n                \"db_table\": \"drop_col_test_table_parent\",\n            },\n        ),\n        migrations.CreateModel(\n            name=\"TestTableChild\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\n                    \"main\",\n                    models.ForeignKey(\n                        null=True,\n                        on_delete=django.db.models.deletion.CASCADE,\n                        to=\"good_flow_drop_column_with_constraints_old.testtablemain\",\n                        to_field=\"main_id\",\n                    ),\n                ),\n            ],\n            options={\n                \"db_table\": \"drop_col_test_table_child\",\n            },\n        ),\n        migrations.AddField(\n            model_name=\"testtablemain\",\n            name=\"parent\",\n            field=models.OneToOneField(\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"good_flow_drop_column_with_constraints_old.testtableparent\",\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                models.F(\"parent\"), models.F(\"field_i1\"), name=\"drop_col_i1\"\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(fields=[\"parent\", \"field_i2\"], name=\"drop_col_i2\"),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                django.db.models.functions.math.Abs(\"field_i3\"), name=\"drop_col_i3\"\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                models.F(\"parent\"),\n                condition=models.Q((\"field_i4__gt\", 0)),\n                name=\"drop_col_i4\",\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                condition=models.Q((\"field_i5__gt\", 0)),\n                fields=[\"parent\"],\n                name=\"drop_col_i5\",\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                models.F(\"parent\"), include=(\"field_i6\",), name=\"drop_col_i6\"\n            ),\n        ),\n        migrations.AddIndex(\n            model_name=\"testtablemain\",\n            index=models.Index(\n                fields=[\"parent\"], include=(\"field_i7\",), name=\"drop_col_i7\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                fields=(\"parent\", \"field_u2\"), name=\"drop_col_u2\"\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                condition=models.Q((\"field_u5__gt\", 0)),\n                fields=(\"parent\",),\n                name=\"drop_col_u5\",\n            ),\n        ),\n        migrations.AddConstraint(\n            model_name=\"testtablemain\",\n            constraint=models.UniqueConstraint(\n                fields=(\"parent\",), include=(\"field_u7\",), name=\"drop_col_u7\"\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0002_remove_testtablemain_drop_col_u2_and_more.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        (\"good_flow_drop_column_with_constraints_old\", \"0001_initial\"),\n    ]\n\n    operations = [\n        # emulate worst case untracked constraints\n        migrations.SeparateDatabaseAndState(\n            database_operations=[\n                # as constraint dropped with cascade or explicitly before cascade\n                # we need to back untracked constraint creation to make migration revert happy\n                migrations.RunSQL(\n                    migrations.RunSQL.noop,\n                    \"\"\"\n                        ALTER TABLE \"drop_col_test_table_main\"\n                        ADD CONSTRAINT \"drop_col_u2\" UNIQUE (\"parent_id\", \"field_u2\");\n                    \"\"\"\n                )\n            ],\n            state_operations=[\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u2\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u5\",\n                ),\n                migrations.RemoveConstraint(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_u7\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i1\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i2\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i3\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i4\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i5\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i6\",\n                ),\n                migrations.RemoveIndex(\n                    model_name=\"testtablemain\",\n                    name=\"drop_col_i7\",\n                ),\n                migrations.RemoveField(\n                    model_name=\"testtablechild\",\n                    name=\"main\",\n                ),\n            ],\n        )\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0003_remove_testtablemain_field_i7.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0002_remove_testtablemain_drop_col_u2_and_more\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i7\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0004_remove_testtablemain_field_i6.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0003_remove_testtablemain_field_i7\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i6\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0005_remove_testtablemain_field_i5.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0004_remove_testtablemain_field_i6\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i5\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0006_remove_testtablemain_field_i4.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0005_remove_testtablemain_field_i5\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i4\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0007_remove_testtablemain_field_i3.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0006_remove_testtablemain_field_i4\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i3\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0008_remove_testtablemain_field_i2.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0007_remove_testtablemain_field_i3\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i2\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0009_remove_testtablemain_field_i1.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0008_remove_testtablemain_field_i2\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_i1\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0010_remove_testtablemain_field_u7.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0009_remove_testtablemain_field_i1\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u7\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0011_remove_testtablemain_field_u5.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0010_remove_testtablemain_field_u7\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u5\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0012_remove_testtablemain_field_u2.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0011_remove_testtablemain_field_u5\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"field_u2\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0013_remove_testtablemain_main_id.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0012_remove_testtablemain_field_u2\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"main_id\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/0014_remove_testtablemain_parent.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\n            \"good_flow_drop_column_with_constraints_old\",\n            \"0013_remove_testtablemain_main_id\",\n        ),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"testtablemain\",\n            name=\"parent\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_drop_column_with_constraints_old/models.py",
    "content": "from django.db import models\n\n# from django.db.models.functions import Abs\n\n\nclass TestTableParent(models.Model):\n\n    class Meta:\n        db_table = 'drop_col_test_table_parent'\n\n\nclass TestTableMain(models.Model):\n    # parent = models.OneToOneField(TestTableParent, null=True, on_delete=models.CASCADE)\n    # main_id = models.IntegerField(null=True, unique=True)\n    # field_u2 = models.IntegerField(null=True)\n    # field_u5 = models.IntegerField(null=True)\n    # field_u7 = models.IntegerField(null=True)\n    # field_i1 = models.IntegerField(null=True)\n    # field_i2 = models.IntegerField(null=True)\n    # field_i3 = models.IntegerField(null=True)\n    # field_i4 = models.IntegerField(null=True)\n    # field_i5 = models.IntegerField(null=True)\n    # field_i6 = models.IntegerField(null=True)\n    # field_i7 = models.IntegerField(null=True)\n\n    class Meta:\n        db_table = 'drop_col_test_table_main'\n        # constraints = [\n        #     models.UniqueConstraint(fields=[\"parent\", \"field_u2\"], name=\"drop_col_u2\"),\n        #     models.UniqueConstraint(fields=[\"parent\"], name=\"drop_col_u5\", condition=models.Q(field_u5__gt=0)),\n        #     models.UniqueConstraint(fields=[\"parent\"], name=\"drop_col_u7\", include=[\"field_u7\"]),\n        # ]\n        # indexes = [\n        #     models.Index(\"parent\", \"field_i1\", name=\"drop_col_i1\"),\n        #     models.Index(fields=[\"parent\", \"field_i2\"], name=\"drop_col_i2\"),\n        #     models.Index(Abs(\"field_i3\"), name=\"drop_col_i3\"),\n        #     models.Index(\"parent\", name=\"drop_col_i4\", condition=models.Q(field_i4__gt=0)),\n        #     models.Index(fields=[\"parent\"], name=\"drop_col_i5\", condition=models.Q(field_i5__gt=0)),\n        #     models.Index(\"parent\", name=\"drop_col_i6\", include=[\"field_i6\"]),\n        #     models.Index(fields=[\"parent\"], name=\"drop_col_i7\", include=[\"field_i7\"]),\n        # ]\n\n\nclass TestTableChild(models.Model):\n    # main = models.ForeignKey(TestTableMain, to_field=\"main_id\", null=True, on_delete=models.CASCADE)\n\n    class Meta:\n        db_table = 'drop_col_test_table_child'\n"
  },
  {
    "path": "tests/apps/good_flow_drop_table_with_constraints/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_drop_table_with_constraints/migrations/0001_initial.py",
    "content": "import django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"TestTableMain\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"main_id\", models.IntegerField(null=True, unique=True)),\n            ],\n            options={\n                \"db_table\": \"drop_tbl_test_table_main\",\n            },\n        ),\n        migrations.CreateModel(\n            name=\"TestTableParent\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n            ],\n            options={\n                \"db_table\": \"drop_tbl_test_table_parent\",\n            },\n        ),\n        migrations.CreateModel(\n            name=\"TestTableChild\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\n                    \"main\",\n                    models.ForeignKey(\n                        null=True,\n                        on_delete=django.db.models.deletion.CASCADE,\n                        to=\"good_flow_drop_table_with_constraints.testtablemain\",\n                        to_field=\"main_id\",\n                    ),\n                ),\n            ],\n            options={\n                \"db_table\": \"drop_tbl_test_table_child\",\n            },\n        ),\n        migrations.AddField(\n            model_name=\"testtablemain\",\n            name=\"parent\",\n            field=models.OneToOneField(\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"good_flow_drop_table_with_constraints.testtableparent\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_table_with_constraints/migrations/0002_remove_testtablechild_main.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        (\"good_flow_drop_table_with_constraints\", \"0001_initial\"),\n    ]\n\n    operations = [\n        # emulate worst case untracked constraints\n        migrations.SeparateDatabaseAndState(\n            database_operations=[],\n            state_operations=[\n                migrations.RemoveField(\n                    model_name=\"testtablechild\",\n                    name=\"main\",\n                ),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_table_with_constraints/migrations/0003_delete_testtablemain.py",
    "content": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"good_flow_drop_table_with_constraints\", \"0002_remove_testtablechild_main\"),\n    ]\n\n    operations = [\n        migrations.DeleteModel(\n            name=\"TestTableMain\",\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/good_flow_drop_table_with_constraints/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/good_flow_drop_table_with_constraints/models.py",
    "content": "from django.db import models\n\n\nclass TestTableParent(models.Model):\n\n    class Meta:\n        db_table = 'drop_tbl_test_table_parent'\n\n\n# class TestTableMain(models.Model):\n#     parent = models.OneToOneField(TestTableParent, null=True, on_delete=models.CASCADE)\n#     main_id = models.IntegerField(null=True, unique=True)\n#\n#     class Meta:\n#         db_table = 'drop_tbl_test_table_main'\n\n\nclass TestTableChild(models.Model):\n    # main = models.ForeignKey(TestTableMain, to_field=\"main_id\", null=True, on_delete=models.CASCADE)\n\n    class Meta:\n        db_table = 'drop_tbl_test_table_child'\n"
  },
  {
    "path": "tests/apps/idempotency_add_auto_field_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_auto_field_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\"test_field_int\", models.IntegerField(primary_key=True, serialize=False)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\"id\", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_auto_field_app/migrations/0002_alter_relatedtesttable_test_field_int.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_auto_field_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.AutoField(primary_key=True, serialize=False),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_auto_field_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_auto_field_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.AutoField(primary_key=True)\n"
  },
  {
    "path": "tests/apps/idempotency_add_check_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_check_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_check_app/migrations/0002_relatedtesttable_idempotency_add_check_app_relatedtesttable_check.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_check_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AddConstraint(\n            model_name=\"relatedtesttable\",\n            constraint=models.CheckConstraint(\n                check=models.Q((\"test_field_int__gt\", 0)),\n                name=\"idempotency_add_check_app_relatedtesttable_check\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_check_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_check_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(null=True)\n\n    class Meta:\n        constraints = [\n            models.CheckConstraint(\n                check=models.Q(test_field_int__gt=0),\n                name=\"idempotency_add_check_app_relatedtesttable_check\",\n            )\n        ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_column_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_app/migrations/0002_relatedtesttable_test_field_str.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_column_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_str\",\n            field=models.CharField(max_length=10, null=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_column_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(null=True)\n    test_field_str = models.CharField(max_length=10, null=True)\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_foreign_key_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_column_foreign_key_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_foreign_key_app/migrations/0002_relatedtesttable_test_model.py",
    "content": "import django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_column_foreign_key_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"relatedtesttable\",\n            name=\"test_model\",\n            field=models.ForeignKey(\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"idempotency_add_column_foreign_key_app.testtable\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_foreign_key_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_column_foreign_key_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(null=True)\n    test_model = models.ForeignKey(TestTable, null=True, on_delete=models.CASCADE)\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_one_to_one_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_column_one_to_one_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_one_to_one_app/migrations/0002_relatedtesttable_test_model.py",
    "content": "import django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_column_one_to_one_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AddField(\n            model_name=\"relatedtesttable\",\n            name=\"test_model\",\n            field=models.OneToOneField(\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"idempotency_add_column_one_to_one_app.testtable\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_column_one_to_one_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_column_one_to_one_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(null=True)\n    test_model = models.OneToOneField(TestTable, null=True, on_delete=models.CASCADE)\n"
  },
  {
    "path": "tests/apps/idempotency_add_foreign_key_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_foreign_key_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_foreign_key_app/migrations/0002_alter_relatedtesttable_test_field_int.py",
    "content": "import django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_foreign_key_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.ForeignKey(\n                db_column=\"test_field_int\",\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"idempotency_add_foreign_key_app.testtable\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_foreign_key_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_foreign_key_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.ForeignKey(\n        TestTable,\n        null=True,\n        on_delete=models.CASCADE,\n        db_column=\"test_field_int\",\n    )\n"
  },
  {
    "path": "tests/apps/idempotency_add_index_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_index_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_index_app/migrations/0002_alter_relatedtesttable_test_field_int.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_index_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.IntegerField(db_index=True, null=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_index_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_index_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(null=True, db_index=True)\n"
  },
  {
    "path": "tests/apps/idempotency_add_index_meta_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_index_meta_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_index_meta_app/migrations/0002_relatedtesttable_relatedtesttable_idx.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_index_meta_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AddIndex(\n            model_name=\"relatedtesttable\",\n            index=models.Index(\n                fields=[\"test_field_int\", \"test_field_str\"], name=\"relatedtesttable_idx\"\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_index_meta_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_index_meta_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n    class Meta:\n        indexes = [\n            models.Index(\n                name=\"relatedtesttable_idx\",\n                fields=[\"test_field_int\", \"test_field_str\"],\n            )\n        ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_one_to_one_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_one_to_one_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_one_to_one_app/migrations/0002_alter_relatedtesttable_test_field_int.py",
    "content": "import django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_one_to_one_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.OneToOneField(\n                db_column=\"test_field_int\",\n                null=True,\n                on_delete=django.db.models.deletion.CASCADE,\n                to=\"idempotency_add_one_to_one_app.testtable\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_one_to_one_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_one_to_one_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.OneToOneField(\n        TestTable,\n        null=True,\n        on_delete=models.CASCADE,\n        db_column=\"test_field_int\",\n    )\n"
  },
  {
    "path": "tests/apps/idempotency_add_primary_key_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_primary_key_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_primary_key_app/migrations/0002_remove_relatedtesttable_id_and_more.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_primary_key_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.RemoveField(\n            model_name=\"relatedtesttable\",\n            name=\"id\",\n        ),\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.IntegerField(primary_key=True, serialize=False),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_primary_key_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_primary_key_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(primary_key=True)\n"
  },
  {
    "path": "tests/apps/idempotency_add_unique_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_unique_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_unique_app/migrations/0002_alter_relatedtesttable_test_field_int.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_unique_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.IntegerField(null=True, unique=True),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_unique_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_unique_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField(null=True, unique=True)\n"
  },
  {
    "path": "tests/apps/idempotency_add_unique_meta_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_unique_meta_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_unique_meta_app/migrations/0002_relatedtesttable_relatedtesttable_uniq.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_add_unique_meta_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AddConstraint(\n            model_name=\"relatedtesttable\",\n            constraint=models.UniqueConstraint(\n                fields=(\"test_field_int\", \"test_field_str\"),\n                name=\"relatedtesttable_uniq\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_add_unique_meta_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_add_unique_meta_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n    class Meta:\n        constraints = [\n            models.UniqueConstraint(\n                name=\"relatedtesttable_uniq\",\n                fields=[\"test_field_int\", \"test_field_str\"],\n            )\n        ]\n"
  },
  {
    "path": "tests/apps/idempotency_create_table_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_create_table_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\ndef insert_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model(\"idempotency_create_table_app\", \"TestTable\")\n    TestTable.objects.using(db_alias).create(test_field_int=1)\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\"id\", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name=\"ID\")),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n        migrations.RunPython(insert_objects, migrations.RunPython.noop),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_create_table_app/migrations/0002_relatedtesttable_and_more.py",
    "content": "# Generated by Django 5.0 on 2024-04-22 15:54\n\nimport django.db.models.deletion\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_create_table_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n                (\n                    \"test_model\",\n                    models.ForeignKey(\n                        null=True,\n                        on_delete=django.db.models.deletion.CASCADE,\n                        to=\"idempotency_create_table_app.testtable\",\n                    ),\n                ),\n            ],\n        ),\n        migrations.AddConstraint(\n            model_name=\"relatedtesttable\",\n            constraint=models.UniqueConstraint(\n                fields=(\"test_model\", \"test_field_int\"),\n                name=\"idempotency_create_table_app_relatedtesttable_uniq\",\n            ),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_create_table_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_create_table_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_model = models.ForeignKey(TestTable, null=True, on_delete=models.CASCADE)\n    test_field_int = models.IntegerField(null=True)\n\n    class Meta:\n        constraints = [\n            models.UniqueConstraint(\n                name=\"idempotency_create_table_app_relatedtesttable_uniq\",\n                fields=[\"test_model\", \"test_field_int\"],\n            )\n        ]\n"
  },
  {
    "path": "tests/apps/idempotency_set_not_null_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_set_not_null_app/migrations/0001_initial.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = []\n\n    operations = [\n        migrations.CreateModel(\n            name=\"RelatedTestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField(null=True)),\n            ],\n        ),\n        migrations.CreateModel(\n            name=\"TestTable\",\n            fields=[\n                (\n                    \"id\",\n                    models.AutoField(\n                        auto_created=True,\n                        primary_key=True,\n                        serialize=False,\n                        verbose_name=\"ID\",\n                    ),\n                ),\n                (\"test_field_int\", models.IntegerField()),\n                (\"test_field_str\", models.CharField(max_length=10)),\n            ],\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_set_not_null_app/migrations/0002_alter_relatedtesttable_test_field_int.py",
    "content": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    atomic = False\n\n    dependencies = [\n        (\"idempotency_set_not_null_app\", \"0001_initial\"),\n    ]\n\n    operations = [\n        migrations.AlterField(\n            model_name=\"relatedtesttable\",\n            name=\"test_field_int\",\n            field=models.IntegerField(),\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/idempotency_set_not_null_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/idempotency_set_not_null_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    test_field_int = models.IntegerField()\n    test_field_str = models.CharField(max_length=10)\n\n\nclass RelatedTestTable(models.Model):\n    test_field_int = models.IntegerField()\n"
  },
  {
    "path": "tests/apps/old_notnull_check_constraint_migration_app/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/old_notnull_check_constraint_migration_app/migrations/0001_initial.py",
    "content": "# Generated by Django 2.2.5 on 2019-09-23 23:28\n\nfrom django.db import migrations, models\n\n\ndef insert_objects(apps, schema_editor):\n    db_alias = schema_editor.connection.alias\n    TestTable = apps.get_model('old_notnull_check_constraint_migration_app', 'TestTable')\n    TestTable.objects.using(db_alias).create(field=1)\n\n\nclass Migration(migrations.Migration):\n\n    initial = True\n\n    dependencies = [\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='TestTable',\n            fields=[\n                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n                ('field', models.IntegerField()),\n            ],\n        ),\n        migrations.RunPython(insert_objects, migrations.RunPython.noop),\n        migrations.RunSQL(\n            'ALTER TABLE old_notnull_check_constraint_migration_app_testtable '\n            'ADD CONSTRAINT old_constraint_notnull CHECK (field IS NOT NULL)',\n            migrations.RunSQL.noop,\n        ),\n        migrations.RunSQL(\n            'ALTER TABLE old_notnull_check_constraint_migration_app_testtable '\n            'ALTER COLUMN field DROP NOT NULL',\n            migrations.RunSQL.noop,\n        ),\n    ]\n"
  },
  {
    "path": "tests/apps/old_notnull_check_constraint_migration_app/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "tests/apps/old_notnull_check_constraint_migration_app/models.py",
    "content": "from django.db import models\n\n\nclass TestTable(models.Model):\n    field = models.IntegerField()\n"
  },
  {
    "path": "tests/integration/__init__.py",
    "content": "import contextlib\nimport os\nimport subprocess\nfrom typing import List\n\nfrom django.conf import settings\nfrom django.db import connection\nfrom django.db.backends.postgresql.base import Database\n\n\ndef one_line_sql(sql: str) -> str:\n    return sql.replace(\"    \", \"\").replace(\"\\n\", \" \").replace(\"( \", \"(\").replace(\" )\", \")\").replace(\"  \", \" \").strip()\n\n\ndef split_sql_queries(sql: str) -> List[str]:\n    return [line for line in sql.splitlines() if line and not line.startswith(\"--\")]\n\n\ndef pg_dump(table: str) -> str:\n    host = settings.DATABASES[\"default\"][\"HOST\"]\n    port = settings.DATABASES[\"default\"][\"PORT\"]\n    name = settings.DATABASES[\"default\"][\"NAME\"]\n    user = settings.DB_SUPER_USER\n    password = settings.DB_SUPER_PASSWORD\n    env = os.environ.copy() | {\"PGPASSWORD\": password}\n    cmd = f\"pg_dump -h {host} -p {port} -U {user} -d {name} -s -t {table} --restrict-key=test\"\n    popen = subprocess.run(cmd, env=env, text=True, shell=True, capture_output=True, check=True)\n    return popen.stdout\n\n\n@contextlib.contextmanager\ndef super_user_cursor():\n    host = settings.DATABASES[\"default\"][\"HOST\"]\n    port = settings.DATABASES[\"default\"][\"PORT\"]\n    name = settings.DATABASES[\"default\"][\"NAME\"]\n    user = settings.DB_SUPER_USER\n    password = settings.DB_SUPER_PASSWORD\n    conn = Database.connect(f\"host={host} port={port} dbname={name} user={user} password={password}\")\n    try:\n        conn.autocommit = True\n        cursor = conn.cursor()\n        try:\n            yield cursor\n        finally:\n            cursor.close()\n    finally:\n        conn.close()\n\n\ndef make_index_invalid(table: str, index: str):\n    with super_user_cursor() as cursor:\n        cursor.execute(\"\"\"\n            UPDATE pg_index\n            SET indisvalid = false\n            WHERE indrelid = %s::regclass::oid\n            AND indexrelid = %s::regclass::oid\n        \"\"\", [table, index])\n    assert not is_valid_index(table, index)\n\n\ndef is_valid_index(table: str, index: str) -> bool:\n    with connection.cursor() as cursor:\n        cursor.execute(\"\"\"\n            SELECT indisvalid\n            FROM pg_index\n            WHERE indrelid = %s::regclass::oid\n            AND indexrelid = %s::regclass::oid\n        \"\"\", [table, index])\n        data = cursor.fetchone()\n        if data is None:\n            raise ValueError(f\"index {index} not found for {table}\")\n        return data[0]\n\n\ndef is_valid_constraint(table: str, constraint: str) -> bool:\n    with connection.cursor() as cursor:\n        cursor.execute(\"\"\"\n            SELECT convalidated\n            FROM pg_constraint\n            WHERE conrelid = %s::regclass::oid\n            AND conname = %s\n        \"\"\", [table, constraint])\n        data = cursor.fetchone()\n        if data is None:\n            raise ValueError(f\"constraint {constraint} not found for {table}\")\n        return data[0]\n"
  },
  {
    "path": "tests/integration/test_migrations.py",
    "content": "import os\nimport textwrap\n\nimport django\nfrom django.apps import apps\nfrom django.core.management import call_command\nfrom django.db import connection\nfrom django.test import modify_settings, override_settings\n\nimport pytest\n\nfrom django_zero_downtime_migrations.backends.postgres.schema import (\n    UnsafeOperationException\n)\nfrom tests import skip_for_default_django_backend\nfrom tests.integration import (\n    is_valid_constraint, is_valid_index, make_index_invalid, one_line_sql,\n    pg_dump, split_sql_queries\n)\n\n\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_sqlmigrate_with_idempotent_mode():\n    app_config = apps.get_app_config(\"good_flow_app\")\n    for migration in os.listdir(os.path.join(app_config.path, \"migrations\")):\n        if not migration.startswith(\"_\") and migration.endswith(\".py\"):\n            call_command(\"sqlmigrate\", \"good_flow_app\", migration[:4])\n\n\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_alter_table_with_same_db_table\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_good_flow_alter_table_with_same_db_table():\n    # forward\n    call_command(\"migrate\", \"good_flow_alter_table_with_same_db_table\")\n\n    # backward\n    call_command(\"migrate\", \"good_flow_alter_table_with_same_db_table\", \"zero\")\n\n\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_good_flow():\n    # forward\n    call_command(\"migrate\", \"good_flow_app\")\n\n    # backward\n    call_command(\"migrate\", \"good_flow_app\", \"zero\")\n\n\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_app_concurrently\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_good_flow_create_and_drop_index_concurrently():\n    # forward\n    call_command(\"migrate\", \"good_flow_app_concurrently\")\n\n    # backward\n    call_command(\"migrate\", \"good_flow_app_concurrently\", \"zero\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_rollback_flow_drop_column_with_notnull_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_rollback_flow_drop_column_with_notnull():\n    # forward\n    call_command(\"migrate\", \"bad_rollback_flow_drop_column_with_notnull_app\")\n\n    # backward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_rollback_flow_drop_column_with_notnull_app\", \"0001\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_rollback_flow_drop_column_with_notnull_default_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_rollback_flow_drop_column_with_notnull_default():\n    # forward\n    call_command(\"migrate\", \"bad_rollback_flow_drop_column_with_notnull_default_app\")\n\n    # backward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_rollback_flow_drop_column_with_notnull_default_app\", \"0001\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_rollback_flow_change_char_type_that_safe_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_rollback_flow_change_char_type_that_safe():\n    # forward\n    call_command(\"migrate\", \"bad_rollback_flow_change_char_type_that_safe_app\")\n\n    # backward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_rollback_flow_change_char_type_that_safe_app\", \"0001\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_flow_add_column_with_default_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_flow_add_column_with_default():\n    # forward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_flow_add_column_with_default_app\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_flow_add_column_with_notnull_default_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_flow_add_column_with_notnull_default():\n    # forward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_flow_add_column_with_notnull_default_app\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_flow_add_column_with_notnull_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_flow_add_column_with_notnull():\n    # forward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_flow_add_column_with_notnull_app\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.bad_flow_change_char_type_that_unsafe_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_bad_flow_change_char_type_that_unsafe():\n    # forward\n    with pytest.raises(UnsafeOperationException):\n        call_command(\"migrate\", \"bad_flow_change_char_type_that_unsafe_app\")\n\n\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.decimal_to_float_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False)\ndef test_decimal_to_float_app():\n    # forward\n    call_command(\"migrate\", \"decimal_to_float_app\")\n\n    # backward\n    call_command(\"migrate\", \"decimal_to_float_app\", \"zero\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_drop_table_with_constraints\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_good_flow_drop_table_with_constraints():\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP=False):\n        call_command(\"migrate\", \"good_flow_drop_table_with_constraints\")\n    drop_tbl_test_table_parent_schema = pg_dump(\"drop_tbl_test_table_parent\")\n    drop_tbl_test_table_child_schema = pg_dump(\"drop_tbl_test_table_child\")\n    call_command(\"migrate\", \"good_flow_drop_table_with_constraints\", \"zero\")\n\n    _drop_child_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"drop_tbl_test_table__main_id_8a4874b6_fk_drop_tbl_\" IMMEDIATE;\n        ALTER TABLE \"drop_tbl_test_table_child\"\n        DROP CONSTRAINT \"drop_tbl_test_table__main_id_8a4874b6_fk_drop_tbl_\";\n    \"\"\")\n    _drop_main_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"drop_tbl_test_table__parent_id_5c6ff8d9_fk_drop_tbl_\" IMMEDIATE;\n        ALTER TABLE \"drop_tbl_test_table_main\"\n        DROP CONSTRAINT \"drop_tbl_test_table__parent_id_5c6ff8d9_fk_drop_tbl_\";\n    \"\"\")\n    _drop_table_sql = one_line_sql(\"\"\"\n        DROP TABLE \"drop_tbl_test_table_main\" CASCADE;\n    \"\"\")\n\n    call_command(\"migrate\", \"good_flow_drop_table_with_constraints\", \"0002\")\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_table_with_constraints\", \"0003\")\n    assert split_sql_queries(migration_sql) == [\n        _drop_child_foreign_key_constraint_sql,\n        _drop_main_foreign_key_constraint_sql,\n        _drop_table_sql,\n    ]\n    call_command(\"migrate\", \"good_flow_drop_table_with_constraints\")\n    assert pg_dump(\"drop_tbl_test_table_parent\") == drop_tbl_test_table_parent_schema\n    assert pg_dump(\"drop_tbl_test_table_child\") == drop_tbl_test_table_child_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_drop_column_with_constraints\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_good_flow_drop_column_with_constraints():\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP=False):\n        call_command(\"migrate\", \"good_flow_drop_column_with_constraints\")\n    drop_col_test_table_parent_schema = pg_dump(\"drop_col_test_table_parent\")\n    drop_col_test_table_main_schema = pg_dump(\"drop_col_test_table_main\")\n    drop_col_test_table_child_schema = pg_dump(\"drop_col_test_table_child\")\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"zero\")\n\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0002\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0003\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i7\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i7\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0003\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0004\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i6\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i6\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0004\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0005\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i5\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i5\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0005\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0006\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i4\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i4\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0006\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0007\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i3\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i3\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0007\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0008\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i2\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i2\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0008\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0009\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i1\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i1\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0009\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0010\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u7\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u7\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0010\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0011\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u6\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u6\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0011\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0012\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u5\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u5\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0012\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0013\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u4\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u4\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0013\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0014\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u3\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u3\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0014\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0015\")\n    assert split_sql_queries(migration_sql) == [\n        'ALTER TABLE \"drop_col_test_table_main\" DROP CONSTRAINT \"drop_col_u2\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u2\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0015\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0016\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u1\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u1\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0016\")\n\n    _drop_main_id_child_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"drop_col_test_table__main_id_9da91a1c_fk_drop_col_\" IMMEDIATE;\n        ALTER TABLE \"drop_col_test_table_child\"\n        DROP CONSTRAINT \"drop_col_test_table__main_id_9da91a1c_fk_drop_col_\";\n    \"\"\")\n    _drop_main_id_field_unique_constraint = one_line_sql(\"\"\"\n        ALTER TABLE \"drop_col_test_table_main\"\n        DROP CONSTRAINT \"drop_col_test_table_main_main_id_key\";\n    \"\"\")\n    _drop_main_id_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"main_id\" CASCADE;\n    \"\"\")\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0017\")\n    assert split_sql_queries(migration_sql) == [\n        _drop_main_id_child_foreign_key_constraint_sql,\n        _drop_main_id_field_unique_constraint,\n        _drop_main_id_column_sql,\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0017\")\n\n    _drop_parent_id_main_foreign_key_constraint_sql = one_line_sql(\"\"\"\n         SET CONSTRAINTS \"drop_col_test_table__parent_id_55b0b5e6_fk_drop_col_\" IMMEDIATE;\n         ALTER TABLE \"drop_col_test_table_main\"\n         DROP CONSTRAINT \"drop_col_test_table__parent_id_55b0b5e6_fk_drop_col_\";\n     \"\"\")\n    _drop_parent_id_field_unique_constraint = one_line_sql(\"\"\"\n         ALTER TABLE \"drop_col_test_table_main\"\n         DROP CONSTRAINT \"drop_col_test_table_main_parent_id_55b0b5e6_uniq\";\n     \"\"\")\n    _drop_parent_id_column_sql = one_line_sql(\"\"\"\n         ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"parent_id\" CASCADE;\n     \"\"\")\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints\", \"0018\")\n    assert split_sql_queries(migration_sql) == [\n        _drop_parent_id_main_foreign_key_constraint_sql,\n        _drop_parent_id_field_unique_constraint,\n        _drop_parent_id_column_sql,\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\", \"0018\")\n\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints\")\n    assert pg_dump(\"drop_col_test_table_parent\") == drop_col_test_table_parent_schema\n    assert pg_dump(\"drop_col_test_table_main\") == drop_col_test_table_main_schema\n    assert pg_dump(\"drop_col_test_table_child\") == drop_col_test_table_child_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.skipif(\n    django.VERSION[:2] >= (4, 0),\n    reason=\"django after 4.0 case\",\n)\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.good_flow_drop_column_with_constraints_old\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_good_flow_drop_column_with_constraints_old():\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP=False):\n        call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\")\n    drop_col_test_table_parent_schema = pg_dump(\"drop_col_test_table_parent\")\n    drop_col_test_table_main_schema = pg_dump(\"drop_col_test_table_main\")\n    drop_col_test_table_child_schema = pg_dump(\"drop_col_test_table_child\")\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"zero\")\n\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0002\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0003\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i7\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i7\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0003\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0004\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i6\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i6\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0004\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0005\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i5\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i5\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0005\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0006\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i4\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i4\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0006\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0007\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i3\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i3\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0007\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0008\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i2\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i2\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0008\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0009\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_i1\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_i1\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0009\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0010\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u7\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u7\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0010\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0011\")\n    assert split_sql_queries(migration_sql) == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"drop_col_u5\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u5\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0011\")\n\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0012\")\n    assert split_sql_queries(migration_sql) == [\n        'ALTER TABLE \"drop_col_test_table_main\" DROP CONSTRAINT \"drop_col_u2\";',\n        'ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"field_u2\" CASCADE;',\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0012\")\n\n    _drop_main_id_child_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"drop_col_test_table__main_id_9da91a1c_fk_drop_col_\" IMMEDIATE;\n        ALTER TABLE \"drop_col_test_table_child\"\n        DROP CONSTRAINT \"drop_col_test_table__main_id_9da91a1c_fk_drop_col_\";\n    \"\"\")\n    _drop_main_id_field_unique_constraint = one_line_sql(\"\"\"\n        ALTER TABLE \"drop_col_test_table_main\"\n        DROP CONSTRAINT \"drop_col_test_table_main_main_id_key\";\n    \"\"\")\n    _drop_main_id_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"main_id\" CASCADE;\n    \"\"\")\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0013\")\n    assert split_sql_queries(migration_sql) == [\n        _drop_main_id_child_foreign_key_constraint_sql,\n        _drop_main_id_field_unique_constraint,\n        _drop_main_id_column_sql,\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0013\")\n\n    _drop_parent_id_main_foreign_key_constraint_sql = one_line_sql(\"\"\"\n         SET CONSTRAINTS \"drop_col_test_table__parent_id_55b0b5e6_fk_drop_col_\" IMMEDIATE;\n         ALTER TABLE \"drop_col_test_table_main\"\n         DROP CONSTRAINT \"drop_col_test_table__parent_id_55b0b5e6_fk_drop_col_\";\n     \"\"\")\n    _drop_parent_id_field_unique_constraint = one_line_sql(\"\"\"\n         ALTER TABLE \"drop_col_test_table_main\"\n         DROP CONSTRAINT \"drop_col_test_table_main_parent_id_55b0b5e6_uniq\";\n     \"\"\")\n    _drop_parent_id_column_sql = one_line_sql(\"\"\"\n         ALTER TABLE \"drop_col_test_table_main\" DROP COLUMN \"parent_id\" CASCADE;\n     \"\"\")\n    migration_sql = call_command(\"sqlmigrate\", \"good_flow_drop_column_with_constraints_old\", \"0014\")\n    assert split_sql_queries(migration_sql) == [\n        _drop_parent_id_main_foreign_key_constraint_sql,\n        _drop_parent_id_field_unique_constraint,\n        _drop_parent_id_column_sql,\n    ]\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\", \"0014\")\n\n    call_command(\"migrate\", \"good_flow_drop_column_with_constraints_old\")\n    assert pg_dump(\"drop_col_test_table_parent\") == drop_col_test_table_parent_schema\n    assert pg_dump(\"drop_col_test_table_main\") == drop_col_test_table_main_schema\n    assert pg_dump(\"drop_col_test_table_child\") == drop_col_test_table_child_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_create_table_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_create_table():\n    _create_table_sql = one_line_sql(\"\"\"\n        CREATE TABLE \"idempotency_create_table_app_relatedtesttable\" (\n            \"id\" integer NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,\n            \"test_field_int\" integer NULL,\n            \"test_model_id\" integer NULL\n        );\n    \"\"\")\n    _create_unique_index_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"idempotency_create_table_app_relatedtesttable_uniq\"\n        ON \"idempotency_create_table_app_relatedtesttable\" (\"test_model_id\", \"test_field_int\");\n    \"\"\")\n    _create_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_create_table_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_create_table_app_relatedtesttable_uniq\"\n        UNIQUE USING INDEX \"idempotency_create_table_app_relatedtesttable_uniq\";\n    \"\"\")\n    _create_foreign_key_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_create_table_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_create_t_test_model_id_09b52f79_fk_idempoten\"\n        FOREIGN KEY (\"test_model_id\")\n        REFERENCES \"idempotency_create_table_app_testtable\" (\"id\")\n        DEFERRABLE INITIALLY DEFERRED NOT VALID;\n    \"\"\")\n    _validate_foreign_key_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_create_table_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_create_t_test_model_id_09b52f79_fk_idempoten\";\n    \"\"\")\n    _create_index_sql = one_line_sql(\"\"\"\n        CREATE INDEX CONCURRENTLY \"idempotency_create_table_a_test_model_id_09b52f79\"\n        ON \"idempotency_create_table_app_relatedtesttable\" (\"test_model_id\");\n    \"\"\")\n\n    _drop_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_create_table_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_create_table_app_relatedtesttable_uniq\";\n    \"\"\")\n    _drop_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"idempotency_create_t_test_model_id_09b52f79_fk_idempoten\" IMMEDIATE;\n        ALTER TABLE \"idempotency_create_table_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_create_t_test_model_id_09b52f79_fk_idempoten\";\n    \"\"\")\n    _drop_table_sql = one_line_sql(\"\"\"\n        DROP TABLE \"idempotency_create_table_app_relatedtesttable\" CASCADE;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    new_schema = pg_dump(\"idempotency_create_table_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_create_table_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_table_sql,\n        _create_unique_index_sql,\n        _create_unique_constraint_sql,\n        _create_foreign_key_sql,\n        _validate_foreign_key_sql,\n        _create_index_sql,\n    ]\n\n    # migrate case 1\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n\n    # migrate case 2.1\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n        cursor.execute(_create_unique_index_sql)\n    assert is_valid_index(\n        \"idempotency_create_table_app_relatedtesttable\",\n        \"idempotency_create_table_app_relatedtesttable_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_create_table_app_relatedtesttable\",\n        \"idempotency_create_table_app_relatedtesttable_uniq\",\n    )\n\n    # migrate case 2.2\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n        cursor.execute(_create_unique_index_sql)\n    make_index_invalid(\n        \"idempotency_create_table_app_relatedtesttable\",\n        \"idempotency_create_table_app_relatedtesttable_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_create_table_app_relatedtesttable\",\n        \"idempotency_create_table_app_relatedtesttable_uniq\",\n    )\n\n    # migrate case 3\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n\n    # migrate case 4\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n\n    # migrate case 5\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_sql)\n        cursor.execute(_validate_foreign_key_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n\n    # migrate case 6\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_table_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_sql)\n        cursor.execute(_validate_foreign_key_sql)\n        cursor.execute(_create_index_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    assert pg_dump(\"idempotency_create_table_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop table case)\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_create_table_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_unique_constraint_sql,\n        _drop_foreign_key_constraint_sql,\n        _drop_table_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_create_table_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_unique_constraint_sql)\n        cursor.execute(_drop_table_sql)\n    call_command(\"migrate\", \"idempotency_create_table_app\", \"0001\")\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_column_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_column():\n    _add_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_app_relatedtesttable\"\n        ADD COLUMN \"test_field_str\" varchar(10) NULL;\n    \"\"\")\n\n    _drop_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_app_relatedtesttable\"\n        DROP COLUMN \"test_field_str\" CASCADE;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_column_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_column_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_column_app\")\n    new_schema = pg_dump(\"idempotency_add_column_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_column_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_column_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _add_column_sql\n    ]\n\n    # migrate case 1\n    call_command(\"migrate\", \"idempotency_add_column_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n    call_command(\"migrate\", \"idempotency_add_column_app\")\n    assert pg_dump(\"idempotency_add_column_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop column case)\n    call_command(\"migrate\", \"idempotency_add_column_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_column_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_column_sql\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_column_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_column_sql)\n    call_command(\"migrate\", \"idempotency_add_column_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_column_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_column_foreign_key_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_column_foreign_key():\n    _add_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_foreign_key_app_relatedtesttable\"\n        ADD COLUMN \"test_model_id\" integer NULL;\n    \"\"\")\n    _create_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_foreign_key_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_colu_test_model_id_99eba75b_fk_idempoten\"\n        FOREIGN KEY (\"test_model_id\")\n        REFERENCES \"idempotency_add_column_foreign_key_app_testtable\" (\"id\")\n        DEFERRABLE INITIALLY DEFERRED NOT VALID;\n    \"\"\")\n    _validate_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_foreign_key_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_add_colu_test_model_id_99eba75b_fk_idempoten\";\n    \"\"\")\n    _create_index_sql = one_line_sql(\"\"\"\n        CREATE INDEX CONCURRENTLY \"idempotency_add_column_for_test_model_id_99eba75b\"\n        ON \"idempotency_add_column_foreign_key_app_relatedtesttable\" (\"test_model_id\");\n    \"\"\")\n\n    _drop_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"idempotency_add_column_for_test_model_id_99eba75b\";\n    \"\"\")\n    _drop_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"idempotency_add_colu_test_model_id_99eba75b_fk_idempoten\" IMMEDIATE;\n        ALTER TABLE \"idempotency_add_column_foreign_key_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_colu_test_model_id_99eba75b_fk_idempoten\";\n    \"\"\")\n    _drop_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_foreign_key_app_relatedtesttable\"\n        DROP COLUMN \"test_model_id\" CASCADE;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    new_schema = pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_column_foreign_key_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _add_column_sql,\n        _create_foreign_key_constraint_sql,\n        _validate_foreign_key_constraint_sql,\n        _create_index_sql,\n    ]\n\n    # migrate case 1\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == new_schema\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == new_schema\n\n    # migrate case 3\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n        cursor.execute(_validate_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == new_schema\n\n    # migrate case 4.1\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n        cursor.execute(_validate_foreign_key_constraint_sql)\n        cursor.execute(_create_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_column_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_column_for_test_model_id_99eba75b\",\n    )\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_column_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_column_for_test_model_id_99eba75b\",\n    )\n\n    # migrate case 4.2\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n        cursor.execute(_validate_foreign_key_constraint_sql)\n        cursor.execute(_create_index_sql)\n    make_index_invalid(\n        \"idempotency_add_column_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_column_for_test_model_id_99eba75b\",\n    )\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_column_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_column_for_test_model_id_99eba75b\",\n    )\n\n    # rollback (covers drop column case)\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_column_foreign_key_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_foreign_key_constraint_sql,\n        _drop_index_sql,\n        _drop_column_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n        cursor.execute(_drop_column_sql)\n    call_command(\"migrate\", \"idempotency_add_column_foreign_key_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_column_foreign_key_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_column_one_to_one_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_column_one_to_one():\n    _add_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        ADD COLUMN \"test_model_id\" integer NULL;\n    \"\"\")\n    _create_unique_index_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\"\n        ON \"idempotency_add_column_one_to_one_app_relatedtesttable\" (\"test_model_id\");\n    \"\"\")\n    _create_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\"\n        UNIQUE USING INDEX \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\";\n    \"\"\")\n    _create_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_colu_test_model_id_3c5a49fe_fk_idempoten\"\n        FOREIGN KEY (\"test_model_id\")\n        REFERENCES \"idempotency_add_column_one_to_one_app_testtable\" (\"id\")\n        DEFERRABLE INITIALLY DEFERRED NOT VALID;\n    \"\"\")\n    _validate_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_add_colu_test_model_id_3c5a49fe_fk_idempoten\";\n    \"\"\")\n\n    _drop_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\";\n    \"\"\")\n    _drop_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"idempotency_add_colu_test_model_id_3c5a49fe_fk_idempoten\" IMMEDIATE;\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_colu_test_model_id_3c5a49fe_fk_idempoten\";\n    \"\"\")\n    _drop_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_column_one_to_one_app_relatedtesttable\"\n        DROP COLUMN \"test_model_id\" CASCADE;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    new_schema = pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_column_one_to_one_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _add_column_sql,\n        _create_unique_index_sql,\n        _create_unique_constraint_sql,\n        _create_foreign_key_constraint_sql,\n        _validate_foreign_key_constraint_sql,\n    ]\n\n    # migrate case 1\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == new_schema\n\n    # migrate case 2.1\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_unique_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_column_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_column_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\",\n    )\n\n    # migrate case 2.2\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_unique_index_sql)\n    make_index_invalid(\n        \"idempotency_add_column_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_column_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_column_o_test_model_id_3c5a49fe_uniq\",\n    )\n\n    # migrate case 3\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == new_schema\n\n    # migrate case 4\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == new_schema\n\n    # migrate case 5\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_add_column_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n        cursor.execute(_validate_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop column case)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_column_one_to_one_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_foreign_key_constraint_sql,\n        _drop_unique_constraint_sql,\n        _drop_column_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n        cursor.execute(_drop_column_sql)\n    call_command(\"migrate\", \"idempotency_add_column_one_to_one_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_column_one_to_one_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_set_not_null_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_set_not_null():\n    _create_check_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_set_not_null_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_set_not_nu_test_field_int_76dfbad6_notnull\"\n        CHECK (\"test_field_int\" IS NOT NULL) NOT VALID;\n    \"\"\")\n    _validate_check_constraint_sql = one_line_sql(\n        \"\"\"\n        ALTER TABLE \"idempotency_set_not_null_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_set_not_nu_test_field_int_76dfbad6_notnull\";\n    \"\"\")\n    _set_column_not_null_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_set_not_null_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" SET NOT NULL;\n    \"\"\")\n    _drop_check_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_set_not_null_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_set_not_nu_test_field_int_76dfbad6_notnull\";\n    \"\"\")\n\n    _drop_column_not_null_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_set_not_null_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" DROP NOT NULL;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_set_not_null_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    new_schema = pg_dump(\"idempotency_set_not_null_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_set_not_null_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_check_constraint_sql,\n        _validate_check_constraint_sql,\n        _set_column_not_null_sql,\n        _drop_check_constraint_sql,\n    ]\n\n    # migrate case 1\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_check_constraint_sql)\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    assert pg_dump(\"idempotency_set_not_null_app_relatedtesttable\") == new_schema\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_check_constraint_sql)\n        cursor.execute(_validate_check_constraint_sql)\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    assert pg_dump(\"idempotency_set_not_null_app_relatedtesttable\") == new_schema\n\n    # migrate case 3\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_check_constraint_sql)\n        cursor.execute(_validate_check_constraint_sql)\n        cursor.execute(_set_column_not_null_sql)\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    assert pg_dump(\"idempotency_set_not_null_app_relatedtesttable\") == new_schema\n\n    # migrate case 4\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_check_constraint_sql)\n        cursor.execute(_validate_check_constraint_sql)\n        cursor.execute(_set_column_not_null_sql)\n        cursor.execute(_drop_check_constraint_sql)\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    assert pg_dump(\"idempotency_set_not_null_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop not null case)\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_set_not_null_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_column_not_null_sql\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_set_not_null_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_column_not_null_sql)\n    call_command(\"migrate\", \"idempotency_set_not_null_app\", \"0001\")\n    assert pg_dump(\"idempotency_set_not_null_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_check_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_check():\n    _create_check_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_check_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_check_app_relatedtesttable_check\"\n        CHECK (\"test_field_int\" > 0) NOT VALID;\n    \"\"\")\n    _validate_check_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_check_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_add_check_app_relatedtesttable_check\";\n    \"\"\")\n\n    _drop_check_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_check_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_check_app_relatedtesttable_check\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_check_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_check_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_check_app\")\n    new_schema = pg_dump(\"idempotency_add_check_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_check_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_check_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_check_constraint_sql,\n        _validate_check_constraint_sql,\n    ]\n\n    # migrate case 1\n    call_command(\"migrate\", \"idempotency_add_check_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_check_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_check_app\")\n    assert pg_dump(\"idempotency_add_check_app_relatedtesttable\") == new_schema\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_add_check_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_check_constraint_sql)\n        cursor.execute(_validate_check_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_check_app\")\n    assert pg_dump(\"idempotency_add_check_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop check case)\n    call_command(\"migrate\", \"idempotency_add_check_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_check_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_check_constraint_sql\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_check_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_check_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_check_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_check_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_foreign_key_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_foreign_key():\n    _create_index_sql = one_line_sql(\"\"\"\n        CREATE INDEX CONCURRENTLY \"idempotency_add_foreign_ke_test_field_int_fa01ee40\"\n        ON \"idempotency_add_foreign_key_app_relatedtesttable\" (\"test_field_int\");\n    \"\"\")\n    _create_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_foreign_key_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_fore_test_field_int_fa01ee40_fk_idempoten\"\n        FOREIGN KEY (\"test_field_int\")\n        REFERENCES \"idempotency_add_foreign_key_app_testtable\" (\"id\")\n        DEFERRABLE INITIALLY DEFERRED NOT VALID;\n    \"\"\")\n    _validate_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_foreign_key_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_add_fore_test_field_int_fa01ee40_fk_idempoten\";\n    \"\"\")\n\n    _drop_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"idempotency_add_fore_test_field_int_fa01ee40_fk_idempoten\" IMMEDIATE;\n        ALTER TABLE \"idempotency_add_foreign_key_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_fore_test_field_int_fa01ee40_fk_idempoten\";\n    \"\"\")\n    _drop_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"idempotency_add_foreign_ke_test_field_int_fa01ee40\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    new_schema = pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_foreign_key_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_index_sql,\n        _create_foreign_key_constraint_sql,\n        _validate_foreign_key_constraint_sql,\n    ]\n\n    # migrate case 1.1\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_foreign_ke_test_field_int_fa01ee40\",\n    )\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_foreign_ke_test_field_int_fa01ee40\",\n    )\n\n    # migrate case 1.2\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n    make_index_invalid(\n        \"idempotency_add_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_foreign_ke_test_field_int_fa01ee40\",\n    )\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_foreign_key_app_relatedtesttable\",\n        \"idempotency_add_foreign_ke_test_field_int_fa01ee40\",\n    )\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\") == new_schema\n\n    # migrate case 3\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n        cursor.execute(_validate_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    assert pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop foreign key case)\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_foreign_key_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_foreign_key_constraint_sql,\n        _drop_index_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n        cursor.execute(_drop_index_sql)\n    call_command(\"migrate\", \"idempotency_add_foreign_key_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_foreign_key_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_one_to_one_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_one_to_one():\n    _create_unique_index_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\"\n        ON \"idempotency_add_one_to_one_app_relatedtesttable\" (\"test_field_int\");\n    \"\"\")\n    _create_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_one_to_one_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\"\n        UNIQUE USING INDEX \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\";\n    \"\"\")\n    _create_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_one_to_one_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_one__test_field_int_8ebac681_fk_idempoten\"\n        FOREIGN KEY (\"test_field_int\")\n        REFERENCES \"idempotency_add_one_to_one_app_testtable\" (\"id\")\n        DEFERRABLE INITIALLY DEFERRED NOT VALID;\n    \"\"\")\n    _validate_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_one_to_one_app_relatedtesttable\"\n        VALIDATE CONSTRAINT \"idempotency_add_one__test_field_int_8ebac681_fk_idempoten\";\n    \"\"\")\n\n    _drop_foreign_key_constraint_sql = one_line_sql(\"\"\"\n        SET CONSTRAINTS \"idempotency_add_one__test_field_int_8ebac681_fk_idempoten\" IMMEDIATE;\n        ALTER TABLE \"idempotency_add_one_to_one_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_one__test_field_int_8ebac681_fk_idempoten\";\n    \"\"\")\n    _drop_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_one_to_one_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\";\n    \"\"\")\n    _drop_like_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"idempotency_add_one_to_o_test_field_int_8ebac681_like\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    new_schema = pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_one_to_one_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_unique_index_sql,\n        _create_unique_constraint_sql,\n        _create_foreign_key_constraint_sql,\n        _validate_foreign_key_constraint_sql,\n    ]\n\n    # migrate case 1.1\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\",\n    )\n\n    # migrate case 1.2\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n    make_index_invalid(\n        \"idempotency_add_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_one_to_one_app_relatedtesttable\",\n        \"idempotency_add_one_to_o_test_field_int_8ebac681_uniq\",\n    )\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == new_schema\n\n    # migrate case 3\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == new_schema\n\n    # migrate case 4\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n        cursor.execute(_create_foreign_key_constraint_sql)\n        cursor.execute(_validate_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop one to one case)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_one_to_one_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_foreign_key_constraint_sql,\n        _drop_unique_constraint_sql,\n        _drop_like_index_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n        cursor.execute(_drop_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == old_schema\n\n    # rollback case 3\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_foreign_key_constraint_sql)\n        cursor.execute(_drop_unique_constraint_sql)\n        cursor.execute(_drop_like_index_sql)\n    call_command(\"migrate\", \"idempotency_add_one_to_one_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_one_to_one_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_index_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_index():\n    _create_index_sql = one_line_sql(\"\"\"\n        CREATE INDEX CONCURRENTLY \"idempotency_add_index_app__test_field_int_ecc428b5\"\n        ON \"idempotency_add_index_app_relatedtesttable\" (\"test_field_int\");\n    \"\"\")\n\n    _drop_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"idempotency_add_index_app__test_field_int_ecc428b5\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_index_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_index_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_index_app\")\n    new_schema = pg_dump(\"idempotency_add_index_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_index_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_index_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_index_sql,\n    ]\n\n    # migrate case 1.1\n    call_command(\"migrate\", \"idempotency_add_index_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_index_app_relatedtesttable\",\n        \"idempotency_add_index_app__test_field_int_ecc428b5\",\n    )\n    call_command(\"migrate\", \"idempotency_add_index_app\")\n    assert pg_dump(\"idempotency_add_index_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_index_app_relatedtesttable\",\n        \"idempotency_add_index_app__test_field_int_ecc428b5\",\n    )\n\n    # migrate case 1.2\n    call_command(\"migrate\", \"idempotency_add_index_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n    make_index_invalid(\n        \"idempotency_add_index_app_relatedtesttable\",\n        \"idempotency_add_index_app__test_field_int_ecc428b5\",\n    )\n    call_command(\"migrate\", \"idempotency_add_index_app\")\n    assert pg_dump(\"idempotency_add_index_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_index_app_relatedtesttable\",\n        \"idempotency_add_index_app__test_field_int_ecc428b5\",\n    )\n\n    # rollback (covers drop index case)\n    call_command(\"migrate\", \"idempotency_add_index_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_index_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_index_sql\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_index_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_index_sql)\n    call_command(\"migrate\", \"idempotency_add_index_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_index_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_index_meta_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_index_meta():\n    _create_index_sql = one_line_sql(\"\"\"\n        CREATE INDEX CONCURRENTLY \"relatedtesttable_idx\"\n        ON \"idempotency_add_index_meta_app_relatedtesttable\" (\"test_field_int\", \"test_field_str\");\n    \"\"\")\n\n    _drop_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"relatedtesttable_idx\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_index_meta_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\")\n    new_schema = pg_dump(\"idempotency_add_index_meta_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_index_meta_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_index_sql,\n    ]\n\n    # migrate case 1.1\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_index_meta_app_relatedtesttable\",\n        \"relatedtesttable_idx\",\n    )\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\")\n    assert pg_dump(\"idempotency_add_index_meta_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_index_meta_app_relatedtesttable\",\n        \"relatedtesttable_idx\",\n    )\n\n    # migrate case 1.2\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_index_sql)\n    make_index_invalid(\n        \"idempotency_add_index_meta_app_relatedtesttable\",\n        \"relatedtesttable_idx\",\n    )\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\")\n    assert pg_dump(\"idempotency_add_index_meta_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_index_meta_app_relatedtesttable\",\n        \"relatedtesttable_idx\",\n    )\n\n    # rollback (covers drop index case)\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_index_meta_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_index_sql\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_index_sql)\n    call_command(\"migrate\", \"idempotency_add_index_meta_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_index_meta_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_unique_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_unique():\n    _create_unique_index_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\"\n        ON \"idempotency_add_unique_app_relatedtesttable\" (\"test_field_int\");\n    \"\"\")\n    _create_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_unique_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\"\n        UNIQUE USING INDEX \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\";\n    \"\"\")\n\n    _drop_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_unique_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\";\n    \"\"\")\n    _drop_like_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"idempotency_add_unique_a_test_field_int_01c4f0c0_like\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_unique_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    new_schema = pg_dump(\"idempotency_add_unique_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_unique_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_unique_index_sql,\n        _create_unique_constraint_sql,\n    ]\n\n    # migrate case 1.1\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_unique_app_relatedtesttable\",\n        \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    assert pg_dump(\"idempotency_add_unique_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_unique_app_relatedtesttable\",\n        \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\",\n    )\n\n    # migrate case 1.2\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n    make_index_invalid(\n        \"idempotency_add_unique_app_relatedtesttable\",\n        \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    assert pg_dump(\"idempotency_add_unique_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_unique_app_relatedtesttable\",\n        \"idempotency_add_unique_a_test_field_int_01c4f0c0_uniq\",\n    )\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    assert pg_dump(\"idempotency_add_unique_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop unique case)\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_unique_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_unique_constraint_sql,\n        _drop_like_index_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_unique_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_unique_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_unique_constraint_sql)\n        cursor.execute(_drop_like_index_sql)\n    call_command(\"migrate\", \"idempotency_add_unique_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_unique_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_unique_meta_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_unique_meta():\n    _create_unique_index_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"relatedtesttable_uniq\"\n        ON \"idempotency_add_unique_meta_app_relatedtesttable\" (\"test_field_int\", \"test_field_str\");\n    \"\"\")\n    _create_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_unique_meta_app_relatedtesttable\"\n        ADD CONSTRAINT \"relatedtesttable_uniq\"\n        UNIQUE USING INDEX \"relatedtesttable_uniq\";\n    \"\"\")\n\n    _drop_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_unique_meta_app_relatedtesttable\"\n        DROP CONSTRAINT \"relatedtesttable_uniq\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_unique_meta_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\")\n    new_schema = pg_dump(\"idempotency_add_unique_meta_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_unique_meta_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _create_unique_index_sql,\n        _create_unique_constraint_sql,\n    ]\n\n    # migrate case 1.1\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_unique_meta_app_relatedtesttable\",\n        \"relatedtesttable_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\")\n    assert pg_dump(\"idempotency_add_unique_meta_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_unique_meta_app_relatedtesttable\",\n        \"relatedtesttable_uniq\",\n    )\n\n    # migrate case 1.2\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n    make_index_invalid(\n        \"idempotency_add_unique_meta_app_relatedtesttable\",\n        \"relatedtesttable_uniq\",\n    )\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\")\n    assert pg_dump(\"idempotency_add_unique_meta_app_relatedtesttable\") == new_schema\n    assert is_valid_index(\n        \"idempotency_add_unique_meta_app_relatedtesttable\",\n        \"relatedtesttable_uniq\",\n    )\n\n    # migrate case 2\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\")\n    assert pg_dump(\"idempotency_add_unique_meta_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop unique case)\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_unique_meta_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_unique_constraint_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_unique_constraint_sql)\n    call_command(\"migrate\", \"idempotency_add_unique_meta_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_unique_meta_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_primary_key_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_primary_key():\n    _drop_column_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n        DROP COLUMN \"id\" CASCADE;\n    \"\"\")\n    _create_unique_index_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\"\n        ON \"idempotency_add_primary_key_app_relatedtesttable\" (\"test_field_int\");\n    \"\"\")\n    _create_primary_key_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\"\n        PRIMARY KEY USING INDEX \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\";\n    \"\"\")\n\n    _drop_unique_constraint_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_primary__test_field_int_e9cebf24_uniq\";\n    \"\"\")\n    _drop_primary_key_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n        DROP CONSTRAINT \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\";\n    \"\"\")\n    _drop_unique_index_sql = one_line_sql(\"\"\"\n        DROP INDEX CONCURRENTLY IF EXISTS \"idempotency_add_primary__test_field_int_e9cebf24_like\";\n    \"\"\")\n    _add_column_for_rollback_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n        ADD COLUMN \"id\" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY;\n    \"\"\")\n    if django.VERSION[:2] < (4, 1):\n        _add_column_for_rollback_sql = one_line_sql(\"\"\"\n            ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n            ADD COLUMN \"id\" serial NOT NULL;\n        \"\"\")\n    _create_unique_index_for_rollback_sql = one_line_sql(\"\"\"\n        CREATE UNIQUE INDEX CONCURRENTLY \"idempotency_add_primary_key_app_relatedtesttable_id_d0e5667c_pk\"\n        ON \"idempotency_add_primary_key_app_relatedtesttable\" (\"id\");\n    \"\"\")\n    _create_unique_constraint_for_rollback_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_primary_key_app_relatedtesttable\"\n        ADD CONSTRAINT \"idempotency_add_primary_key_app_relatedtesttable_id_d0e5667c_pk\"\n        PRIMARY KEY USING INDEX \"idempotency_add_primary_key_app_relatedtesttable_id_d0e5667c_pk\";\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    new_schema = pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")\n\n    # migrate\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_primary_key_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _drop_column_sql,\n        _create_unique_index_sql,\n        _create_primary_key_sql,\n    ]\n\n    # migrate case 1\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_column_sql)\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    assert pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\") == new_schema\n\n    # migrate case 2.1\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_column_sql)\n        cursor.execute(_create_unique_index_sql)\n    assert is_valid_index(\n        \"idempotency_add_primary_key_app_relatedtesttable\",\n        \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\",\n    )\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    assert pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_primary_key_app_relatedtesttable\",\n        \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\",\n    )\n\n    # migrate case 2.2\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_column_sql)\n        cursor.execute(_create_unique_index_sql)\n    make_index_invalid(\n        \"idempotency_add_primary_key_app_relatedtesttable\",\n        \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\",\n    )\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    assert pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\") == new_schema\n    assert is_valid_constraint(\n        \"idempotency_add_primary_key_app_relatedtesttable\",\n        \"idempotency_add_primary_k_test_field_int_e9cebf24_pk\",\n    )\n\n    # migrate case 3\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_column_sql)\n        cursor.execute(_create_unique_index_sql)\n        cursor.execute(_create_primary_key_sql)\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    assert pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop primary key case)\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    with override_settings(\n        ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False,\n        ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False,\n    ):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_primary_key_app\", \"0002\")\n    if django.VERSION[:2] < (4, 1):\n        assert split_sql_queries(rollback_sql) == [\n            _drop_unique_constraint_sql,\n            _drop_primary_key_sql,\n            _drop_unique_index_sql,\n            _add_column_for_rollback_sql,\n            _create_unique_index_for_rollback_sql,\n            _create_unique_constraint_for_rollback_sql,\n        ]\n    else:\n        assert split_sql_queries(rollback_sql) == [\n            _drop_primary_key_sql,\n            _drop_unique_index_sql,\n            _add_column_for_rollback_sql,\n            _create_unique_index_for_rollback_sql,\n            _create_unique_constraint_for_rollback_sql,\n        ]\n\n    def old_schema_compatible(dump: str) -> str:\n        \"\"\"\n        django creates different name for primary key constraint than postgres\n        rolling back drop index can be reason of columns order changes\n        \"\"\"\n        return dump.replace(\n            \"idempotency_add_primary_key_app_relatedtesttable_id_d0e5667c_pk\",\n            \"idempotency_add_primary_key_app_relatedtesttable_pkey\",\n        ).replace(\n            \"test_field_int integer NOT NULL,\\n    id integer NOT NULL\",\n            \"id integer NOT NULL,\\n    test_field_int integer NOT NULL\",\n        )\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_primary_key_sql)\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    assert old_schema_compatible(pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")) == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_primary_key_sql)\n        cursor.execute(_drop_unique_index_sql)\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    assert old_schema_compatible(pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")) == old_schema\n\n    # rollback case 3\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_primary_key_sql)\n        cursor.execute(_drop_unique_index_sql)\n        cursor.execute(_add_column_for_rollback_sql)\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    assert old_schema_compatible(pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")) == old_schema\n\n    # rollback case 4\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_primary_key_sql)\n        cursor.execute(_drop_unique_index_sql)\n        cursor.execute(_add_column_for_rollback_sql)\n        cursor.execute(_create_unique_index_for_rollback_sql)\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    assert old_schema_compatible(pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")) == old_schema\n\n    # rollback case 5\n    call_command(\"migrate\", \"idempotency_add_primary_key_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_primary_key_sql)\n        cursor.execute(_drop_unique_index_sql)\n        cursor.execute(_add_column_for_rollback_sql)\n        cursor.execute(_create_unique_index_for_rollback_sql)\n        cursor.execute(_create_unique_constraint_for_rollback_sql)\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_primary_key_app\", \"0001\")\n    assert old_schema_compatible(pg_dump(\"idempotency_add_primary_key_app_relatedtesttable\")) == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_auto_field_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_auto_field():\n    _set_type_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" TYPE integer;\n    \"\"\")\n    _set_identity_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" ADD GENERATED BY DEFAULT AS IDENTITY;\n    \"\"\")\n\n    _drop_identity_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" DROP IDENTITY IF EXISTS;\n    \"\"\")\n    _set_type_for_rollback_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" TYPE integer;\n    \"\"\")\n    _sql_drop_sequence_sql = one_line_sql(\"\"\"\n        DROP SEQUENCE IF EXISTS \"idempotency_add_auto_field_app_relatedtestta_test_field_int_seq\" CASCADE;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    new_schema = pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_auto_field_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _set_type_sql,\n        _set_identity_sql,\n    ]\n\n    # migrate case 1\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == new_schema\n\n    # migrate case 2\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n        cursor.execute(_set_identity_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == new_schema\n\n    # rollback (covers drop auto field case)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_auto_field_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _drop_identity_sql,\n        _set_type_for_rollback_sql,\n        _sql_drop_sequence_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_identity_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_identity_sql)\n        cursor.execute(_set_type_for_rollback_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == old_schema\n\n    # rollback case 3\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_identity_sql)\n        cursor.execute(_set_type_for_rollback_sql)\n        cursor.execute(_sql_drop_sequence_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == old_schema\n\n\n@skip_for_default_django_backend\n@pytest.mark.skipif(\n    django.VERSION[:2] >= (4, 1),\n    reason=\"django before 4.1 case\",\n)\n@pytest.mark.django_db(transaction=True)\n@modify_settings(INSTALLED_APPS={\"append\": \"tests.apps.idempotency_add_auto_field_app\"})\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True)\ndef test_idempotency_add_auto_field_old():\n    _set_type_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" TYPE integer USING \"test_field_int\"::integer;\n    \"\"\")\n    _drop_sequence_sql = one_line_sql(\"\"\"\n        DROP SEQUENCE IF EXISTS \"idempotency_add_auto_field_app_relatedtesttable_test_field_int_seq\" CASCADE;\n    \"\"\")\n    _create_sequence_sql = one_line_sql(\"\"\"\n        CREATE SEQUENCE \"idempotency_add_auto_field_app_relatedtesttable_test_field_int_seq\";\n    \"\"\")\n    _set_default_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\"\n        SET DEFAULT nextval(\\'\"idempotency_add_auto_field_app_relatedtesttable_test_field_int_seq\"\\');\n    \"\"\")\n    _set_max_value_sql = one_line_sql(\"\"\"\n        SELECT setval('\"idempotency_add_auto_field_app_relatedtesttable_test_field_int_seq\"', MAX(\"test_field_int\"))\n        FROM \"idempotency_add_auto_field_app_relatedtesttable\";\n    \"\"\")\n    _set_owner_sql = one_line_sql(\"\"\"\n        ALTER SEQUENCE \"idempotency_add_auto_field_app_relatedtesttable_test_field_int_seq\"\n        OWNED BY \"idempotency_add_auto_field_app_relatedtesttable\".\"test_field_int\";\n    \"\"\")\n\n    _set_type_for_rollback_sql = one_line_sql(\"\"\"\n        ALTER TABLE \"idempotency_add_auto_field_app_relatedtesttable\"\n        ALTER COLUMN \"test_field_int\" TYPE integer USING \"test_field_int\"::integer;\n    \"\"\")\n    _drop_sequence_sql = one_line_sql(\"\"\"\n        DROP SEQUENCE IF EXISTS \"idempotency_add_auto_field_app_relatedtesttable_test_field_int_seq\" CASCADE;\n    \"\"\")\n\n    # get target schema\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    old_schema = pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    new_schema = pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")\n\n    # migrate\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        migration_sql = call_command(\"sqlmigrate\", \"idempotency_add_auto_field_app\", \"0002\")\n    assert split_sql_queries(migration_sql) == [\n        _set_type_sql,\n        _drop_sequence_sql,\n        _create_sequence_sql,\n        _set_default_sql,\n        _set_max_value_sql,\n        _set_owner_sql,\n    ]\n\n    def schema_compatible_sequence_alter(dump: str) -> str:\n        return dump.replace(\n            textwrap.dedent(\"\"\"\n                CREATE TABLE public.idempotency_add_auto_field_app_relatedtesttable (\n                    test_field_int integer DEFAULT\n                    nextval('public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_'::regclass) NOT NULL\n                );\n\n\n                ALTER TABLE public.idempotency_add_auto_field_app_relatedtesttable OWNER TO test;\n            \"\"\").replace(\"\\n    nextval\", \" nextval\"),\n            textwrap.dedent(\"\"\"\n                CREATE TABLE public.idempotency_add_auto_field_app_relatedtesttable (\n                    test_field_int integer NOT NULL\n                );\n\n\n                ALTER TABLE public.idempotency_add_auto_field_app_relatedtesttable OWNER TO test;\n\n                --\n                -- Name: idempotency_add_auto_field_app_relatedtesttable_test_field_int_;\n                -- Type: SEQUENCE; Schema: public; Owner: test\n                --\n\n                CREATE SEQUENCE public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_\n                    START WITH 1\n                    INCREMENT BY 1\n                    NO MINVALUE\n                    NO MAXVALUE\n                    CACHE 1;\n\n\n                ALTER SEQUENCE public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_ OWNER TO test;\n\n                --\n                -- Name: idempotency_add_auto_field_app_relatedtesttable_test_field_int_;\n                -- Type: SEQUENCE OWNED BY; Schema: public; Owner: test\n                --\n\n                ALTER SEQUENCE public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_\n                OWNED BY public.idempotency_add_auto_field_app_relatedtesttable.test_field_int;\n\n\n                --\n                -- Name: idempotency_add_auto_field_app_relatedtesttable test_field_int;\n                -- Type: DEFAULT; Schema: public; Owner: test\n                --\n\n                ALTER TABLE ONLY public.idempotency_add_auto_field_app_relatedtesttable ALTER COLUMN test_field_int\n                SET DEFAULT nextval('public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_'::regclass);\n\n            \"\"\").replace(\"\\n-- Type\", \" Type\").replace(\"\\nOWNED\", \" OWNED\").replace(\"\\nSET\", \" SET\"),\n        )\n\n    def schema_compatible_explicit_type(dump: str) -> str:\n        return dump.replace(\n            textwrap.dedent(\"\"\"\n                CREATE SEQUENCE public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_\n                    AS integer\n                    START WITH 1\n                    INCREMENT BY 1\n                    NO MINVALUE\n                    NO MAXVALUE\n                    CACHE 1;\n            \"\"\"),\n            textwrap.dedent(\"\"\"\n                CREATE SEQUENCE public.idempotency_add_auto_field_app_relatedtesttable_test_field_int_\n                    START WITH 1\n                    INCREMENT BY 1\n                    NO MINVALUE\n                    NO MAXVALUE\n                    CACHE 1;\n            \"\"\"),\n        )\n\n    # migrate case 1\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == new_schema\n\n    # migrate case 2\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n        cursor.execute(_drop_sequence_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == new_schema\n\n    # migrate case 3\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n        cursor.execute(_drop_sequence_sql)\n        cursor.execute(_create_sequence_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == new_schema\n\n    # migrate case 4\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n        cursor.execute(_drop_sequence_sql)\n        cursor.execute(_create_sequence_sql)\n        cursor.execute(_set_default_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert schema_compatible_sequence_alter(\n        pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")\n    ) == new_schema\n\n    # migrate case 5\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n        cursor.execute(_drop_sequence_sql)\n        cursor.execute(_create_sequence_sql)\n        cursor.execute(_set_default_sql)\n        cursor.execute(_set_max_value_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert schema_compatible_sequence_alter(\n        pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")\n    ) == new_schema\n\n    # migrate case 6\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False):\n        call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_sql)\n        cursor.execute(_drop_sequence_sql)\n        cursor.execute(_create_sequence_sql)\n        cursor.execute(_set_default_sql)\n        cursor.execute(_set_max_value_sql)\n        cursor.execute(_set_owner_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    assert schema_compatible_explicit_type(pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\")) == new_schema\n\n    # rollback (covers drop auto field case)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False):\n        rollback_sql = call_command(\"sqlmigrate\", \"--backwards\", \"idempotency_add_auto_field_app\", \"0002\")\n    assert split_sql_queries(rollback_sql) == [\n        _set_type_for_rollback_sql,\n        _drop_sequence_sql,\n    ]\n\n    # rollback case 1\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_set_type_for_rollback_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == old_schema\n\n    # rollback case 2\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\")\n    with connection.cursor() as cursor:\n        cursor.execute(_drop_sequence_sql)\n    call_command(\"migrate\", \"idempotency_add_auto_field_app\", \"0001\")\n    assert pg_dump(\"idempotency_add_auto_field_app_relatedtesttable\") == old_schema\n"
  },
  {
    "path": "tests/settings.py",
    "content": "\"\"\"\nDjango settings for test project.\n\nGenerated by 'django-admin startproject' using Django 2.0.6.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/2.0/topics/settings/\n\nFor the full list of settings and their values, see\nhttps://docs.djangoproject.com/en/2.0/ref/settings/\n\"\"\"\n\nimport os\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = '56wnjyh@bo044_p7o_ckx37*^vv1^c0a5u(c1gt9!6t4+96$nm'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = True\n\nALLOWED_HOSTS = []\n\n\n# Application definition\n\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'django_zero_downtime_migrations',\n    'tests',\n]\n\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\nMIGRATION_MODULES = {\n    'admin': None,\n    'auth': None,\n    'contenttypes': None,\n    'sessions': None,\n    'messages': None,\n    'staticfiles': None,\n}\n\nTEMPLATES = [\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': [],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': [\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = 'tests2.wsgi.application'\n\n\n# Database\n# https://docs.djangoproject.com/en/2.0/ref/settings/#databases\n\nDATABASES = {\n    'default': {\n        # 'ENGINE': 'django.db.backends.postgresql',\n        'ENGINE': os.getenv('DB_ENGINE', 'django_zero_downtime_migrations.backends.postgres'),\n        'NAME': 'test',\n        'USER': os.getenv('DB_USER', 'postgres'),\n        'PASSWORD': os.getenv('DB_PASSWORD', os.getenv('DB_USER', 'postgres')),\n        'HOST': os.getenv('DB_HOST', 'localhost'),\n        'PORT': os.getenv('DB_PORT', '5432'),\n    },\n}\nDB_SUPER_USER = os.getenv('DB_SUPER_USER', DATABASES['default']['USER'])\nDB_SUPER_PASSWORD = os.getenv('DB_SUPER_PASSWORD', os.getenv('DB_SUPER_USER', DATABASES['default']['PASSWORD']))\n\n# Password validation\n# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators\n\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n    },\n]\n\n\n# Internationalization\n# https://docs.djangoproject.com/en/2.0/topics/i18n/\n\nLANGUAGE_CODE = 'en-us'\n\nTIME_ZONE = 'UTC'\n\nUSE_I18N = True\n\nUSE_TZ = True\n\n\n# Static files (CSS, JavaScript, Images)\n# https://docs.djangoproject.com/en/2.0/howto/static-files/\n\nSTATIC_URL = '/static/'\n"
  },
  {
    "path": "tests/settings_make_migrations.py",
    "content": "from .settings import *  # noqa: F401, F403\n\nINSTALLED_APPS += [  # noqa: F405\n    'tests.apps.good_flow_alter_table_with_same_db_table',\n    'tests.apps.good_flow_app',\n    'tests.apps.good_flow_app_concurrently',\n    'tests.apps.bad_rollback_flow_drop_column_with_notnull_default_app',\n    'tests.apps.bad_rollback_flow_drop_column_with_notnull_app',\n    'tests.apps.bad_rollback_flow_change_char_type_that_safe_app',\n    'tests.apps.bad_flow_add_column_with_default_app',\n    'tests.apps.bad_flow_add_column_with_notnull_default_app',\n    'tests.apps.bad_flow_add_column_with_notnull_app',\n    'tests.apps.bad_flow_change_char_type_that_unsafe_app',\n    'tests.apps.old_notnull_check_constraint_migration_app',\n    'tests.apps.good_flow_drop_table_with_constraints',\n    'tests.apps.good_flow_drop_column_with_constraints',\n    'tests.apps.idempotency_create_table_app',\n    'tests.apps.idempotency_add_column_app',\n    'tests.apps.idempotency_add_column_foreign_key_app',\n    'tests.apps.idempotency_add_column_one_to_one_app',\n    'tests.apps.idempotency_set_not_null_app',\n    'tests.apps.idempotency_add_check_app',\n    'tests.apps.idempotency_add_foreign_key_app',\n    'tests.apps.idempotency_add_one_to_one_app',\n    'tests.apps.idempotency_add_index_app',\n    'tests.apps.idempotency_add_index_meta_app',\n    'tests.apps.idempotency_add_unique_app',\n    'tests.apps.idempotency_add_unique_meta_app',\n    'tests.apps.idempotency_add_primary_key_app',\n    'tests.apps.idempotency_add_auto_field_app',\n]\n"
  },
  {
    "path": "tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/test_schema.py",
    "content": "from functools import partial\n\nimport django\nfrom django.conf import settings\nfrom django.contrib.postgres.constraints import ExclusionConstraint\nfrom django.contrib.postgres.indexes import (\n    BrinIndex, BTreeIndex, GinIndex, GistIndex, HashIndex, SpGistIndex\n)\nfrom django.db import connection, models\nfrom django.db.backends.postgresql.schema import (\n    DatabaseSchemaEditor as CoreDatabaseSchemaEditor\n)\nfrom django.test import override_settings\nfrom django.utils.module_loading import import_string\n\nimport pytest\n\nfrom django_zero_downtime_migrations.backends.postgres.schema import (\n    UnsafeOperationException, UnsafeOperationWarning\n)\n\nDatabaseSchemaEditor = import_string(settings.DATABASES['default']['ENGINE'] + '.schema.DatabaseSchemaEditor')\n\n\nSTART_TIMEOUTS = [\n    'SET statement_timeout TO \\'0\\';',\n    'SET lock_timeout TO \\'0\\';',\n]\nEND_TIMEOUTS = [\n    'SET statement_timeout TO \\'0ms\\';',\n    'SET lock_timeout TO \\'0ms\\';',\n]\nSTART_FLEXIBLE_STATEMENT_TIMEOUT = [\n    'SET statement_timeout TO \\'0ms\\';',\n]\nEND_FLEXIBLE_STATEMENT_TIMEOUT = [\n    'SET statement_timeout TO \\'0ms\\';',\n]\n\n\ndef timeouts(statements):\n    if isinstance(statements, str):\n        statements = [statements]\n    return START_TIMEOUTS + statements + END_TIMEOUTS\n\n\ndef flexible_statement_timeout(statements):\n    if isinstance(statements, str):\n        statements = [statements]\n    return START_FLEXIBLE_STATEMENT_TIMEOUT + statements + END_FLEXIBLE_STATEMENT_TIMEOUT\n\n\nclass Model(models.Model):\n    field1 = models.IntegerField()\n    field2 = models.IntegerField()\n\n\nclass Model2(models.Model):\n    pass\n\n\nschema_editor = partial(DatabaseSchemaEditor, connection=connection, collect_sql=True)\n\n\nclass cmp_schema_editor:\n    schema_editor = DatabaseSchemaEditor\n    core_schema_editor = CoreDatabaseSchemaEditor\n\n    def __enter__(self):\n        self.editor = self.schema_editor(connection=connection, collect_sql=True).__enter__()\n        self.core_editor = self.core_schema_editor(connection=connection, collect_sql=True, atomic=False).__enter__()\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.core_editor.__exit__(exc_type, exc_value, traceback)\n        self.editor.__exit__(exc_type, exc_value, traceback)\n\n    def __getattr__(self, item):\n        self.method = item\n        value = getattr(self.editor, self.method)\n        if callable(value):\n            return self\n        return value\n\n    def __call__(self, *args, **kwargs):\n        getattr(self.core_editor, self.method)(*args, **kwargs)\n        return getattr(self.editor, self.method)(*args, **kwargs)\n\n    @property\n    def django_sql(self):\n        return self.core_editor.collected_sql\n\n\n@pytest.fixture(autouse=True)\ndef zero_timeouts():\n    with override_settings(ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT=0):\n        with override_settings(ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT=0):\n            yield\n\n\n@pytest.fixture(autouse=True)\ndef cursor(mocker):\n    with connection.cursor() as cursor:\n        mocker.patch.object(connection, 'cursor')().__enter__.return_value = cursor\n        yield cursor\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_create_model__ok():\n    with cmp_schema_editor() as editor:\n        editor.create_model(Model)\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == [\n        'CREATE TABLE \"tests_model\" '\n        '(\"id\" integer NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, '\n        '\"field1\" integer NOT NULL, \"field2\" integer NOT NULL);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_model__ok():\n    with cmp_schema_editor() as editor:\n        editor.delete_model(Model)\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == [\n        'DROP TABLE \"tests_model\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_model__drop_foreign_key__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').return_value = [\n        ('tests_model_model2_id_fk', 'f', 'tests_model', 'tests_model2', ['model2_id'], ['id'])\n    ]\n    with cmp_schema_editor() as editor:\n        editor.delete_model(Model)\n    assert editor.collected_sql == timeouts(\n        'SET CONSTRAINTS \"tests_model_model2_id_fk\" IMMEDIATE; '\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_model2_id_fk\";',\n    ) + [\n       'DROP TABLE \"tests_model\" CASCADE;'\n    ]\n    assert editor.django_sql == [\n        'DROP TABLE \"tests_model\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_model__drop_foreign_key_backref__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').return_value = [\n        ('tests_model2_model_id_fk', 'f', 'tests_model2', 'tests_model', ['model_id'], ['id'])\n    ]\n    with cmp_schema_editor() as editor:\n        editor.delete_model(Model)\n    assert editor.collected_sql == timeouts(\n        'SET CONSTRAINTS \"tests_model2_model_id_fk\" IMMEDIATE; '\n        'ALTER TABLE \"tests_model2\" DROP CONSTRAINT \"tests_model2_model_id_fk\";',\n    ) + [\n        'DROP TABLE \"tests_model\" CASCADE;'\n    ]\n    assert editor.django_sql == [\n        'DROP TABLE \"tests_model\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\ndef test_rename_model__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER TABLE RENAME is unsafe operation'):\n            editor.alter_db_table(Model, 'old_name', 'new_name')\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"old_name\" RENAME TO \"new_name\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_rename_model__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER TABLE RENAME is unsafe operation'):\n            editor.alter_db_table(Model, 'old_name', 'new_name')\n    assert editor.django_sql == [\n        'ALTER TABLE \"old_name\" RENAME TO \"new_name\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_rename_model_with_same_db_table__ok():\n    with cmp_schema_editor() as editor:\n        editor.alter_db_table(Model, 'same_table', 'same_table')\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == []\n\n\n@pytest.mark.django_db\ndef test_change_model_tablespace__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER TABLE SET TABLESPACE is unsafe operation'):\n            editor.alter_db_tablespace(Model, 'old_tablespace', 'new_tablespace')\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" SET TABLESPACE \"new_tablespace\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_change_model_tablespace__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER TABLE SET TABLESPACE is unsafe operation'):\n            editor.alter_db_tablespace(Model, 'old_tablespace', 'new_tablespace')\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" SET TABLESPACE \"new_tablespace\";',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (4, 2), reason='functionality provided in django 4.2')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_change_model_comment__ok():\n    with cmp_schema_editor() as editor:\n        editor.alter_db_table_comment(Model, 'old_comment', 'new_comment')\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == ['COMMENT ON TABLE \"tests_model\" IS \\'new_comment\\';']\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, null=True)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL;'\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_code_default_null__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, default='test', null=True)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NULL;',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NULL;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    ]\n\n\n@pytest.mark.django_db\ndef test_add_field_with_code_default_not_null__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ADD COLUMN NOT NULL is unsafe operation'):\n            field = models.CharField(max_length=40, default='test', null=False)\n            field.set_attributes_from_name('field')\n            editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;'\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;'\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_code_default_not_null__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ADD COLUMN NOT NULL is unsafe operation'):\n            field = models.CharField(max_length=40, default='test', null=False)\n            field.set_attributes_from_name('field')\n            editor.add_field(Model, field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] >= (5, 0), reason='setting deprecated for django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT=True)\ndef test_add_field_with_code_default_not_null__keep_default__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, default='test', null=False)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;'\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='setting deprecated in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT=True)\ndef test_add_field_with_code_default_not_null__keep_default__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ADD COLUMN NOT NULL is unsafe operation'):\n            field = models.CharField(max_length=40, default='test', null=False)\n            field.set_attributes_from_name('field')\n            editor.add_field(Model, field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_db_default_null__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, db_default='test', null=True)\n        field.set_attributes_from_name('field')\n        field.model = Model\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_db_default_not_null__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, db_default='test', null=False)\n        field.set_attributes_from_name('field')\n        field.model = Model\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_code_default_db_default_not_null__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, db_default='test', default='test2', null=False)\n        field.set_attributes_from_name('field')\n        field.model = Model\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) DEFAULT \\'test\\' NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\ndef test_add_field_with_not_null__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ADD COLUMN NOT NULL is unsafe operation'):\n            field = models.CharField(max_length=40, null=False)\n            field.set_attributes_from_name('field')\n            editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_field_with_not_null__with_flexible_timeout__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ADD COLUMN NOT NULL is unsafe operation'):\n            field = models.CharField(max_length=40, null=False)\n            field.set_attributes_from_name('field')\n            editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_not_null__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ADD COLUMN NOT NULL is unsafe operation'):\n            field = models.CharField(max_length=40, null=False)\n            field.set_attributes_from_name('field')\n            editor.add_field(Model, field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_foreign_key__ok():\n    with cmp_schema_editor() as editor:\n        field = models.ForeignKey(Model2, null=True, on_delete=models.CASCADE)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field_id\" integer NULL;',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'FOREIGN KEY (\"field_id\") REFERENCES \"tests_model2\" (\"id\") DEFERRABLE INITIALLY DEFERRED NOT VALID;',\n    ) + [\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\";',\n    ] + [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field_id\" integer NULL '\n        'CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'REFERENCES \"tests_model2\"(\"id\") DEFERRABLE INITIALLY DEFERRED; '\n        'SET CONSTRAINTS \"tests_model_field_id_0166400c_fk_tests_model2_id\" IMMEDIATE;',\n        'CREATE INDEX \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_field_with_foreign_key__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        field = models.ForeignKey(Model2, null=True, on_delete=models.CASCADE)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field_id\" integer NULL;',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'FOREIGN KEY (\"field_id\") REFERENCES \"tests_model2\" (\"id\") DEFERRABLE INITIALLY DEFERRED NOT VALID;',\n    ) + flexible_statement_timeout(\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\";',\n    ) + flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field_id\" integer NULL '\n        'CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'REFERENCES \"tests_model2\"(\"id\") DEFERRABLE INITIALLY DEFERRED; '\n        'SET CONSTRAINTS \"tests_model_field_id_0166400c_fk_tests_model2_id\" IMMEDIATE;',\n        'CREATE INDEX \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_primary_key__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, null=True, primary_key=True)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL;',\n    ) + [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_pk\" ON \"tests_model\" (\"field\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_pk\" '\n        'PRIMARY KEY USING INDEX \"tests_model_field_0a53d95f_pk\";',\n    ) + [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL PRIMARY KEY;',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_field_with_primary_key__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, null=True, primary_key=True)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL;',\n    ) + flexible_statement_timeout(\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_pk\" ON \"tests_model\" (\"field\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_pk\" '\n        'PRIMARY KEY USING INDEX \"tests_model_field_0a53d95f_pk\";',\n    ) + flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL PRIMARY KEY;',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_field_with_unique__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, null=True, unique=True)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL;',\n    ) + [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_uniq\" ON \"tests_model\" (\"field\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_uniq\" '\n        'UNIQUE USING INDEX \"tests_model_field_0a53d95f_uniq\";',\n    ) + [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" '\n        'ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL UNIQUE;',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_field_with_unique__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40, null=True, unique=True)\n        field.set_attributes_from_name('field')\n        editor.add_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL;',\n    ) + flexible_statement_timeout(\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_uniq\" ON \"tests_model\" (\"field\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_uniq\" '\n        'UNIQUE USING INDEX \"tests_model_field_0a53d95f_uniq\";',\n    ) + flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" '\n        'ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD COLUMN \"field\" varchar(40) NULL UNIQUE;',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_integer_to_integer_identity__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.IntegerField(primary_key=True)\n        old_field.set_attributes_from_name('field')\n        old_field.model = Model\n        new_field = models.AutoField(primary_key=True)\n        new_field.set_attributes_from_name('field')\n        new_field.model = Model\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE integer;',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" ADD GENERATED BY DEFAULT AS IDENTITY;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE integer;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" ADD GENERATED BY DEFAULT AS IDENTITY;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_integer_identity_to_integer__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.AutoField(primary_key=True)\n        old_field.set_attributes_from_name('field')\n        old_field.model = Model\n        new_field = models.IntegerField(primary_key=True)\n        new_field.set_attributes_from_name('field')\n        new_field.model = Model\n        editor.alter_field(Model, old_field, new_field)\n    if django.VERSION[:2] >= (4, 1):\n        assert editor.collected_sql == timeouts(\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP IDENTITY IF EXISTS;',\n        ) + timeouts(\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE integer;',\n        )\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP IDENTITY IF EXISTS;',\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE integer;',\n        ]\n    else:\n        assert editor.collected_sql == timeouts(\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE integer USING \"field\"::integer;',\n        ) + timeouts(\n            'DROP SEQUENCE IF EXISTS \"tests_model_field_seq\" CASCADE;',\n        )\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE integer USING \"field\"::integer;',\n            'DROP SEQUENCE IF EXISTS \"tests_model_field_seq\" CASCADE;',\n        ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_integer_to_bigint_identity__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.IntegerField(primary_key=True)\n            old_field.set_attributes_from_name('field')\n            old_field.model = Model\n            new_field = models.BigAutoField(primary_key=True)\n            new_field.set_attributes_from_name('field')\n            new_field.model = Model\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" ADD GENERATED BY DEFAULT AS IDENTITY;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" ADD GENERATED BY DEFAULT AS IDENTITY;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_integer_to_bigint_identity__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.IntegerField(primary_key=True)\n            old_field.set_attributes_from_name('field')\n            old_field.model = Model\n            new_field = models.BigAutoField(primary_key=True)\n            new_field.set_attributes_from_name('field')\n            new_field.model = Model\n            editor.alter_field(Model, old_field, new_field)\n    if django.VERSION[:2] >= (4, 1):\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" ADD GENERATED BY DEFAULT AS IDENTITY;'\n        ]\n    else:\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n            'DROP SEQUENCE IF EXISTS \"tests_model_field_seq\" CASCADE;',\n            'CREATE SEQUENCE \"tests_model_field_seq\";',\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET DEFAULT nextval(\\'\"tests_model_field_seq\"\\');',\n            'SELECT setval(\\'\"tests_model_field_seq\"\\', MAX(\"field\")) FROM \"tests_model\";',\n            'ALTER SEQUENCE \"tests_model_field_seq\" OWNED BY \"tests_model\".\"field\";',\n        ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_integer_identity_to_bigint__warning(mocker):\n    mocker.patch.object(connection.introspection, 'get_sequences').return_value = [{\n        'column': 'field',\n        'name': 'field_seq',\n        'table': 'tests_model',\n    }]\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.AutoField(primary_key=True)\n            old_field.set_attributes_from_name('field')\n            old_field.model = Model\n            new_field = models.BigIntegerField(primary_key=True)\n            new_field.set_attributes_from_name('field')\n            new_field.model = Model\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP IDENTITY IF EXISTS;',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n    ) + timeouts(\n        'DROP SEQUENCE IF EXISTS \"field_seq\" CASCADE;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP IDENTITY IF EXISTS;',\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n        'DROP SEQUENCE IF EXISTS \"field_seq\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_integer_identity_to_bigint__raise(mocker):\n    mocker.patch.object(connection.introspection, 'get_sequences').return_value = [{\n        'column': 'field',\n        'name': 'field_seq',\n        'table': 'tests_model',\n    }]\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.AutoField(primary_key=True)\n            old_field.set_attributes_from_name('field')\n            old_field.model = Model\n            new_field = models.BigIntegerField(primary_key=True)\n            new_field.set_attributes_from_name('field')\n            new_field.model = Model\n            editor.alter_field(Model, old_field, new_field)\n    if django.VERSION[:2] >= (4, 1):\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP IDENTITY IF EXISTS;',\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n            'DROP SEQUENCE IF EXISTS \"field_seq\" CASCADE;',\n        ]\n    else:\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n            'DROP SEQUENCE IF EXISTS \"tests_model_field_seq\" CASCADE;',\n        ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_integer_identity_to_bigint_identity__warning(mocker):\n    mocker.patch.object(connection.introspection, 'get_sequences').return_value = [{\n        'column': 'field',\n        'name': 'field_seq',\n        'table': 'tests_model',\n    }]\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.AutoField(primary_key=True)\n            old_field.set_attributes_from_name('field')\n            old_field.model = Model\n            new_field = models.BigAutoField(primary_key=True)\n            new_field.set_attributes_from_name('field')\n            new_field.model = Model\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n    ) + timeouts(\n        'ALTER SEQUENCE IF EXISTS \"field_seq\" AS bigint;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n        'ALTER SEQUENCE IF EXISTS \"field_seq\" AS bigint;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_integer_identity_to_bigint_identity__raise(mocker):\n    mocker.patch.object(connection.introspection, 'get_sequences').return_value = [{\n        'column': 'field',\n        'name': 'field_seq',\n        'table': 'tests_model',\n    }]\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.AutoField(primary_key=True)\n            old_field.set_attributes_from_name('field')\n            old_field.model = Model\n            new_field = models.BigAutoField(primary_key=True)\n            new_field.set_attributes_from_name('field')\n            new_field.model = Model\n            editor.alter_field(Model, old_field, new_field)\n    if django.VERSION[:2] >= (4, 1):\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n            'ALTER SEQUENCE IF EXISTS \"field_seq\" AS bigint;',\n        ]\n    else:\n        assert editor.django_sql == [\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE bigint USING \"field\"::bigint;',\n            'DROP SEQUENCE IF EXISTS \"tests_model_field_seq\" CASCADE;',\n            'CREATE SEQUENCE \"tests_model_field_seq\";',\n            'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET DEFAULT nextval(\\'\"tests_model_field_seq\"\\');',\n            'SELECT setval(\\'\"tests_model_field_seq\"\\', MAX(\"field\")) FROM \"tests_model\";',\n            'ALTER SEQUENCE \"tests_model_field_seq\" OWNED BY \"tests_model\".\"field\";',\n        ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_varchar40_to_varchar20__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.CharField(max_length=40)\n            old_field.set_attributes_from_name('field')\n            new_field = models.CharField(max_length=20)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE varchar(20);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_varchar40_to_varchar20_error():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.CharField(max_length=40)\n            old_field.set_attributes_from_name('field')\n            new_field = models.CharField(max_length=20)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE varchar(20);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_varchar40_to_varchar80__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=80)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE varchar(80);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_varchar40_to_text__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.TextField()\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE text USING \"field\"::text;',\n    ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_decimal10_2_to_decimal5_2__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.DecimalField(max_digits=10, decimal_places=2)\n            old_field.set_attributes_from_name('field')\n            new_field = models.DecimalField(max_digits=5, decimal_places=2)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(5, 2);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_decimal10_2_to_decimal5_2__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.DecimalField(max_digits=10, decimal_places=2)\n            old_field.set_attributes_from_name('field')\n            new_field = models.DecimalField(max_digits=5, decimal_places=2)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(5, 2);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_decimal10_2_to_decimal20_2__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.DecimalField(max_digits=10, decimal_places=2)\n        old_field.set_attributes_from_name('field')\n        new_field = models.DecimalField(max_digits=20, decimal_places=2)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(20, 2);',\n    ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_decimal10_2_to_decimal10_3__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.DecimalField(max_digits=10, decimal_places=2)\n            old_field.set_attributes_from_name('field')\n            new_field = models.DecimalField(max_digits=10, decimal_places=3)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(10, 3);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_decimal10_2_to_decimal10_3__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.DecimalField(max_digits=10, decimal_places=2)\n            old_field.set_attributes_from_name('field')\n            new_field = models.DecimalField(max_digits=10, decimal_places=3)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(10, 3);',\n    ]\n\n\n@pytest.mark.django_db\ndef test_alter_field_type_decimal10_2_to_decimal10_1__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.DecimalField(max_digits=10, decimal_places=2)\n            old_field.set_attributes_from_name('field')\n            new_field = models.DecimalField(max_digits=10, decimal_places=1)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(10, 1);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_type_decimal10_2_to_decimal10_1__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER COLUMN TYPE is unsafe operation'):\n            old_field = models.DecimalField(max_digits=10, decimal_places=2)\n            old_field.set_attributes_from_name('field')\n            new_field = models.DecimalField(max_digits=10, decimal_places=1)\n            new_field.set_attributes_from_name('field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" TYPE numeric(10, 1);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_set_not_null__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, null=True)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, null=False)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_notnull\" '\n        'CHECK (\"field\" IS NOT NULL) NOT VALID;',\n    ) + [\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_0a53d95f_notnull\";',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET NOT NULL;'\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_notnull\";'\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_alter_field_set_not_null__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, null=True)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, null=False)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_notnull\" '\n        'CHECK (\"field\" IS NOT NULL) NOT VALID;',\n    ) + flexible_statement_timeout(\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_0a53d95f_notnull\";',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET NOT NULL;'\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_notnull\";'\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_filed_drop_not_null__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchone').return_value = None\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, null=False)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, null=True)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP NOT NULL;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_set_code_default__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, default='test')\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    # no sql executed because django doesn't use database defaults\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == []\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_code_default__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, default='test')\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    # no sql executed because django doesn't use database defaults\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == []\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_set_db_default__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        old_field.model = Model\n        new_field = models.CharField(max_length=40, db_default='test')\n        new_field.set_attributes_from_name('field')\n        new_field.model = Model\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" SET DEFAULT \\'test\\';',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_db_default__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, db_default='test')\n        old_field.set_attributes_from_name('field')\n        old_field.model = Model\n        new_field = models.CharField(max_length=40)\n        new_field.set_attributes_from_name('field')\n        new_field.model = Model\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ALTER COLUMN \"field\" DROP DEFAULT;',\n    ]\n\n\n@pytest.mark.django_db\ndef test_rename_field__warning():\n    with cmp_schema_editor() as editor:\n        with pytest.warns(UnsafeOperationWarning, match='ALTER TABLE RENAME COLUMN is unsafe operation'):\n            old_field = models.CharField(max_length=40)\n            old_field.set_attributes_from_name('old_field')\n            new_field = models.CharField(max_length=40)\n            new_field.set_attributes_from_name('new_field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" RENAME COLUMN \"old_field\" TO \"new_field\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_rename_field__raise():\n    with cmp_schema_editor() as editor:\n        with pytest.raises(UnsafeOperationException, match='ALTER TABLE RENAME COLUMN is unsafe operation'):\n            old_field = models.CharField(max_length=40)\n            old_field.set_attributes_from_name('old_field')\n            new_field = models.CharField(max_length=40)\n            new_field.set_attributes_from_name('new_field')\n            editor.alter_field(Model, old_field, new_field)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" RENAME COLUMN \"old_field\" TO \"new_field\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_field__ok():\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40)\n        field.set_attributes_from_name('field')\n        editor.remove_field(Model, field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_field_with_foreign_key__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').side_effect = [\n        [\n            ('tests_model_field_fk', 'f', 'tests_model', 'tests_model2', ['field'], ['id']),\n            ('tests_model_field2_fk', 'f', 'tests_model', 'tests_model2', ['field2'], ['id']),\n        ],\n        [\n            ('tests_model_field2_fk', 'f', 'tests_model', 'tests_model2', ['field2'], ['id']),\n        ],\n        [],\n    ]\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40)\n        field.set_attributes_from_name('field')\n        editor.remove_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'SET CONSTRAINTS \"tests_model_field_fk\" IMMEDIATE; '\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_fk\";'\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;'\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_field_with_foreign_key_backref__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').side_effect = [\n        [\n            ('tests_model2_model_field_fk', 'f', 'tests_model2', 'tests_model', ['model_field'], ['field']),\n            ('tests_model2_model_field2_fk', 'f', 'tests_model2', 'tests_model', ['model_field2'], ['field2']),\n        ],\n        [\n            ('tests_model2_model_field2_fk', 'f', 'tests_model2', 'tests_model', ['model_field2'], ['field2']),\n        ],\n        [],\n    ]\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40)\n        field.set_attributes_from_name('field')\n        editor.remove_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'SET CONSTRAINTS \"tests_model2_model_field_fk\" IMMEDIATE; '\n        'ALTER TABLE \"tests_model2\" DROP CONSTRAINT \"tests_model2_model_field_fk\";'\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;'\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_field_with_unique_constraint__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').side_effect = [\n        [\n            ('tests_model_field2_field_uniq', 'u', 'tests_model', None, ['field2', 'field'], []),\n            ('tests_model_field2_field3_uniq', 'u', 'tests_model', None, ['field2', 'field3'], []),\n        ],\n        [\n            ('tests_model_field2_field_uniq', 'u', 'tests_model', None, ['field2', 'field'], []),\n            ('tests_model_field2_field3_uniq', 'u', 'tests_model', None, ['field2', 'field3'], []),\n        ],\n        [],\n    ]\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40)\n        field.set_attributes_from_name('field')\n        editor.remove_field(Model, field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field2_field_uniq\";',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_field_with_index__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').side_effect = [\n        [],\n        [],\n        [\n            ('tests_model_field2_field_idx', 'tests_model', ['field2', 'field']),\n            ('tests_model_field2_field3_idx', 'tests_model', ['field2', 'field3']),\n        ],\n    ]\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40)\n        field.set_attributes_from_name('field')\n        editor.remove_field(Model, field)\n    assert editor.collected_sql == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field2_field_idx\";',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_remove_field_with_index__with_flexible_timeout__ok(cursor, mocker):\n    mocker.patch.object(cursor, 'execute')\n    mocker.patch.object(cursor, 'fetchall').side_effect = [\n        [],\n        [],\n        [\n            ('tests_model_field2_field_idx', 'tests_model', ['field2', 'field']),\n            ('tests_model_field2_field3_idx', 'tests_model', ['field2', 'field3']),\n        ],\n    ]\n    with cmp_schema_editor() as editor:\n        field = models.CharField(max_length=40)\n        field.set_attributes_from_name('field')\n        editor.remove_field(Model, field)\n    assert editor.collected_sql == flexible_statement_timeout(\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field2_field_idx\";',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP COLUMN \"field\" CASCADE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_add_constraint_check__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.IntegerField()\n        old_field.set_attributes_from_name('field')\n        new_field = models.PositiveIntegerField()\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_check\" '\n        'CHECK (\"field\" >= 0) NOT VALID;',\n    ) + [\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_0a53d95f_check\";',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_check\" CHECK (\"field\" >= 0);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_alter_field_add_constraint_check__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.IntegerField()\n        old_field.set_attributes_from_name('field')\n        new_field = models.PositiveIntegerField()\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_check\" '\n        'CHECK (\"field\" >= 0) NOT VALID;',\n    ) + flexible_statement_timeout(\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_0a53d95f_check\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_check\" CHECK (\"field\" >= 0);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_constraint_check__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_0a53d95f_check': {\n            'columns': ['field'],\n            'primary_key': False,\n            'unique': False,\n            'foreign_key': None,\n            'check': True,\n            'index': False,\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        old_field = models.PositiveIntegerField()\n        old_field.set_attributes_from_name('field')\n        new_field = models.IntegerField()\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_check\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_filed_add_constraint_foreign_key__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.IntegerField()\n        old_field.set_attributes_from_name('field_id')\n        new_field = models.ForeignKey(Model2, on_delete=models.CASCADE)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'FOREIGN KEY (\"field_id\") REFERENCES \"tests_model2\" (\"id\") DEFERRABLE INITIALLY DEFERRED NOT VALID;',\n    ) + [\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\";',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'FOREIGN KEY (\"field_id\") REFERENCES \"tests_model2\" (\"id\") DEFERRABLE INITIALLY DEFERRED;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_alter_filed_add_constraint_foreign_key__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.IntegerField()\n        old_field.set_attributes_from_name('field_id')\n        new_field = models.ForeignKey(Model2, on_delete=models.CASCADE)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'FOREIGN KEY (\"field_id\") REFERENCES \"tests_model2\" (\"id\") DEFERRABLE INITIALLY DEFERRED NOT VALID;',\n    ) + flexible_statement_timeout(\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\";',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field_id_0166400c\" ON \"tests_model\" (\"field_id\");',\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_id_0166400c_fk_tests_model2_id\" '\n        'FOREIGN KEY (\"field_id\") REFERENCES \"tests_model2\" (\"id\") DEFERRABLE INITIALLY DEFERRED;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_constraint_foreign_key__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_0a53d95f_fk': {\n            'columns': ['field_id'],\n            'primary_key': False,\n            'unique': False,\n            'foreign_key': (Model2._meta.db_table, 'id'),\n            'check': False,\n            'index': False,\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        old_field = models.ForeignKey(Model2, on_delete=models.CASCADE)\n        old_field.set_attributes_from_name('field')\n        new_field = models.IntegerField()\n        new_field.set_attributes_from_name('field_id')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'SET CONSTRAINTS \"tests_model_field_0a53d95f_fk\" IMMEDIATE; '\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_fk\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_add_constraint_primary_key__ok(mocker):\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, unique=True)\n        old_field.set_attributes_from_name('field')\n        old_field.model = Model\n        new_field = models.CharField(max_length=40, primary_key=True)\n        new_field.set_attributes_from_name('field')\n        new_field.model = Model\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_pk\" ON \"tests_model\" (\"field\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_pk\" '\n        'PRIMARY KEY USING INDEX \"tests_model_field_0a53d95f_pk\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_pk\" PRIMARY KEY (\"field\");',\n    ]\n\n\n@pytest.mark.skipif(django.VERSION < (5, 2), reason='Composite PK were added in Django 5.2')\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_composite_primary_key_field__ok(mocker):\n    with cmp_schema_editor() as editor:\n        field_a = models.IntegerField(unique=True)\n        field_a.set_attributes_from_name('field')\n        field_a.model = Model\n        field_b = models.IntegerField(unique=True)\n        field_b.set_attributes_from_name('field')\n        field_b.model = Model\n        composite_pk = models.CompositePrimaryKey(\"product_id\", \"order_id\")\n        composite_pk.set_attributes_from_name('field')\n        composite_pk.model = Model\n        editor.add_field(Model, composite_pk)\n    # Django does not support migrating to a composite primary key:\n    # https://docs.djangoproject.com/en/5.2/topics/composite-primary-key/#migrating-to-a-composite-primary-key\n    assert editor.collected_sql == []\n    assert editor.django_sql == []\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_alter_field_add_constraint_primary_key__with_flexible_timeout__ok(mocker):\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, unique=True)\n        old_field.set_attributes_from_name('field')\n        old_field.model = Model\n        new_field = models.CharField(max_length=40, primary_key=True)\n        new_field.set_attributes_from_name('field')\n        new_field.model = Model\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_pk\" ON \"tests_model\" (\"field\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_pk\" '\n        'PRIMARY KEY USING INDEX \"tests_model_field_0a53d95f_pk\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_pk\" PRIMARY KEY (\"field\");',\n    ]\n\n\n@pytest.mark.skipif(django.VERSION < (5, 2), reason='Composite PK were added in Django 5.2')\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_composite_primary_key_field__with_flexible_timeout__ok(mocker):\n    with cmp_schema_editor() as editor:\n        field_a = models.IntegerField(unique=True)\n        field_a.set_attributes_from_name('field')\n        field_a.model = Model\n        field_b = models.IntegerField(unique=True)\n        field_b.set_attributes_from_name('field')\n        field_b.model = Model\n        composite_pk = models.CompositePrimaryKey(\"product_id\", \"order_id\")\n        composite_pk.set_attributes_from_name('field')\n        composite_pk.model = Model\n        editor.add_field(Model, composite_pk)\n    # Django does not support migrating to a composite primary key:\n    # https://docs.djangoproject.com/en/5.2/topics/composite-primary-key/#migrating-to-a-composite-primary-key\n    assert editor.collected_sql == []\n    assert editor.django_sql == []\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_constraint_primary_key__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_0a53d95f_pk': {\n            'columns': ['field'],\n            'primary_key': True,\n            'unique': True,\n            'foreign_key': None,\n            'check': False,\n            'index': False,\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, primary_key=True)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_pk\";',\n    ) + [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field_0a53d95f_like\";',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_pk\";',\n        'DROP INDEX IF EXISTS \"tests_model_field_0a53d95f_like\";',\n    ]\n\n\n@pytest.mark.skipif(django.VERSION < (5, 2), reason='Composite PK were added in Django 5.2')\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_composite_primary_key_field__ok(mocker):\n    with cmp_schema_editor() as editor:\n        field_a = models.IntegerField(unique=True)\n        field_a.set_attributes_from_name('field')\n        field_a.model = Model\n        field_b = models.IntegerField(unique=True)\n        field_b.set_attributes_from_name('field')\n        field_b.model = Model\n        composite_pk = models.CompositePrimaryKey(\"product_id\", \"order_id\")\n        composite_pk.set_attributes_from_name('field')\n        composite_pk.model = Model\n        editor.remove_field(Model, composite_pk)\n    # Django does not support migrating from a composite primary key:\n    # https://docs.djangoproject.com/en/5.2/topics/composite-primary-key/#migrating-to-a-composite-primary-key\n    assert editor.collected_sql == []\n    assert editor.django_sql == []\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_add_constraint_unique__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, unique=True)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_uniq\" ON \"tests_model\" (\"field\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_uniq\" '\n        'UNIQUE USING INDEX \"tests_model_field_0a53d95f_uniq\";',\n    ) + [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" '\n        'ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_uniq\" UNIQUE (\"field\");',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_alter_field_add_constraint_unique__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, unique=True)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_uniq\" ON \"tests_model\" (\"field\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_uniq\" '\n        'UNIQUE USING INDEX \"tests_model_field_0a53d95f_uniq\";',\n    ) + flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" '\n        'ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field_0a53d95f_uniq\" UNIQUE (\"field\");',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_alter_field_drop_constraint_unique__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_0a53d95f_uniq': {\n            'columns': ['field'],\n            'primary_key': False,\n            'unique': True,\n            'foreign_key': None,\n            'check': False,\n            'index': False,\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, unique=True)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_uniq\";',\n    ) + [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field_0a53d95f_like\";',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_0a53d95f_uniq\";',\n        'DROP INDEX IF EXISTS \"tests_model_field_0a53d95f_like\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_index__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, db_index=True)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f\" ON \"tests_model\" (\"field\");',\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field_0a53d95f\" ON \"tests_model\" (\"field\");',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40, db_index=True)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f\" ON \"tests_model\" (\"field\");',\n    ) + flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field_0a53d95f\" ON \"tests_model\" (\"field\");',\n        'CREATE INDEX \"tests_model_field_0a53d95f_like\" ON \"tests_model\" (\"field\" varchar_pattern_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_index__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_idx': {\n            'columns': ['field'],\n            'orders': ['ASC'],\n            'primary_key': False,\n            'unique': False,\n            'foreign_key': None,\n            'check': False,\n            'index': True,\n            'type': 'idx',\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        old_field = models.CharField(max_length=40, db_index=True)\n        old_field.set_attributes_from_name('field')\n        new_field = models.CharField(max_length=40)\n        new_field.set_attributes_from_name('field')\n        editor.alter_field(Model, old_field, new_field)\n    assert editor.collected_sql == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field_idx\";',\n    ]\n    assert editor.django_sql == [\n        'DROP INDEX IF EXISTS \"tests_model_field_idx\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_unique_together__ok(mocker):\n    with cmp_schema_editor() as editor:\n        editor.alter_unique_together(Model, [], [['field1', 'field2']])\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field1_field2_51878e08_uniq\" '\n        'ON \"tests_model\" (\"field1\", \"field2\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field1_field2_51878e08_uniq\" '\n        'UNIQUE USING INDEX \"tests_model_field1_field2_51878e08_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field1_field2_51878e08_uniq\" '\n        'UNIQUE (\"field1\", \"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_unique_together__with_flexible_timeout__ok(mocker):\n    with cmp_schema_editor() as editor:\n        editor.alter_unique_together(Model, [], [['field1', 'field2']])\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE UNIQUE INDEX CONCURRENTLY \"tests_model_field1_field2_51878e08_uniq\" '\n        'ON \"tests_model\" (\"field1\", \"field2\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field1_field2_51878e08_uniq\" '\n        'UNIQUE USING INDEX \"tests_model_field1_field2_51878e08_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"tests_model_field1_field2_51878e08_uniq\" '\n        'UNIQUE (\"field1\", \"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_unique_together__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_idx': {\n            'columns': ['field1', 'field2'],\n            'primary_key': False,\n            'unique': True,\n            'foreign_key': None,\n            'check': False,\n            'index': False,\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        editor.alter_unique_together(Model, [['field1', 'field2']], [])\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"tests_model_field_idx\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_index_together__ok(mocker):\n    with cmp_schema_editor() as editor:\n        editor.alter_index_together(Model, [], [['field1', 'field2']])\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_field2_51878e08_idx\" '\n        'ON \"tests_model\" (\"field1\", \"field2\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_field2_51878e08_idx\" ON \"tests_model\" (\"field1\", \"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_index_together__with_flexible_timeout__ok(mocker):\n    with cmp_schema_editor() as editor:\n        editor.alter_index_together(Model, [], [['field1', 'field2']])\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_field2_51878e08_idx\" '\n        'ON \"tests_model\" (\"field1\", \"field2\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_field2_51878e08_idx\" ON \"tests_model\" (\"field1\", \"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_remove_index_together__ok(mocker):\n    mocker.patch.object(connection.introspection, 'get_constraints').return_value = {\n        'tests_model_field_idx': {\n            'columns': ['field1', 'field2'],\n            'orders': ['ASC', 'ASC'],\n            'primary_key': False,\n            'unique': False,\n            'foreign_key': None,\n            'check': False,\n            'index': True,\n            'type': 'idx',\n            'definition': None,\n            'options': None,\n        }\n    }\n    with cmp_schema_editor() as editor:\n        editor.alter_index_together(Model, [['field1', 'field2']], [])\n    assert editor.collected_sql == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field_idx\";',\n    ]\n    assert editor.django_sql == [\n        'DROP INDEX IF EXISTS \"tests_model_field_idx\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_check_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.CheckConstraint(check=models.Q(field1__gt=0), name='field1_gt_0'))\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_gt_0\" '\n        'CHECK (\"field1\" > 0) NOT VALID;',\n    ) + [\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"field1_gt_0\";',\n    ]\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_gt_0\" CHECK (\"field1\" > 0);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_check_constraint__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.CheckConstraint(check=models.Q(field1__gt=0), name='field1_gt_0'))\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_gt_0\" '\n        'CHECK (\"field1\" > 0) NOT VALID;',\n    ) + flexible_statement_timeout(\n        'ALTER TABLE \"tests_model\" VALIDATE CONSTRAINT \"field1_gt_0\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_gt_0\" CHECK (\"field1\" > 0);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_meta_check_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.remove_constraint(Model, models.CheckConstraint(check=models.Q(field1__gt=0), name='field1_gt_0'))\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"field1_gt_0\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"field1_gt_0\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.UniqueConstraint(fields=('field1',), name='field1_uniq'))\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" '\n        'UNIQUE USING INDEX \"field1_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" UNIQUE (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_unique_constraint__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.UniqueConstraint(fields=('field1',), name='field1_uniq'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\");',\n    ) + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" '\n        'UNIQUE USING INDEX \"field1_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" UNIQUE (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_multicolumn_unique_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.UniqueConstraint(fields=('field1', 'field2'), name='field1_field2_uniq'))\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_field2_uniq\" ON \"tests_model\" (\"field1\", \"field2\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_field2_uniq\" '\n        'UNIQUE USING INDEX \"field1_field2_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_field2_uniq\" UNIQUE (\"field1\", \"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_conditional_unique_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.UniqueConstraint(\n            fields=('field1',), name='field1_uniq', condition=models.Q(field1__gt=0)))\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") WHERE \"field1\" > 0;',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_uniq\" ON \"tests_model\" (\"field1\") WHERE \"field1\" > 0;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_conditional_multicolumn_unique_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(Model, models.UniqueConstraint(\n            fields=('field1', 'field2'), name='field1_field2_uniq', condition=models.Q(field1=models.F('field2'))))\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_field2_uniq\" ON \"tests_model\" (\"field1\", \"field2\") '\n        'WHERE \"field1\" = (\"field2\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_field2_uniq\" ON \"tests_model\" (\"field1\", \"field2\") '\n        'WHERE \"field1\" = (\"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_nulls_distinct_fields__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field1',), name='field1_uniq', nulls_distinct=True),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") NULLS DISTINCT;',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" '\n        'UNIQUE USING INDEX \"field1_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" UNIQUE NULLS DISTINCT (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_nulls_not_distinct_fields__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field1',), name='field1_uniq', nulls_distinct=False),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") NULLS NOT DISTINCT;',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" '\n        'UNIQUE USING INDEX \"field1_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" UNIQUE NULLS NOT DISTINCT (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_nulls_distinct_expression__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(models.F('field1'), name='field1_uniq', nulls_distinct=True),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") NULLS DISTINCT;',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_uniq\" ON \"tests_model\" (\"field1\") NULLS DISTINCT;',\n    ]\n\n\n@pytest.mark.django_db\n@pytest.mark.skipif(django.VERSION[:2] < (5, 0), reason='functionality provided in django 5.0')\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_nulls_not_distinct_expression__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(models.F('field1'), name='field1_uniq', nulls_distinct=False),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") NULLS NOT DISTINCT;',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_uniq\" ON \"tests_model\" (\"field1\") NULLS NOT DISTINCT;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_deferrable_deferred__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field1',), name='field1_uniq', deferrable=models.Deferrable.DEFERRED),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" '\n        'UNIQUE USING INDEX \"field1_uniq\" DEFERRABLE INITIALLY DEFERRED;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_uniq\" UNIQUE (\"field1\") DEFERRABLE INITIALLY DEFERRED;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_deferrable_immediate__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field2',), name='field2_uniq', deferrable=models.Deferrable.IMMEDIATE),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field2_uniq\" ON \"tests_model\" (\"field2\");',\n    ] + timeouts(\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field2_uniq\" '\n        'UNIQUE USING INDEX \"field2_uniq\" DEFERRABLE INITIALLY IMMEDIATE;',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field2_uniq\" UNIQUE (\"field2\") DEFERRABLE INITIALLY IMMEDIATE;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_include__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field1',), name='field1_uniq', include=['field2']),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") INCLUDE (\"field2\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_uniq\" ON \"tests_model\" (\"field1\") INCLUDE (\"field2\");'\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_opclasses__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field1',), name='field1_uniq', opclasses=['int4_ops']),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\" int4_ops);',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_uniq\" ON \"tests_model\" (\"field1\" int4_ops);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_unique_constraint_condition__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_constraint(\n            Model,\n            models.UniqueConstraint(fields=('field1',), name='field1_uniq', condition=models.Q(field1__lt=1)),\n        )\n    assert editor.collected_sql == [\n        'CREATE UNIQUE INDEX CONCURRENTLY \"field1_uniq\" ON \"tests_model\" (\"field1\") WHERE \"field1\" < 1;',\n    ]\n    assert editor.django_sql == [\n        'CREATE UNIQUE INDEX \"field1_uniq\" ON \"tests_model\" (\"field1\") WHERE \"field1\" < 1;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_meta_unique_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.remove_constraint(Model, models.UniqueConstraint(fields=('field1',), name='field1_uniq'))\n    assert editor.collected_sql == timeouts(\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"field1_uniq\";',\n    )\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"field1_uniq\";',\n    ]\n\n\n@pytest.mark.django_db\ndef test_add_meta_exclusion_constraint__warning():\n    with pytest.warns(UnsafeOperationWarning, match='ADD CONSTRAINT EXCLUDE is unsafe operation'):\n        with cmp_schema_editor() as editor:\n            editor.add_constraint(Model, ExclusionConstraint(expressions=[('field1', '=')], name='field1_excluded'))\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_excluded\" EXCLUDE USING GIST (\"field1\" WITH =);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_exclusion_constraint__raise():\n    with pytest.raises(UnsafeOperationException, match='ADD CONSTRAINT EXCLUDE is unsafe operation'):\n        with cmp_schema_editor() as editor:\n            editor.add_constraint(Model, ExclusionConstraint(expressions=[('field1', '=')], name='field1_excluded'))\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" ADD CONSTRAINT \"field1_excluded\" EXCLUDE USING GIST (\"field1\" WITH =);',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_meta_exclusion_constraint__ok():\n    with cmp_schema_editor() as editor:\n        editor.remove_constraint(Model, ExclusionConstraint(expressions=[('field1', '=')], name='field1_excluded'))\n    assert editor.collected_sql == timeouts(editor.django_sql)\n    assert editor.django_sql == [\n        'ALTER TABLE \"tests_model\" DROP CONSTRAINT \"field1_excluded\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" '\n        'ON \"tests_model\" (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" '\n        'ON \"tests_model\" (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_multicolumn_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(fields=['field1', 'field2'], name='tests_model_field1_45bc7f_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_45bc7f_idx\" '\n        'ON \"tests_model\" (\"field1\", \"field2\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_45bc7f_idx\" ON \"tests_model\" (\"field1\", \"field2\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_conditional_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(condition=models.Q(field1__gt=0), fields=['field1'], name='field1_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"field1_idx\" ON \"tests_model\" (\"field1\") WHERE \"field1\" > 0;',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"field1_idx\" ON \"tests_model\" (\"field1\") WHERE \"field1\" > 0;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_conditional_multicolumn_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(condition=models.Q(field1__gt=0), fields=['field1', 'field2'],\n                                             name='field1_field2_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"field1_field2_idx\" ON \"tests_model\" (\"field1\", \"field2\") WHERE \"field1\" > 0;',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"field1_field2_idx\" ON \"tests_model\" (\"field1\", \"field2\") WHERE \"field1\" > 0;',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_index_concurrently__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(fields=['field1'], name='tests_model_field1_9b60dc_idx'),\n                         concurrently=True)\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" (\"field1\");'\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_index_concurrently__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, models.Index(fields=['field1'], name='tests_model_field1_9b60dc_idx'),\n                         concurrently=True)\n    assert editor.collected_sql == flexible_statement_timeout(editor.django_sql)\n    assert editor.django_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" (\"field1\");'\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_meta_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.remove_index(Model, models.Index(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field1_9b60dc_idx\";',\n    ]\n    assert editor.django_sql == [\n        'DROP INDEX IF EXISTS \"tests_model_field1_9b60dc_idx\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_drop_meta_index_concurrently__ok():\n    with cmp_schema_editor() as editor:\n        editor.remove_index(Model, models.Index(fields=['field1'], name='tests_model_field1_9b60dc_idx'),\n                            concurrently=True)\n    assert editor.collected_sql == editor.django_sql\n    assert editor.django_sql == [\n        'DROP INDEX CONCURRENTLY IF EXISTS \"tests_model_field1_9b60dc_idx\";',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_brin_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, BrinIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING brin (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING brin (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_brin_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, BrinIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING brin (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING brin (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_btree_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, BTreeIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING btree (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING btree (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_btree_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, BTreeIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING btree (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING btree (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_gin_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, GinIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gin (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gin (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_gin_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, GinIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gin (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gin (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_gist_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, GistIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gist (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gist (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_gist_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, GistIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gist (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING gist (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_hash_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, HashIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING hash (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING hash (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_hash_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, HashIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING hash (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING hash (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True)\ndef test_add_meta_spgist_index__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, SpGistIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == [\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING spgist (\"field1\");',\n    ]\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING spgist (\"field1\");',\n    ]\n\n\n@pytest.mark.django_db\n@override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True,\n                   ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT=True)\ndef test_add_meta_spgist_index__with_flexible_timeout__ok():\n    with cmp_schema_editor() as editor:\n        editor.add_index(Model, SpGistIndex(fields=['field1'], name='tests_model_field1_9b60dc_idx'))\n    assert editor.collected_sql == flexible_statement_timeout(\n        'CREATE INDEX CONCURRENTLY \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING spgist (\"field1\");',\n    )\n    assert editor.django_sql == [\n        'CREATE INDEX \"tests_model_field1_9b60dc_idx\" ON \"tests_model\" USING spgist (\"field1\");',\n    ]\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist =\n    py{3.10,3.11,3.12,3.13}-django{5.1,5.2}-psycopg{2,3}\n    py{3.10,3.11,3.12}-django{5.0}-psycopg{2,3}\n    py{3.8,3.9,3.10,3.11,3.12}-django{4.2}-psycopg{2,3}\n\n[testenv]\nusedevelop = True\nallowlist_externals = bash\ncommands =\n    # linters\n    py{3.13}-django{5.2}-psycopg{3}: flake8\n    py{3.13}-django{5.2}-psycopg{3}: isort . --check --diff\n\n    # unit tests\n    py{3.8,3.9,3.10,3.11,3.12,3.13}-django{4.2,5.0,5.1,5.2}-psycopg{2,3}: bash -c \"DB_HOST=pg17 DB_USER=test pytest tests/unit\"\n    py{3.8,3.9,3.10,3.11,3.12,3.13}-django{4.2,5.0,5.1,5.2}-psycopg{2,3}: bash -c \"DB_HOST=postgis17 DB_USER=root DB_ENGINE=django_zero_downtime_migrations.backends.postgis pytest tests/unit\"\n\n    # integration tests\n    py{3.13}-django{5.2}-psycopg{3}: bash -c \"DB_HOST=pg17 DB_USER=test DB_ENGINE=django.db.backends.postgresql pytest tests/integration\"\n    py{3.13}-django{5.2}-psycopg{3}: bash -c \"DB_HOST=pg17 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.13}-django{5.2}-psycopg{3}: bash -c \"DB_HOST=postgis17 DB_USER=root DB_ENGINE=django_zero_downtime_migrations.backends.postgis pytest tests/integration\"\n\n    # old psycopg version support integration tests\n    py{3.13}-django{5.2}-psycopg{2}: bash -c \"DB_HOST=pg17 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n\n    # old postgres version support integration tests\n    py{3.13}-django{5.2}-psycopg{3}: bash -c \"DB_HOST=pg16 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.13}-django{5.2}-psycopg{3}: bash -c \"DB_HOST=pg15 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.13}-django{5.2}-psycopg{3}: bash -c \"DB_HOST=pg14 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.13}-django{5.1}-psycopg{3}: bash -c \"DB_HOST=pg13 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.12}-django{5.0}-psycopg{3}: bash -c \"DB_HOST=pg12 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n\n    # old django version support integration tests\n    py{3.13}-django{5.1}-psycopg{3}: bash -c \"DB_HOST=pg17 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.12}-django{5.0}-psycopg{3}: bash -c \"DB_HOST=pg17 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n    py{3.12}-django{4.2}-psycopg{3}: bash -c \"DB_HOST=pg17 DB_USER=test DB_SUPER_USER=root pytest tests/integration\"\n\ndeps =\n    py{3.13}-django{5.2}-psycopg{3}: flake8\n    py{3.13}-django{5.2}-psycopg{3}: isort\n\n    pytest\n    pytest-django\n    pytest-mock\n\n    psycopg2: psycopg2-binary\n    psycopg3: psycopg[binary]\n\n    django4.2: django>=4.2,<5.0\n    django5.0: django>=5.0,<5.1\n    django5.1: django>=5.1,<5.2\n    django5.2: django>=5.2,<6.0\n"
  }
]