[Gravity] added support for recursive common table expressions in gravity
Review Request #544 — Created Feb. 15, 2018 and submitted
Information | |
---|---|
k.hengsdijk | |
Lunr | |
recursiveCTEsupport | |
Reviewers | |
lunr | |
added support for recursive common table expressions to gravity
Change Summary:
added support for the recursive clause in a WITH statement and hopefully correctly updated this review request
Diff: |
Revision 2 (+64 -1) |
---|
-
This is still not incremental. It still shows everything as new. Please make sure you reference your parent properly
Change Summary:
this time changed the parent so hopefully this review request is correct
Branch: |
|
||||
---|---|---|---|---|---|
Diff: |
Revision 3 (+19 -14) |
-
-
src/Lunr/Gravity/Database/DMLQueryBuilderInterface.php (Diff revision 3) While this certainly works, it shoves all the responsibility to create the right query somewhere else, either to the developer or to the escaper. And I can't really think of a escaper function name that wouldn't be confusing or hard to identify.
recursive_with_query
is a mouthful...We can do better ;)
Let's see if you can come up with a better solution than this on your own ;)
Change Summary:
now the with function takes a second query which is part of the recursive query. The function also now takes away some responsibility from the developer but the developer is still responsible for creating the right select querys. I really cant think of a way to do this without the developer creating the two queries because the columns have to be specified in both queries.
Diff: |
Revision 4 (+67 -1) |
---|
-
-
-
src/Lunr/Gravity/Database/DMLQueryBuilderInterface.php (Diff revision 4) Descriptions should start with a capital letter
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 4) doesn't it contain the whole WITH query?
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 4) Do CTEs only work for SELECT queries?
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 4) there should probably be a space between the alias and the collumns
-
-
-
src/Lunr/Gravity/Database/DMLQueryBuilderInterface.php (Diff revision 5) This is really clumsy for various reasons:
1) can't use recursive CTEs without specifying column_names. If you reverse the order you can't specify column_names without declaring a recursive part. Ideally you want neither.
2) Parameter names are misleading. $recursive is part of the SQL query, so what is the extra $sql_query about?
-
Change Summary:
i created a new method to use for recursion. This works pretty much the same as my first solution but i really can't think of a way of way to remove the responsibility to create the right query from the developer. Because the developer has to specify the two select statements in a recursive CTE so all thats left for me to take away from the developer is a UNION statement but a function for that already exists.
at the moment this is how a recursive CTE can be created by the developer:
$escaper = $connector->get_query_escaper_object(); $builder = $connector->get_new_dml_query_builder_object(FALSE); $builder2 = $connector->get_new_dml_query_builder_object(FALSE); $builder3 = $connector->get_new_dml_query_builder_object(FALSE); $builder2 ->select('*') ->from('Werknemer') ->where($escaper->column('wnr'), $escaper->value('7369')); $builder3 ->select('Werknemer.*') ->from('Werknemer') ->from('Chefs') ->where($escaper->column('Werknemer.wnr'), $escaper->column('Chefs.chef')); $builder2->union($builder3->get_select_query()); $builder ->recursive_with('chefs', $builder2->get_select_query()) ->select('*') ->from('chefs');
Diff: |
Revision 6 (+90 -1) |
---|
-
-
src/Lunr/Gravity/Database/DMLQueryBuilderInterface.php (Diff revision 7) This is a good step in the right direction, but you didn't have to throw away all the other code :/
$sql_query alone is too lazy. We can offer an interface for the developer that better covers the WITH RECURSIVE use case.
Change Summary:
modified the recursive_with function now it accepts 2 select queries from the user and creates the recursive query with them.
Diff: |
Revision 8 (+44 -1) |
---|
-
-
-
src/Lunr/Gravity/Database/SQLDMLQueryBuilder.php (Diff revision 8) Sorry, that's wishful thinking.
Individual query parts are meant to be able to be constructed out-of-order. So conceptually, I can define a union upfront, and construct from/where/having/etc later on based on other conditions.
But with this code, I could no longer do that, since the upfront defined union would conflict with a recursive CTE and the query constructed would not at all be what I want it to look like.
Change Summary:
now i have stopped reusing a function but instead i have copied a part of the union() function. this way the order of how the query parts are constructed shouldnt matter anymore. i dont know how bad it is to duplicate such a part of the code but this is the best solution i can think of at the moment.
Diff: |
Revision 9 (+47 -1) |
---|
-
-
src/Lunr/Gravity/Database/DMLQueryBuilderInterface.php (Diff revision 9) When reading this, nobody has any clue wtf this is about. Why would I ever not want all results?
-
-
-
src/Lunr/Gravity/Database/SQLDMLQueryBuilder.php (Diff revision 9) query constructing should happen in sql_with()
Don't make the larger use-case fit the smaller interface.
Change Summary:
moved the way a query is build to the sql_with function. now that function is kinda more generic and it has more parameters. in the function with() i hard coded the two parameters with '' so that the user doesnt have to worry about them. Also made changes to the name of parameters.
Diff: |
Revision 10 (+47 -6) |
---|
-
-
src/Lunr/Gravity/Database/DMLQueryBuilderInterface.php (Diff revision 10) This is confusing, you'd think
union = true
would mean that it's usingUNION
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 10) consistent uppercasing for types please
-
-
-
-
-
src/Lunr/Gravity/Database/SQLDMLQueryBuilder.php (Diff revision 12) Please don't use uppercase for classes.
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 13) I would not bind this to the alias. Either bind it to the keyword (WITH) or make it a separate variable
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 13) recursive statements only work in the else clause?
Change Summary:
changed recursive clause so it is independent of the alias
Diff: |
Revision 14 (+113 -10) |
---|
Change Summary:
removed the constraint that with recursive can only be the first with statement
Diff: |
Revision 15 (+114 -11) |
---|
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 15) I don't think that's correct though. If you check the grammar, the "RECURSIVE" keyword is only mentioned once in the beginning. You can't have that one more than once.
What I read is that it needs to be present as soon as one of the later mentioned CTEs is recursive.
However, since we can specify CTEs in any order in the code, we don't know with the first statement whether there's a recursive one coming later. That's a fun one to solve :)
Change Summary:
fixed the problem where the order of the with statements mattered. now the order doesnt matter anymore
Diff: |
Revision 16 (+118 -10) |
---|
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 16) what if $temp already includes a recursive CTE?
It really helps adding unit tests for the cases that you want to implement. Think about all the cases that could happen, add tests for them, and then make them work.
Change Summary:
added some unit tests and fixed that a user can specify two recursive CTE's
Diff: |
Revision 17 (+202 -10) |
---|
-
-
src/Lunr/Gravity/Database/Tests/DatabaseDMLQueryBuilderQueryPartsWithTest.php (Diff revisions 16 - 17) what about testNonRecursiveWithAfterRecursiveQuery*?
Change Summary:
moved the handling of WITH and WITH recursive to implode_query
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 19) This doesn't work, as the semantic meaning of what's written here means "put WITH statements between WHERE and GROUP BY".
I forgot that SELECT isn't part of the implode, so you'll likely have to handle the "WITH (RECURSIVE)" part directly here (in get_select_query()).
Change Summary:
move the handling of with statements to get_select_query()
Diff: |
Revision 20 (+301 -20)
|
---|
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 20) 'recursive' is not enough as a variable name. Missing context
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 20) You can't write into $this->with. get_*() methods must be without side-effects (calling it twice should yield the same result)
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 20) I would use NULL as default value everywhere, but still handle empty strings as well
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revision 20) You don't need a temporary variable.
-
Diff: |
Revision 21 (+307 -21)
|
---|
Change Summary:
removed the temp variable
Diff: |
Revision 22 (+306 -21)
|
---|
-
-
src/Lunr/Gravity/Database/DatabaseDMLQueryBuilder.php (Diff revisions 21 - 22) Much better :)
Now merge this if with the if below and we are done ;)
Change Summary:
merged the if
Diff: |
Revision 23 (+306 -21)
|
---|