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

Add support for RECURSIVE withs #110

Merged
merged 3 commits into from
Aug 27, 2024
Merged

Conversation

Raveline
Copy link
Contributor

I'm going to need some recursive powered CTE, but hpqtypes-extra doesn't offer support for these neat things.
This PR propose adding support for them. It's a bit tricky, because the syntax demands that "RECURSIVE" be right after the "WITH" and cannot target a singular CTE.

@Raveline Raveline requested a review from jonathanjouty July 17, 2024 14:59
@Raveline Raveline force-pushed the add-support-for-recursive-with branch 3 times, most recently from 8f9b30d to d1b0551 Compare July 18, 2024 13:00
@Raveline Raveline force-pushed the add-support-for-recursive-with branch from d1b0551 to 11631a0 Compare July 18, 2024 15:34
@@ -127,10 +127,12 @@ module Database.PostgreSQL.PQTypes.SQL.Builder
, sqlLimit
, sqlDistinct
, sqlWith
, sqlWithRecursive
Copy link
Contributor

Choose a reason for hiding this comment

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

I need to read up on how WITH RECURSIVE works, but in the meantime I have an excellent code review delay tactic: could you add a test that uses sqlWithRecursive?

(to be fair I do honestly think we should have a test)

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 must fix something first, but will gladly add tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

BTW the code looks sensible, but I'm not too familiar with the inner workings of this library.
Who else can we get to take a look in @arybczak's absence 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There you go, I've added a simple test showcasing the recursive with ! I don't really know who else is familiar enough with the library, your name was on the suggested reviewers, that's why I bothered you in the first place !

@Raveline Raveline force-pushed the add-support-for-recursive-with branch from 6b4a67d to 2090101 Compare July 25, 2024 10:14
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.

What happens if you do

sqlWithRecursive ...
sqlWith ...
...

?

Looks to me like the last sqlWith will overwrite the Recursive tag of the first sqlWithRecursive, right?

The simplest solution here would be to mark all WITH clauses recursive, right?

@@ -443,6 +452,16 @@ checkAndRememberMaterializationSupport = do
fetchOne runIdentity
liftIO $ writeIORef withMaterializedSupported (isRight res)

-- This function has to be called as one of first things in your program
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this feature supported in pretty much all PostgreSQL versions? I checked and 9.6 looks to have it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They started in 8.4 according to Wikipedia, but I don't know what are the older versions supported by hpqtypes-extra. If we don't need to check for the feature, I'll be glad to remove these !

Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't really care for anything older than 11 at this point ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed then.

@Raveline
Copy link
Contributor Author

@arybczak : Indeed, it would get overridden. I didn't think about it, shame on me.

As for marking ALL CTEs as recursive, apart from the obvious "adding code that is not always explicitly needed" issue (I guess we can live with that one), I just would like for @marco44 to confirm that there is no overhead / issue we didn't think about in putting "RECURSIVE" everywhere.

@marco44
Copy link

marco44 commented Aug 27, 2024

If you're building a list of CTE expressions, and one in the list is recursive, you need to add the recursive keyword to the whole CTE block, so if you have a bunch of sqlWith, sqlWithRecursive... the sqlWithRecursive "wins", you need to add the keyword. You're telling PostgreSQL that any of the subqueries may be recursive, so if it seens some UNION/UNION ALL constructs, it has to inspect them differently.

Making all CTEs recursive in the whole app (if I understood correctly the question) seems bad to me:

  • Of course, there are a few things that will get more costly to parse and to plan, not by that much, but I don't think anyone benchmarked the impact of having a non-recursive query marked "recursive" (it would be quite complicated to benchmark, given the amount of cases that you'd need to test). The final plan should make no difference (so no execution penalty), as it's mostly allowing UNION/UNION ALL in the CTE subqueries after the WITH clause, and allowing those to iterate if they're referencing the initial term (the one before the UNION). So you're basically asking the planner to check for that all the time. And planning is still a big chunk of our total resource consumption (like 20-25% of the total CPU spent), so anything saved here is probably worth it.

  • The second problem I see is that you're removing a protection that you get by not marking unnecessarily a query as recursive, as then the planner would notice you doing naugthy recursive things in a supposedly non recursive query and refuse it.

  • And a last argument against… please think of your DBA. It's very useful for me to see a query marked as "with recursive" only if it is, because I know what it's doing without having to look into all blocks to guess if it's really doing some recursion or not.

@Raveline
Copy link
Contributor Author

All right, lemme find a better way of handling this then.

@Raveline
Copy link
Contributor Author

This version keeps RECURSIVE iff there is at least one withRecursiveWith around (this is tested), using a Semigroup instance that behaves more or less like Any.


class SqlWith a where
sqlWith1 :: a -> SQL -> SQL -> Materialized -> a
instance Semigroup Recursive where
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can put a comment here that explains why it works like this 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added !

@Raveline Raveline force-pushed the add-support-for-recursive-with branch from ccfe023 to 2f7cf86 Compare August 27, 2024 13:03
@Raveline Raveline merged commit 915a285 into master Aug 27, 2024
7 checks passed
@Raveline Raveline deleted the add-support-for-recursive-with branch August 27, 2024 13:23
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.

4 participants