Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement config for customising the FK name #1244

Merged
merged 14 commits into from
Apr 28, 2021

Conversation

danbroooks
Copy link
Contributor

@danbroooks danbroooks commented Apr 22, 2021

Before submitting your PR, check that you've:

  • Documented new APIs with Haddock markup
  • Added @since declarations to the Haddock
  • Ran stylish-haskell on any changed files.
  • Adhered to the code style (see the .editorconfig file for details)

After submitting your PR:

  • Update the Changelog.md file with a link to your PR
  • Bumped the version number if there isn't an (unreleased) on the Changelog
  • Check that CI passes (or if it fails, for reasons unrelated to your change, like CI timeouts)

Closes #1124

Initially, I went with an approach that mirrors what is already done with psToDBName (a function Text -> Text rather than an optional Maybe Text value), though I thought this might not be preferred, and that it may make more sense to provide an optional infix as described on the issue.

I then thought it might make sense to create a newtype wrapper that is private to the module, which you can either go with the default (that just appends the two strings without any infix), or an infixing configuration, as illustrated in the test:

https://github.com/yesodweb/persistent/pull/1244/files#diff-bb8c841390595945f4d325e011ab6d4a0dc800bccaae6ea1ed348164dda7e7bfR362

This might be a bit overkill, so I am happy to change to how it is described in the issue if that is preferred.

@danbroooks danbroooks marked this pull request as draft April 22, 2021 21:11
@danbroooks danbroooks marked this pull request as ready for review April 22, 2021 21:23
@parsonsmatt
Copy link
Collaborator

Can you base new changes off of the persistent-2.13 branch? I'm going to be working pretty heavily on refactoring and reorganizing code for that release and it'd be a lot easier if these changes were on top of that.

@danbroooks danbroooks changed the base branch from master to persistent-2.13 April 23, 2021 13:20
Copy link
Collaborator

@parsonsmatt parsonsmatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

We need to expose a setter in the Database.Persist.Quasi module since the type and fields are only Internal details.

Also the @since declarations are for 2.12.1.2 - adding an export item is a minor bump, so backporting would be persistent-2.12.2.0. And this PR is against persistent-2.13, so it would come out with persistent-2.13 as the initial release currently without a backport.

, psToFKName :: !ToFKName
-- ^ Function used to generate the FK name. Default value: @mappend@
--
-- @since 2.12.1.2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is currently against persistent-2.13 - are you planning on backporting it?

Note that the PersistSettings is currently hidden, so we'll want to export a setter setPsToFKName :: ToFKName -> PersistSettings -> PersistSettings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a hangover from my original change request, I will update these @since declarations 👍 looks like I need to rethink how this is switched on as well as you say, as in 2.13 PersistSettings is now in an Internal module, so I will add something like setPsToFKName

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just pushed some more stuff up, so thats ready for a second look 👍 I will add some annotations to what I've done as there are some additional things in there to consider

@@ -413,11 +413,14 @@ Unfortunately, we can't use this to create Haddocks for you, because <https://gi
-}
module Database.Persist.Quasi
( parse
, PersistSettings (..)
, PersistSettings
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the fields for PersistSettings were not actually being hidden, though from your comment I assume this is the desired behaviour in this version of persistent going forward, so I have snipped the fields off the export here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, dang, good catch 👍🏻

, preparse
, splitExtras
, takeColsEx
)
Copy link
Contributor Author

@danbroooks danbroooks Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise with this test, we are importing Quasi.Internal, but I assume, long term, we want to test the main Quasi module really without touching the Internal module (except other than perhaps the internal types, Line/Token and so on). I can revert this if you want though.

Having worked a little now on these modules/tests, I think it might be nice (especially after this Internal split on the quasi module), to have a test file for Quasi and a test file for Quasi.Internal, though I feel like a lot of these tests in here were added initially to cover as much of the module as possible because of the initial lack of coverage, it might be that a test file for Quasi to test the expected parsing behaviour would be sufficient. Not one for this PR though, just a thought I had while making this change 👍

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'd eventually like to hollow this out into a more 'typical' hspec test suite, so we'd have Database.Persist.QuasiSpec etc that contains all this stuff.

, nullable
) where

import Database.Persist.Quasi.Internal

setPsUseSnakeCaseForiegnKeys :: PersistSettings -> PersistSettings
setPsUseSnakeCaseForiegnKeys ps = ps { psToFKName = toFKNameInfixed "_" }
Copy link
Contributor Author

@danbroooks danbroooks Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bit of a long winded function name, but I went with this approach because I think people will want to either have this switched on or off. There is room for further flexibility though if this is wanted, but maybe that would be overkill.

I also thought with this change, that persistent should have this behaviour as default, potentially allowing people to configure this if they wanted to (though why they would I'm not sure), though unfortunately if we did that off the bat then that would break existing persistent codebases, right? Not sure what the long term fix is here, or it might have to be something that came along with a major version bump. Even along with a major version bump we are making people deal with renaming their foreign keys when there is actually technically wrong with them (they're just not aesthetically pleasing to the human reader).

Another approach with a major bump would be to swap this functionality, so rather than opt-in it would become opt-out, if for some reason people would rather just change the value in PersistentSettings than changing things in the DB.

Again, just thinking out loud here :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the default behavior should be consistent with whatever is currently happening - so end users wouldn't notice a change from this new option.

I'd prefer to see a more general setter exposed, even if this is the most likely variant that folks will want. People can just refer to the .Internal module but that's clunky.

Copy link
Collaborator

@parsonsmatt parsonsmatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's export a setter and the types for constructing the foreign key conversion functions.

It'd be nice to document the Text parameters to the function - TableName and ColumnName - somehow. But I'm not sure if you can attach Haddock to function arguments in a record field 🤔

sentToSecond Text

Foreign User fk_noti_user sentToFirst sentToSecond References emailFirst emailSecond
|]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome test <3

@@ -413,11 +413,14 @@ Unfortunately, we can't use this to create Haddocks for you, because <https://gi
-}
module Database.Persist.Quasi
( parse
, PersistSettings (..)
, PersistSettings
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, dang, good catch 👍🏻

, nullable
) where

import Database.Persist.Quasi.Internal

setPsUseSnakeCaseForiegnKeys :: PersistSettings -> PersistSettings
setPsUseSnakeCaseForiegnKeys ps = ps { psToFKName = toFKNameInfixed "_" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the default behavior should be consistent with whatever is currently happening - so end users wouldn't notice a change from this new option.

I'd prefer to see a more general setter exposed, even if this is the most likely variant that folks will want. People can just refer to the .Internal module but that's clunky.

@@ -98,8 +99,16 @@ parseFieldType t0 =
PSDone -> PSSuccess (front []) t
-- _ ->

newtype ToFKName = ToFKName
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an exposed type, let's put a doc comment on it:

Suggested change
newtype ToFKName = ToFKName
-- | A function for converting a table and column name into a constraint name.
--
-- @since 2.13.0.0
newtype ToFKName = ToFKName

Comment on lines 103 to 104
{ unToFKName :: Text -> Text -> Text
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{ unToFKName :: Text -> Text -> Text
}
{ unToFKName :: Text -> Text -> Text
}

, upperCaseSettings
, lowerCaseSettings
, setPsUseSnakeCaseForiegnKeys
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should export the FKName type and constructor here too, for use with the setter.

, preparse
, splitExtras
, takeColsEx
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'd eventually like to hollow this out into a more 'typical' hspec test suite, so we'd have Database.Persist.QuasiSpec etc that contains all this stuff.

Copy link
Collaborator

@parsonsmatt parsonsmatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heck yeah! Thank you so much 👐🏻

--
-- @since 2.13.0.0
setPsToFKName :: (EntityNameHS -> ConstraintNameHS -> Text) -> PersistSettings -> PersistSettings
setPsToFKName setter ps = ps { psToFKName = setter }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful 👍🏻

@@ -102,6 +103,11 @@ parseFieldType t0 =
data PersistSettings = PersistSettings
{ psToDBName :: !(Text -> Text)
-- ^ Modify the Haskell-style name into a database-style name.
, psToFKName :: !(EntityNameHS -> ConstraintNameHS -> Text)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blessed, thanks! I thought it was a table name 😂 types FTW

, parsedEntityDefEntityAttributes :: [Attr]
, parsedEntityDefFieldAttributes :: [[Token]]
, parsedEntityDefExtras :: M.Map Text [ExtraLine]
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this is fantastic

In #1256 I'm probably going to be only forwarding this type along, instead of the EntityDef. Maybe the UnboundEntityDef.

@parsonsmatt parsonsmatt merged commit be9fb52 into yesodweb:persistent-2.13 Apr 28, 2021
@danbroooks danbroooks deleted the 1124-prefixable-fkeys branch May 5, 2021 16:24
parsonsmatt added a commit that referenced this pull request May 5, 2021
* Internalize SqlBackend (#1225)

* abstractification

* abstractification

* Internalize SqlBackend

* suppor tother sql dbs

* bounds

* asdf

* import data.monoid

* limitoffset and backendspecificoverrides

* use setter

* getConnUpsertSql

* formatting

* sigh

* clean warns

* cabal formatting

* merge resolve

* lol

* ok

* update changelogs

* one more [ci skip]

* Implicit ID Column Configuration (#1234)

* Implicit ID Column COnfiguration

* PersistSettings is internal

* start teasing out the module structure

* move around, factor out the Names module

* start enumerating types, make entitydef abstract

* entity def abstraction

* teasing out the EntityDef stuff

* builds

* testinggg

* it works

* it works

* tidy up

* sigh

* i hate you

* tidy

* wrote test for mysql, need to set maxlen sigh

* mysql test, need to be able to set maxlen

* support mysql lmfao

* whyyy

* Deprecate mpsGeneric (#1250)

* discoverEntities (#1253)

* discover entities

* remove fdescribe

* changelog

* yupo

* remove error

* Fix migrations (#1252)

* Better migrations

* why is the test failing

* Columns are present in entityFields now, but the generated code is broken.

* th specs work

* fixed mkColumns

* changelog entry

* fix mongo

* no idea why this is broken now

* why on earth did this work

* remove debug trace statements

* typo

* what no put that back in

* Check for existence of entities before generating them (#1255)

* wtf

* hmmm

* refactor and tidy

* are foreign fields never right ?!

* changelog

* fix comments

* dead code

* Implement config for customising the FK name (#1244)

* Implement config for customising the FK name

* Update changelog

* Tweak test description

* Tweaks/better use of types

* Review tweaks

* Some initial post-review changes

* Table name turned out to be EntityNameHS

* Do the same thing but for the constraint

* Expose more stuff

* Some refactoring / cleanup

* Fix changelog indentation

* Tidy code layout

* QuasiQuoter Improvements (#1256)

* QQ now returns UnboundEntityDef

* Relocate fixForeignKeysAll

* deprecate some stuff, reorganize some code

* ok, now we need to set sql types appropriately.

* dodgy instances are banned

* fuse away the EntityDefSqlTypeExp stuff

* refactor to top level

* fuse sqlTypeExp in there

* fix Key vs Id stuff

* still need to get the foreign key types right

* hmmm

* move to QuasiSpec

* clean up tests

* so close

* ok but what if i don't fix foreign keys

* wip

* oh man please

* getting closer...

* make some tests

* fix json and keyFromValueM

* slightly more graceful handling

* return dummy field for id, from persist values

* got some tests passing

* well sqlite works

* pg tests running

* what happened

* hmm mongo is trashed maybe

* bye mongo

* ok for real bye mongo, for now at least

* clean warns

* asdf

* drop GHC 8.2 support

* sigh

* lots of commments

* Merge branch 'master' into persistent-2.13 (#1261)

* Reexport PersistValue

* fix json for MigrationOnly

* Export onlyOneUniqueDef (fixes #1194)

* use onlyOneUniqueDef in persistent-postgresql

Co-authored-by: Dan Brooks <danbroooks@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Foreign key constraint name is weird
2 participants