Hi, On 2022-11-23 20:28:29 -0500, Tom Lane wrote: > Andres Freund <andres@xxxxxxxxxxx> writes: > > On 2022-11-23 18:11:22 -0500, Tom Lane wrote: > >> Huh ... do you recall the details? Large as tab-complete is, it's > >> far smaller than gram.y: > > > So It might just be that we need to split up that very long "else if" chain in > > psql_completion(). > > Could be, yeah. It's not quite correct, but to test that I wrapped all the Matches("ALTER", ...) into a if (HeadMatches("ALTER")). That's enough to make the file compile without any other changes. The problem with that of course is that we then don't try the later completions if there's no match for any of the Matches("ALTER", ...). I think it'd be easier to deal with this if COMPLETE_WITH_* caused the the containing function to return. Then the completions wouldn't need to be in one huge if-else if. Leaving msvc aside, that also seems nice for efficiency. > Related point: I've wondered for awhile how long that > chain can get before we start noticing performance issues on slower > machines. Same. > Even when the standard is just human reaction time, there's > a limit to how many cycles you can throw away. So I'd like to see it > refactored somehow to be a bit less stupid --- but I have a nagging > feeling that we'd end up building some special-purpose program generator, > which is something I've not wanted to do. OTOH, in the best of all > possible worlds such a tool might make it easier to add tab > completions? > (In the past I've fantasized about auto-generating tab completion > logic from the backend grammar, but I fear it's just fantasy. > The backend grammar isn't factored right, and too many of its names > don't have clear traceability to what-kind-of-object-is-that.) I've thought about a grammar based approach as well, but as you say, it's not obvious how to make that work well. I wonder if we could model the completions as something roughly akin to a DFA. We don't even need to generate the real state machine at compile time, it'd be fine to do it at psql startup. But even just representing the current top-level conditions in an array, and then having a few obvious optimizations when matching the input to the array, should make it easy to beat the current approach. And it'd result in a much smaller amount of .text. There's a small number of else ifs that aren't just combinations of *Matches* conditions, e.g. stuff like !ends_with(), but those could be dealt via optional callbacks. I'm, very roughly, thinking of something like: compl_match_rule match_rules[] = { {.match = Matches("CREATE"), .compl_func = create_command_generator}, {.match = TailMatches("CREATE", "OR", "REPLACE"), .compl = {"FUNCTION", "..."}}, ... {.match = MatchAnd( HeadMatches("ALTER", "PUBLICATION", MatchAny), TailMatches("WHERE") ), .compl = {")"}}, ... } where of course Matches() etc wouldn't directly generate code, but evaluate to a literal struct with const char* members for the different options etc. I think this should make it easier to optimize evaluation. We e.g. could e.g. require that the Matches() rules are sorted, allowing to find the appropriate Matches() / HeadMatches() starting with the word we're trying to complete. Greetings, Andres Freund