Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
👀 |
| describe.anonymize(); | ||
| } | ||
| if let Some(describe) = describe { | ||
| return Ok(HandleResult::PrependProtocolMessageRewrite { |
There was a problem hiding this comment.
Q: why do we need this? I'm not too familiar with this piece of code, would be good to have a small example explaining.
| })) | ||
| } | ||
|
|
||
| fn internal_describe(&mut self, name: &str) -> Option<ProtocolMessage> { |
There was a problem hiding this comment.
Oh I see. The cross-shard Describe has different columns that direct-to-shard, so we need to rewrite it. I'm remembering now.
jkaczman
left a comment
There was a problem hiding this comment.
LGTM!
It would be great if you could document certain decisions e.g. why you chose to move from an explicit A_Const node with the limit + offset to an A_Expr executing on Postgres. I think I understand the tradeoffs, but it would be great to see the author's POV when reading the code.
I would also be curious to know if the benchmark from before vs after changed at all.
|
Benchmarked release builds of main and this PR using pgbench prepared protocol against the same three shards. Each case used a 3s warmup followed by three 15s runs at 10 and 50 clients. Results were effectively neutral: direct and cross-shard AVG ranged from −0.6% to +1.0%; comparable ORDER BY was ~1% slower (expected from post-route bookkeeping). CPU was unchanged (+0.13%), with ~476 KiB higher peak RSS. |
| pub(super) fn select( | ||
| &mut self, | ||
| cached_ast: &Ast, | ||
| _cached_ast: &Ast, |
There was a problem hiding this comment.
I think we're fine to remove this as it's no longer needed in this func
| } | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub(crate) enum HelperKind { | ||
| Count, |
There was a problem hiding this comment.
Why remove the comments that were originally in aggregate/plan.rs?
| @@ -0,0 +1,199 @@ | |||
| use pg_raw_parse::{ConstValue, Node, make, nodes}; | |||
|
|
|||
There was a problem hiding this comment.
It would be great to have some comments explaining some of the logic in this file.
| } | ||
|
|
||
| #[derive(Debug, Default, Clone)] | ||
| pub(crate) struct RewriteOutput { |
There was a problem hiding this comment.
It looks like this struct is just a basic wrapper around ProjectionRewritePlan. Why not just use ProjectionRewritePlan by itself?
| ) | ||
| } | ||
|
|
||
| pub(crate) fn aggregate_helpers(&self) -> &[AggregateHelper] { |
There was a problem hiding this comment.
These fields can be exposed pub(crate) and used directly if we're providing basic getters/setters without any special invariants enforced or anything.
| && projected.fields().into_iter().next_back().and_then(Node::as_str) | ||
| == name)) | ||
| }) | ||
| } |
There was a problem hiding this comment.
I think this will fail with something like SELECT p.* FROM products p ORDER BY price. Looks like it would work fine if it was SELECT * FROM products ORDER BY price or SELECT p.* FROM products p ORDER BY p.price, but not if we have a mixed case of one being qualified and the other not.
| pub(crate) fn projection_rewrite_plan(&self) -> &ProjectionRewritePlan { | ||
| &self.projection_rewrite_plan | ||
| } | ||
|
|
||
| pub(crate) fn set_rewrite_plan(&mut self, plan: AggregateRewritePlan) { | ||
| self.rewrite_plan = plan; | ||
| pub(crate) fn set_projection_rewrite_plan(&mut self, plan: ProjectionRewritePlan) { | ||
| self.projection_rewrite_plan = plan; |
There was a problem hiding this comment.
This is another case we could probably get rid of the getter/setter
| fn cross_shard_variant_name(name: &str) -> String { | ||
| format!("{name}_cross_shard") | ||
| } |
There was a problem hiding this comment.
I wonder if we could re-use the previous allocation somehow, here's a super simple example (probably not optimal):
buf = "x__pgdog_1"
cross_shard_variant = buf
base = buf[1..]
| continue; | ||
| } | ||
|
|
||
| let projected_column = select.target_list().len() + helpers.len(); |
There was a problem hiding this comment.
I think we shouldn't rely on this column index; it gives us the parse tree entries, but not the actual output columns (considering when * is used). I wonder if it would be better to use what you do in aggregate.rs again (decoder.row_description().field_index(&helper.alias) else {). Right now, I think there are edge cases where this can accidentally delete user columns / sort on the wrong column.
|
|
||
| #[test] | ||
| fn projects_column_not_covered_by_qualified_star() { | ||
| let (sql, plan) = rewrite("SELECT a.* FROM a JOIN b ON a.id = b.a_id ORDER BY b.score"); |
There was a problem hiding this comment.
This should also test the index (going off of my comment above)
Aggregate helpers, ORDER BY helpers, and LIMIT/OFFSET pushdown now run only after the route is known, so direct shard execution keeps original SQL and does not leak helper columns.
closes #1425 closes #1135