|
| 1 | +# Creating a new migration |
| 2 | + |
| 3 | +After the PyCDS ORM has been modified, you will usually want to create an Alembic migration script to enable existing databases to be upgraded to the new model. |
| 4 | + |
| 5 | +Alembic can generate a migration script for you. It can generate an empty script, or autogenerate a script containing changes inferred by comparison between the ORM and an existing database. The command for this is `alembic revision ...`. |
| 6 | + |
| 7 | +In the context of PCIC's databases (CRMP, Metnorth), autogeneration is less useful than one might hope. PCIC's databases have not been managed solely through PyCDS, and so diverge from the ORM definitions. Much of this divergence has been cleaned up, but there are still differences in some table definitions, indexes and constraints. Furthermore, the autogenerate function is only aware of tables and table-related objects. It does not handle functions, views, and matviews, and these are critical to our databases' operation. |
| 8 | + |
| 9 | +In this situation, autogenerate generates draft migrations containing a great deal of spurious and undesirable content, and also omits all content related to non-table objects. Fixing this actually adds unnecessary work. |
| 10 | + |
| 11 | +Therefore, developers usually create a new migration _without_ using the `--autogenerate` flag. The result is a skeleton migration script with empty `upgrade` and `downgrade` functions. The benefits are that it generates a unique revision identifier, creates an appropriately named file in the correct directory, and inserts the migration into the migration sequence automatically. See [Create a Migration Script](https://alembic.sqlalchemy.org/en/latest/tutorial.html#create-a-migration-script) for an example. |
| 12 | + |
| 13 | +## Generating a new migration script (without autogeneration) |
| 14 | + |
| 15 | +Instructions: |
| 16 | + |
| 17 | +1. Generate the migration script: |
| 18 | + |
| 19 | + ```shell script |
| 20 | + alembic revision -m "<message>" |
| 21 | + ``` |
| 22 | + |
| 23 | + Notes: |
| 24 | + - `PYCDS_SU_ROLE_NAME` is not required for this operation. |
| 25 | + - The `<message>` should be a succinct description of what the migration accomplishes; for example "Add name column to Users". This message becomes part of the name and content of the script. |
| 26 | + |
| 27 | +2. Alembic writes a new script to the directory `pycds/alembic/versions`. Its name includes a unique revision identifier (a SHA) and a version of `<message>`. |
| 28 | + |
| 29 | +3. Add code to create, modify, and/or drop commands for all items to be managed in this migration. (These commands are on the object `alembic.op`, which is imported in the skeleton script.) |
| 30 | + |
| 31 | + 1. **Ensure that all changes respect the specified schema name** that the user will supply when this migration is applied. In particular: |
| 32 | + 1. It's useful to obtain the specified schema name with `schema=pycds.get_schema_name()`. |
| 33 | + 2. Ensure the specified schema name is used for all objects, including functions, views and materialized views. This should come without special effort due to the structure of how these items are declared, but it is worth verifying. |
| 34 | +
|
| 35 | + 2. Do this for both upgrade and downgrade functions in the script! |
| 36 | + 3. Data and schema migrations are not handled separately and should be included as part of the migration where applicable. |
| 37 | +
|
| 38 | +4. Write some tests for the migration, including tests for the data migration where applicable. Examples can be found in the existing code. |
| 39 | +
|
| 40 | +5. Commit the new migration script and its tests to the repo. |
| 41 | +
|
| 42 | +For more information, see [Create a Migration Script](https://alembic.sqlalchemy.org/en/latest/tutorial.html#create-a-migration-script). |
| 43 | +
|
| 44 | +Modify the script to perform the necessary actions for upgrading and downgrading a database to/from this revision. For useful examples, see other scripts in directory `pycds/alembic/versions`. |
| 45 | +
|
| 46 | +## Autogenerating a new migration script |
| 47 | +
|
| 48 | +As noted above, **_autogeneration is not usually helpful_**. However, here are some instructions if you wish to do so. For more details, see the [Alembic documentation](https://alembic.sqlalchemy.org/en/latest/autogenerate.html). |
| 49 | +
|
| 50 | +To autogenerate a migration script, you must have a reference database schema for Alembic to compare to the modified PyCDS ORM. After Alembic autogenerates the script, you must edit it to ensure completeness, correctness, and that it respects the specified schema name (see instructions below). |
| 51 | +
|
| 52 | +Instructions: |
| 53 | +
|
| 54 | +1. Choose or create a reference database schema that is at the latest migration. (Or a database schema at an otherwise desired "base" migration, which is unusual but not necessarily wrong.) |
| 55 | + To serve as a reference, a schema need not contain any data. |
| 56 | +
|
| 57 | +1. If the reference database is not named in `alembic.ini`, create an entry there for it of the form |
| 58 | +
|
| 59 | + ```ini |
| 60 | + [<db-label>] |
| 61 | + sqlalchemy.url = postgresql://<user-name>@<server-name>/<db-name> |
| 62 | + ``` |
| 63 | +
|
| 64 | +1. Autogenerate the migration script: |
| 65 | +
|
| 66 | + ```shell script |
| 67 | + [PYCDS_SCHEMA_NAME=<schema name>] alembic -x db=<db-label> revision --autogenerate -m "<message>" |
| 68 | + ``` |
| 69 | +
|
| 70 | + Notes: |
| 71 | + - `PYCDS_SU_ROLE_NAME` is not required for this operation. |
| 72 | + - The `<message>` should be a succinct description of what the migration accomplishes; for example "Add name column to Users". This message becomes part of the name and content of the script. |
| 73 | +
|
| 74 | +1. Alembic writes a new script to the directory `alembic/versions`. Its name includes a unique revision identifier (a SHA) and a version of `<message>`. |
| 75 | +
|
| 76 | +1. Review and edit the script to ensure correctness, completeness, and that it respects the specified schema name. Specifically: |
| 77 | +
|
| 78 | + 1. There are (still) significant differences between the CRMP schema and this ORM. Expect to remove or alter many of the autogenerated operations. |
| 79 | +
|
| 80 | + 2. Review to ensure that it picks up all changes to tables and implements them appropriately. |
| 81 | + In particular, Alembic does not pick up on changes of table or column name, which must be manually converted from "drop old name, add new name" to "rename". Some other schema changes are also not detected. |
| 82 | +
|
| 83 | + 3. For more information, see [What does Autogenerate Detect (and what does it not detect?)](https://alembic.sqlalchemy.org/en/latest/autogenerate.html#what-does-autogenerate-detect-and-what-does-it-not-detect). |
| 84 | +
|
| 85 | + 4. Manually add creation or dropping of the following things not handled by Alembic autogenerate: |
| 86 | + 1. Functions |
| 87 | + 1. Views |
| 88 | + 1. Materialized views |
| 89 | +
|
| 90 | + 5. **Ensure that all changes respect the specified schema name** that the user will supply when this migration is applied. In particular: |
| 91 | + 1. Replace any autogenerated schema name usage (e.g., `schema='crmp'`) with the specified schema name (`schema=pycds.get_schema_name()`). |
| 92 | + 1. Ensure the specified schema name is used for functions, views and materialized views. This should come without special effort due to the structure of how these items are declared, but it is worth verifying. |
| 93 | +
|
| 94 | + 6. Do this for both upgrade and downgrade functions in the script! |
| 95 | +
|
| 96 | +1. Write some tests for the migration. Examples can be found in the existing code. |
| 97 | +
|
| 98 | +1. Commit the new migration script and its tests to the repo. |
| 99 | +
|
0 commit comments