yesodweb / yesodweb/persistent
Schema Compatibility Checking
Nobody has claimed this yet.
- Dominant language
- Haskell
- Stars
- 486
- Forks
- 306
- PR merge metrics
- No merged PRs in 30d
Description
Currently, persistent's migrations can determine whether or not the database is an exact match. It would be nice to have a feature where persistent can use the migration machinery to determine compatibility - eg, can persistent talk to the connected database without problems?
There are a few things I want to hash out here, so let's go over some scenarios for matching and mismatching, and what is actually compatible.
Foreign Key Cascade
mkPersist sqlSettings [persistLowerCase|
User
name Text
Organization
owner UserId
|]
CREATE TABLE user (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE organization (
id SERIAL PRIMARY KEY,
owner INT NOT NULL REFERENCES user(id) ON DELETE CASCADE
);
These are not an exact match because the organization table has an ON DELETE CASCADE, while the Organization model does not specify (and receives the default ON DELETE RESTRICT behavior). However, these definitions are compatible - persistent can read/write Users and Organizations just fine. The only difference in runtime behavior is that writing delete userId with an ON DELETE RESTRICT will cause a runtime error due to the schema constraint violation, while ON DELETE CASCADE will also delete the Organization that is owned by the User.
Extra Fields
With Default?
Foo
name Text
-- foo.sql
CREATE TABLE foo (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT
);
These are not an exact match, but they are compatible -
- Parsing a
Fooout of the database should succeed, since a row will contain theid,name,ageand we only needid,name. - Inserting a
Fooshould succeed, sinceagehas an implicitNULLdefault value.
Currently, we use MigrationOnly to indicate "this column should exist on the database, but not in Haskell." But we uhhh don't do any checks to ensure that the resulting database schema can accept a NULL in the insert list, so that at least needs to be fixed. #918 tracks this, and this comment proposes what is IMO the best solution.
Without Default
Now, consider that foo has a NOT NULL constraint on age. This is now incompatible, because if we try to insert Foo { fooName = "Hello" }, we'll get a runtime error that we've violated a NOT NULL constraint for the column. This is implcitly a default=NULL, so really we should verify that we have a default of some sort in a column for compatibility to work out there.
Unique Key Names ?
What about constraint names?
We can name constraints. Do we use them anywhere? If so, for what operations? It clearly doesn't matter for some operations, but others may rely on the actual name of the constraint (thinking about postgres' upsertBy).
Others?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing persistent's migration machinery and how MigrationOnly columns are handled, then read the discussion in issue #918. Examine the PostgreSQL upsertBy constraint-name question as another compatibility case; done would require agreed compatibility rules and corresponding implementation and tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell, postgresql
- Domain
- databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100