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

Introduce sqlAll, sqlAny and related state types #112

Merged
merged 6 commits into from
Sep 30, 2024

Conversation

zlondrej
Copy link
Contributor

This allows writing certain helper functions which weren't possible before.

Specific use case is for filters:

data EqFilter a
  = Eq a
  | In [a]

newtype SomeUserInfo = SomeUserInfo { someUserInfo :: Text }

...hs
-- This should convert the `EqFilter` into `hpqtypes-extras` expressions
applyEqFilter :: forall v n . (MonadState v n, SqlWhere v) => EqFilter SomeUserInfo -> n ()
applyEqFilter = \case
  Eq value -> sqlWhereSomeInfo sqlWhereEq value
  In value -> sqlWhereSomeInfo sqlWhereIn value
  where
    -- Without the type that's used for `sqlWhereAny`, I couldn't figure out
    -- how to write the type signature so it would actually type check.
    sqlWhereSomeInfo :: (SQL -> a -> State SqlWhereAny ()) -> a -> n ()
    sqlWhereSomeInfo sqlCondition value =
      sqlWhereAny
        [ sqlCondition "first_name" value
        , sqlCondition "last_name" value
        , sqlCondition "email" value
        ]

Without the type available, one can only use constraints, but then there's an issue of ambiguous type variables:

    • Could not deduce (SqlWhere v2)
        arising from a use of ‘sqlWhereSomeInfo’
      from the context: (MonadState v n, SqlWhere v)
        bound by the type signature for:
                   applyEqFilter :: forall v (n :: * -> *).
                                          (MonadState v n, SqlWhere v) =>
                                          EqFilter SomeUserInfo -> n ()
        at ...
      The type variable ‘v2’ is ambiguous
      Potentially matching instances:
        instance SqlWhere SqlDelete
          -- Defined in ‘Database.PostgreSQL.PQTypes.SQL.Builder’
        instance SqlWhere SqlInsertSelect
          -- Defined in ‘Database.PostgreSQL.PQTypes.SQL.Builder’
        instance SqlWhere SqlSelect
          -- Defined in ‘Database.PostgreSQL.PQTypes.SQL.Builder’
        instance SqlWhere SqlUpdate
          -- Defined in ‘Database.PostgreSQL.PQTypes.SQL.Builder’
        ...plus one instance involving out-of-scope types
          Potentially matching instance:
            instance SqlWhere
                       Database.PostgreSQL.PQTypes.SQL.Builder.SqlAll
    • In the expression: sqlWhereSomeInfo sqlWhereEq value
      In a \case alternative:
          Eq value -> sqlWhereSomeInfo sqlWhereEq value
    |
    |           Eq value -> sqlWhereSomeInfo sqlWhereEq value
    |                       ^^^^^^^^^^^^^^^^

Note: I realized later that there's is a way to write the helper, which is by omitting the type signature entirely, but I don't see any reason why this type has to be hidden.

@zlondrej zlondrej requested a review from kubek2k August 23, 2024 19:03
@zlondrej zlondrej requested a review from arybczak September 4, 2024 10:09
Copy link
Collaborator

@arybczak arybczak left a comment

Choose a reason for hiding this comment

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

There are a few hlint warnings btw.

-- | Type representing a set of conditions that are joined by 'AND'.
--
-- This is usually not needed as the default behavior is to join conditions
-- by 'AND', but it is used to implement `sqlWhereAny`.
Copy link
Collaborator

@arybczak arybczak Sep 9, 2024

Choose a reason for hiding this comment

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

Is it? Is says the same thing about SqlWhereAny, it's a bit confusing.

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 guess I'll remove the implementation details from the comments.

-- | Run monad that joins all conditions with 'AND' operator.
--
-- When no conditions are given, the result is 'TRUE'.
sqlAll :: State SqlWhereAll () -> SQL
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is it for? Apart from being used in sqlWhereAny. Tests?

Because sqlWhere . sqlAll is redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well for nesting ANDs in OR's and parity with sqlAny. How else are you going to write (a OR (b AND c))?

I mean, you could use sqlWhereAny, but if you want to go with sqlWhere . sqlAny $ ..., then at one point you have to use sqlWhere . sqlAll $ ... inside.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I see. The comment you added to the code makes it clear, thanks.

-- | Run monad that joins all conditions with 'OR' operator.
--
-- When no conditions are given, the result is 'FALSE'.
sqlAny :: State SqlWhereAny () -> SQL
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps we should deprecate sqlWhereAny and start using sqlWhere . sqlAny $ do ... instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Depends if you'd like to remove it eventually. But if you want to deprecate sqlWhereAny, then I'd like to deprecate sqlOR, sqlConcatAND and sqlConcatOR as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If it comes to kontrakcja, sqlOR is used 1 time, sqlConcatAND 5 times and sqlConcatOR 4 times, so it seems feasible. Let's do it a separate PR though.

This allows writing cartain helper functions which weren't possible
before.
Previous commit introduced some inconsistencies in terms of documentation
vs implementation for `SqlWhereAny`.

For that reason, I dediced to expand on the scope a little bit and
introduce `WHERE` wrappers for `AND` and `OR` conditions.
@zlondrej zlondrej force-pushed the dev-janosik-export-sql-all branch from 083fba7 to 79de921 Compare September 9, 2024 13:38
@zlondrej zlondrej requested a review from arybczak September 9, 2024 13:38
Copy link
Collaborator

@arybczak arybczak left a comment

Choose a reason for hiding this comment

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

Looks good, thanks. Feel free to merge after addressing the comment about empty line in tests.

-- | Run monad that joins all conditions with 'AND' operator.
--
-- When no conditions are given, the result is 'TRUE'.
sqlAll :: State SqlWhereAll () -> SQL
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I see. The comment you added to the code makes it clear, thanks.

test/Main.hs Outdated
, sqlWhere "cond4"
]
]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the empty line? :)

-- | Run monad that joins all conditions with 'OR' operator.
--
-- When no conditions are given, the result is 'FALSE'.
sqlAny :: State SqlWhereAny () -> SQL
Copy link
Collaborator

Choose a reason for hiding this comment

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

If it comes to kontrakcja, sqlOR is used 1 time, sqlConcatAND 5 times and sqlConcatOR 4 times, so it seems feasible. Let's do it a separate PR though.

@arybczak
Copy link
Collaborator

I forgot, please update the PR title (IIUC it's outdated) and add changelog entry.

@zlondrej zlondrej changed the title Export SqlWhereAny type so it can be used in type signatures Introduce sqlAll, sqlAny and related state types Sep 30, 2024
@zlondrej zlondrej merged commit 573a865 into master Sep 30, 2024
7 checks passed
@zlondrej zlondrej deleted the dev-janosik-export-sql-all branch September 30, 2024 14:02
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.

2 participants