Обсуждение: pg_plan_advice
As I have mentioned on previous threads, for the past while I have been working on planner extensibility. I've posted some extensibility patches previously, and got a few of them committed in Sepember/October with Tom's help, but I think the time has come a patch which actually makes use of that infrastructure as well as some further infrastructure that I'm also including in this posting.[1] The final patch in this series adds a new contrib module called pg_plan_advice. Very briefly, what pg_plan_advice knows how to do is process a plan and emits a (potentially long) long text string in a special-purpose mini-language that describes a bunch of key planning decisions, such as the join order, selected join methods, types of scans used to access individual tables, and where and how partitionwise join and parallelism were used. You can then set pg_plan_advice.advice to that string to get a future attempt to plan the same query to reproduce those decisions, or (maybe a better idea) you can trim that string down to constrain some decisions (e.g. the join order) but not others (e.g. the join methods), or (if you want to make your life more exciting) you can edit that advice string and thereby attempt to coerce the planner into planning the query the way you think best. There is a README that explains the design philosophy and thinking in a lot more detail, which is a good place to start if you're curious, and I implore you to read it if you're interested, and *especially* if you're thinking of flaming me. But that doesn't mean that you *shouldn't* flame me. There are a remarkable number of things that someone could legitimately be unhappy about in this patch set. First, any form of user control over the planner tends to be a lightning rod for criticism around here. I've come to believe that's the wrong way of thinking about it: we can want to improve the planner over the long term and *also* want to have tools available to work around problems with it in the short term. Further, we should not imagine that we're going to solve problems that have stumped other successful database projects any time in the foreseeable future; no product will ever get 100% of cases right, and you don't need to get to very obscure cases before other products throw up their hands just as we do. But, second, even if you're OK with the idea of some kind of user control over the planner, you could very well be of the opinion that what I've implemented here is utter crap. I've certainly had to make a ton of very opinionated decisions to get to this point, and you are entitled to hate them. Of course, I did have *reasons* for making the decisions, so if your operating theory as to why I did something is that I'm a stupid moron, perhaps consider an alternative explanation or two as well. Finally, even if you're OK with the concept and feel that I've made some basically reasonable design decisions, you might notice that the code is full of bugs, needs a lot of cleanup, is missing features, lacks documentation, and a bunch of other stuff. In that judgement, you would be absolutely correct. I'm not posting it here because I'm hoping to get it committed in November -- or at least, not THIS November. What I would like to do is getting some design feedback on the preliminary patches, which I think will be more possible if reviewers also have the main pg_plan_advice to look at as a way of understanding why the exist, and also some feedback on the pg_plan_advice patch itself. Now I do want to caveat the statement that I am looking for feedback just a little bit. I imagine that there will be some people reading this who are already imagining how great life will be when they put this into production, and begin complaining about either (1) features that it's missing or (2) things that they don't like about the design of the advice mini-language. What I'd ask you to keep in mind is that you will not be able to put this into production unless and until something gets committed, and getting this committed is probably going to be super-hard even if you don't creep the scope, so maybe don't do that, especially if you haven't read the README yet to understand what the scope is actually intended to be. The details of the advice mini-language are certainly open to negotiation; of everything, that would be one of the easier things to change. However, keep in mind that there are probably LOTS AND LOTS of people who all have their own opinions about what decisions I should have made when designing that mini-language, and an outcome where you personally get everything you want and everyone who disagrees is out of luck is unlikely. In other words, constructive suggestions for improvement are welcome, but please think twice before turning this into a bikeshedding nightmare. Now is the time to talk about whether I've got the overall design somewhat correct moreso than whether I've spelled everything the way you happen to prefer.[2] I want to mention that, beyond the fact that I'm sure some people will want to use something like this (with more feature and a lot fewer bugs) in production, it seems to be super-useful for testing. We have a lot of regression test cases that try to coerce the planner to do a particular thing by manipulating enable_* GUCs, and I've spent a lot of time trying to do similar things by hand, either for regression test coverage or just private testing. This facility, even with all of the bugs and limitations that it currently has, is exponentially more powerful than frobbing enable_* GUCs. Once you get the hang of the advice mini-language, you can very quickly experiment with all sorts of plan shapes in ways that are currently very hard to do, and thereby find out how expensive the planner thinks those things are and which ones it thinks are even legal. So I see this as not only something that people might find useful for in production deployments, but also something that can potentially be really useful to advance PostgreSQL development. Which brings me to the question of where this code ought to go if it goes anywhere at all. I decided to propose pg_plan_advice as a contrib module rather than a part of core because I had to make a WHOLE lot of opinionated design decisions just to get to the point of having something that I could post and hopefully get feedback on. I figured that all of those opinionated decisions would be a bit less unpalatable if they were mostly encapsulated in a contrib module, with the potential for some future patch author to write a different contrib module that adopted different solutions to all of those problems. But what I've also come to realize is that there's so much infrastructure here that leaving the next person to reinvent it may not be all that appealing. Query jumbling is a previous case where we initially thought that different people might want to do different things, but eventually realized that most people really just wanted some solution that they didn't have to think too hard about. Likewise, in this patch, the relation identifier system described in the README is the only thing of its kind, to my knowledge, and any system that wants to accomplish something similar to what pg_plan_advice does would need a system like that. pg_hint_plan doesn't have something like that, because pg_hint_plan is just trying to do hints. This is trying to do round-trip-safe plan stability, where the system will tell you how to refer unambiguously to a certain part of the query in a way that will work correctly on every single query regardless of how it's structured or how many times it refers to the same tables or to different tables using the same aliases. If we say that we're never going to put any of that infrastructure in core, then anyone who wants to write a module to control the planner is going to need to start by either (a) reinventing something similar, (b) cloning all the relevant code, or (c) just giving up on the idea of unambiguous references to parts of a query. None of those seem like great options, so now I'm less sure whether contrib is actually the right place for this code, but that's where I have put it for now. Feedback welcome, on this and everything else. Perhaps more than any other patch I've ever written, I know I'm playing with fire here just by putting this out on the list, but I'm nevertheless hopeful that something good can come of it, and I hope we can have a constructive discussion about what that thing should be. I think there is unquestionably is a lot of demand for the ability to influence the planner in some form, but there is a lot of room for debate about what exactly that should mean in practice. While I personally am pretty happy with the direction of the code I've written, modulo the large amount of not-yet-completed bug fixing and cleanup, there's certainly plenty of room for other people to feel differently, and finding out what other people think is, of course, the whole point of posting things publicly before committing them -- or in this case, before even finishing them.[3] If you're interested it contributing to the conversation, I urge you to start with the following things: (1) the README in the final patch; (2) the regression test examples in the final patch, which give a good sense of what it actually looks like to use this; and (3) the earlier patches, which show the minimum amount of core infrastructure that I think we need in order to make something like this workable (ideas on how to further reduce that footprint are very welcome). Thanks, -- Robert Haas EDB: http://www.enterprisedb.com [1] All of the earlier patches have been posted previously in some form, but the commit messages have been rewritten for clarity, and the "Allow for plugin control over path generation strategies" patch has been heavily rewritten since it was last posted; the earlier versions turned out to have substantial inadequacies. [2] This is not to say that proposal to modify or improve the syntax are unwelcome, but the bigger obstacle to getting something committed here is probably reaching some agreement on the internal details. Any changes to src/include/optimizer or src/backend/optimizer need careful scrutiny from a design perspective. Also, keep in mind that the syntax needs to fit what we can actually do: a proposal to change the syntax to something that implies semantics we can't implement is a dead letter. [3] Note, however, that a proposal to achieve the same or similar goals by different means is more welcome than a proposal that I should have done some other project entirely. I've already put a lot of work into these goals and hope to achieve them, at least to some degree, before I start working toward something else.
Вложения
- v1-0005-Allow-for-plugin-control-over-path-generation-str.patch
- v1-0003-Store-information-about-Append-node-consolidation.patch
- v1-0002-Store-information-about-elided-nodes-in-the-final.patch
- v1-0001-Store-information-about-range-table-flattening-in.patch
- v1-0004-Temporary-hack-to-unbreak-partitionwise-join-cont.patch
- v1-0006-WIP-Add-pg_plan_advice-contrib-module.patch
On Thu, Oct 30, 2025 at 3:00 PM Robert Haas <robertmhaas@gmail.com> wrote:
[..over 400kB of attachments, yay]
Thank You for working on this!
My gcc-13 was nitpicking a little bit (see
compilation_warnings_v1.txt), so attached is just a tiny diff to fix
some of those issues. After that, clang-20 run was clean too.
> First, any form of user control over the planner tends to be a lightning rod for criticism around here.
I do not know where this is coming from, but everybody I've talked to
was saying this is needed to handle real enterprise databases and
applications. I just really love it, how one could precisely adjust
the plan with this even with the presence of heavy aliasing:
postgres=# explain (plan_advice, costs off) SELECT * FROM (select *
from t1 a join t2 b using (id)) a, t2 b, t3 c WHERE a.id = b.id and
b.id = c.id;
QUERY PLAN
-----------------------------------------------------
Merge Join
Merge Cond: (a.id = c.id)
-> Merge Join
Merge Cond: (a.id = b.id)
-> Index Scan using t1_pkey on t1 a
-> Index Scan using t2_pkey on t2 b
-> Sort
Sort Key: c.id
-> Seq Scan on t3 c
Supplied Plan Advice:
SEQ_SCAN(ble5) /* not matched */
Generated Plan Advice:
JOIN_ORDER(a#2 b#2 c)
MERGE_JOIN_PLAIN(b#2 c)
SEQ_SCAN(c)
INDEX_SCAN(a#2 public.t1_pkey )
NO_GATHER(c a#2 b#2)
(17 rows)
postgres=# set pg_plan_advice.advice = 'SEQ_SCAN(b#2)';
SET
postgres=# explain (plan_advice, costs off) SELECT * FROM (select *
from t1 a join t2 b using (id)) a, t2 b, t3 c WHERE a.id = b.id and
b.id = c.id;
QUERY PLAN
----------------------------------------------------
Hash Join
Hash Cond: (b.id = a.id)
-> Seq Scan on t2 b
-> Hash
-> Merge Join
Merge Cond: (a.id = c.id)
-> Index Scan using t1_pkey on t1 a
-> Sort
Sort Key: c.id
-> Seq Scan on t3 c
Supplied Plan Advice:
SEQ_SCAN(b#2) /* matched */
Generated Plan Advice:
JOIN_ORDER(b#2 (a#2 c))
MERGE_JOIN_PLAIN(c)
HASH_JOIN(c)
SEQ_SCAN(b#2 c)
INDEX_SCAN(a#2 public.t1_pkey)
NO_GATHER(c a#2 b#2)
To attract a little attention to the $thread, the only bigger design
(usability) question that keeps ringing in my head is how we are going
to bind it to specific queries without even issuing any SETs(or ALTER
USER) in the far future in the grand scheme of things. The discussed
query id (hash), full query text comparison, maybe even strstr(query ,
"partial hit") or regex all seem to be kind too limited in terms of
what crazy ORMs can come up with (each query will be potentially
slightly different, but if optimizer reference points are stable that
should nail it good enough, but just enabling it for the very specific
set of queries and not the others [with same aliases] is some major
challenge).
Due to this, at some point I was even thinking about some hashes for
every plan node (including hashes of subplans), e.g.:
Merge Join // hash(MERGE_JOIN_PLAIN(b#2) + ';' somehashval1 + ';'+
somehahsval2 ) => somehashval3
Merge Cond: (a.id = c.id)
-> Merge Join
Merge Cond: (a.id = b.id)
-> Index Scan using t1_pkey on t1 a // hash(INDEX_SCAN(a#2
public.t1_pkey)) => somehashval1
-> Index Scan using t2_pkey on t2 b // hash(INDEX_SCAN(b#2
public.t2_pkey)) => somehashval2
and then having a way to use `somehashval3` (let's say it's SHA1) as a
way to activate the necessary advice. Something like having a way to
express it using plan_advice.on_subplanhashes_plan_advice =
'somehashval3: SEQ_SCAN(b#2)'. This would have the benefit of being
able to override multiple similiar SQL queries in one go rather than
collecting all possible query_ids, but probably it's stupid,
heavyweight, but that would be my dream ;)
-J.
Вложения
On Fri, Oct 31, 2025 at 5:59 AM Jakub Wartak <jakub.wartak@enterprisedb.com> wrote: > > First, any form of user control over the planner tends to be a lightning rod for criticism around here. > > I do not know where this is coming from, but everybody I've talked to > was saying this is needed to handle real enterprise databases and > applications. I just really love it, how one could precisely adjust > the plan with this even with the presence of heavy aliasing: Thanks for the kind words. I'll respond to the points about compiler warnings later. > To attract a little attention to the $thread, the only bigger design > (usability) question that keeps ringing in my head is how we are going > to bind it to specific queries without even issuing any SETs(or ALTER > USER) in the far future in the grand scheme of things. The discussed > query id (hash), full query text comparison, maybe even strstr(query , > "partial hit") or regex all seem to be kind too limited in terms of > what crazy ORMs can come up with (each query will be potentially > slightly different, but if optimizer reference points are stable that > should nail it good enough, but just enabling it for the very specific > set of queries and not the others [with same aliases] is some major > challenge). Yeah, I haven't really dealt with this problem yet. > Due to this, at some point I was even thinking about some hashes for > every plan node (including hashes of subplans), [...] > > and then having a way to use `somehashval3` (let's say it's SHA1) as a > way to activate the necessary advice. Something like having a way to This doesn't make sense to me, because it seems circular. We can't use anything in the plan to choose which advice string to use, because the purpose of the advice string is to influence the choice of plan. In other words, our choice of what advice string to use has to be based on the properties of the query, not the plan. We can implement anything we want to do in terms of exactly how that works: we can use the query ID, or the query text, or the query node tree. Hypothetically, we could call out to a user-defined function and pass the query text or the query node tree as an argument and let it do whatever it wants to decide on an advice string. The practical problem here is computational cost -- any computation that gets performed for every single query is going to have to be pretty cheap to avoid creating a performance problem. That's why I thought matching on query ID or exact matching on query text would likely be the most practical approaches, aside from the obvious alternative of setting and resetting pg_plan_advice.advice manually. But I haven't really explored this area too much yet, because I need to get all the basics working first. -- Robert Haas EDB: http://www.enterprisedb.com
On Fri, 31 Oct 2025, 12:51 Robert Haas, <robertmhaas@gmail.com> wrote:
On Fri, Oct 31, 2025 at 5:59 AM Jakub Wartak
<jakub.wartak@enterprisedb.com> wrote:
> > First, any form of user control over the planner tends to be a lightning rod for criticism around here.
>
> I do not know where this is coming from, but everybody I've talked to
> was saying this is needed to handle real enterprise databases and
> applications. I just really love it, how one could precisely adjust
> the plan with this even with the presence of heavy aliasing:
I really like the functionality of the current patch as well, even though I am suspicious of user control over the planner. By giving concise, precise control over a plan, this allows people who believe they can out-plan the planner to test their alternative, and possibly fail.
Whatever other UIs and integrations you build as you develop this towards you goal, please keep what's currently there user accessible. Not only for testing code, but also for testing users' belief that they know better.
Alastair
This weas recently shared in LinkedIn https://www.vldb.org/pvldb/vol18/p5126-bress.pdf For example it says that 31% of all queries are metadata queries, 78% have LIMIT, 20% of queries have 10+ joins, with 0.52% exceeding 100 joins. , 12% of expressions have depths between 11-100 levels, some exceeding 100. These deeply nested conditions create optimization challenges benchmarks don't capture.etc. This reinforces my belief thet we either should have some kind of two-level optimization, where most queries are handled quickly but with something to trigger a more elaborate optimisation and investigation workflow. Or alternatively we could just have an extra layer before the query is sent to the database which deals with unwinding the product of excessively stupid query generators (usually, but not always, some BI tools :) ) On Fri, Oct 31, 2025 at 10:18 PM Alastair Turner <minion@decodable.me> wrote: > > > On Fri, 31 Oct 2025, 12:51 Robert Haas, <robertmhaas@gmail.com> wrote: >> >> On Fri, Oct 31, 2025 at 5:59 AM Jakub Wartak >> <jakub.wartak@enterprisedb.com> wrote: >> > > First, any form of user control over the planner tends to be a lightning rod for criticism around here. >> > >> > I do not know where this is coming from, but everybody I've talked to >> > was saying this is needed to handle real enterprise databases and >> > applications. I just really love it, how one could precisely adjust >> > the plan with this even with the presence of heavy aliasing: > > > I really like the functionality of the current patch as well, even though I am suspicious of user control over the planner.By giving concise, precise control over a plan, this allows people who believe they can out-plan the planner to testtheir alternative, and possibly fail. > > Whatever other UIs and integrations you build as you develop this towards you goal, please keep what's currently thereuser accessible. Not only for testing code, but also for testing users' belief that they know better. > > Alastair
On Fri, Oct 31, 2025 at 5:17 PM Alastair Turner <minion@decodable.me> wrote: > I really like the functionality of the current patch as well, even though I am suspicious of user control over the planner.By giving concise, precise control over a plan, this allows people who believe they can out-plan the planner to testtheir alternative, and possibly fail. Indeed. The downside of letting people control anything is that they may leverage that control to do something bad. However, I think it is unlikely that very many people would prefer to write an entire query plan by hand. If you wanted to do that, why would you being using PostgreSQL in the first place? Furthermore, if somebody does try to do that, I expect that they will find it frustrating and difficult: the planner considers a large number of options even for simple queries and an absolutely vast number of options for more difficult queries, and a human being trying possibilities one by one is only ever going to consider a tiny fraction of those possibilities. The ideal possibility often won't be in that small subset of the search space, and the user will be wasting their time. If that were the design goal of this feature, I don't think it would be worth having. But it isn't. As I say in the README, what I consider the principal use case is reproducing plans that you know to have worked well in the past. Sometimes, the planner is correct for a while and then it's wrong later. We don't need to accept the proposition that users can out-plan the planner. We only need to accept that they can tell good plans from bad plans better than the planner. That is a low bar to clear. The planner never finds out what happens when the plans that it generates are actually executed, but users do. If they are sufficiently experienced, they can make reasonable judgements about whether the plan they're currently getting is one they'd like to continue getting. Of course, they may make wrong judgements even then, because they lack knowledge or experience or just make a mistake, but it's not a farcically unreasonable thing to do. I've basically never wanted to write my own query plan from scratch, but I've certainly looked at many plans over the years and judged them to be great, or terrible, or good for now but risky in the long-term; and I'm probably not the only human being on the planet capable of making such judgements with some degree of competence. > Whatever other UIs and integrations you build as you develop this towards you goal, please keep what's currently thereuser accessible. Not only for testing code, but also for testing users' belief that they know better. And this is also a good point. Knowledgeable and experienced users can look at a plan that the planner generated, feel like it's bad, and wonder why the planner picked it. You can try to figure that out by, for example, setting enable_SOMETHING = false and re-running EXPLAIN, but since there aren't that many such knobs relevant to any given query, and since changing any of those knobs can affect large swathes of the query and not just the part you're trying to understand better, it can actually be really difficult to understand why the planner thought that something was the best option. Sometimes you can't even tell whether the planner thinks that the plan you expected to be chosen is *impossible* or just *more expensive*, which is always one of the things that I'm keen to find out when something weird is happening. This can make answering that question a great deal easier. If some important index is not getting used, you can say "no, really, I want to see what happens with this query when you plan it with that index" -- and then it either gives you a plan that does use that index, and you can see how much more expensive it is and why, or it still doesn't give you a plan using that index, and you know that the index is inapplicable to the query or unusable in general for some reason. You don't necessarily have it as a goal to coerce the planner in production; your goal may very well be to find out why your belief that you know better is incorrect. -- Robert Haas EDB: http://www.enterprisedb.com
On Sat, Nov 1, 2025 at 12:10 PM Hannu Krosing <hannuk@google.com> wrote: > This reinforces my belief thet we either should have some kind of > two-level optimization, where most queries are handled quickly but > with something to trigger a more elaborate optimisation and > investigation workflow. > > Or alternatively we could just have an extra layer before the query is > sent to the database which deals with unwinding the product of > excessively stupid query generators (usually, but not always, some BI > tools :) ) I'd like to keep the focus of this thread on the patches that I'm proposing, rather than other ideas for improving the planner. I actually agree with you that at least the first of these things might be a very good idea, but that would be an entirely separate project from these patches, and I feel a lot more qualified to do this project than that one. -- Robert Haas EDB: http://www.enterprisedb.com