1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#[derive(serde::Deserialize, Debug)]
#[serde(untagged)]
pub(crate) enum Explain {
// NOTE: the returned JSON may not contain a `plan` field, for example, with `CALL` statements:
// https://github.com/launchbadge/sqlx/issues/1449
//
// In this case, we should just fall back to assuming all is nullable.
//
// It may also contain additional fields we don't care about, which should not break parsing:
// https://github.com/launchbadge/sqlx/issues/2587
// https://github.com/launchbadge/sqlx/issues/2622
Plan {
#[serde(rename = "Plan")]
plan: Plan,
},
// This ensures that parsing never technically fails.
//
// We don't want to specifically expect `"Utility Statement"` because there might be other cases
// and we don't care unless it contains a query plan anyway.
Other(serde::de::IgnoredAny),
}
#[derive(serde::Deserialize, Debug)]
pub(crate) struct Plan {
#[serde(rename = "Join Type")]
pub(crate) join_type: Option<String>,
#[serde(rename = "Parent Relationship")]
pub(crate) parent_relation: Option<String>,
#[serde(rename = "Output")]
pub(crate) output: Option<Vec<String>>,
#[serde(rename = "Plans")]
pub(crate) plans: Option<Vec<Plan>>,
}
pub(crate) fn visit_plan(plan: &Plan, outputs: &[String], nullables: &mut Vec<Option<bool>>) {
if let Some(plan_outputs) = &plan.output {
// all outputs of a Full Join must be marked nullable
// otherwise, all outputs of the inner half of an outer join must be marked nullable
if plan.join_type.as_deref() == Some("Full") || plan.parent_relation.as_deref() == Some("Inner") {
for output in plan_outputs {
if let Some(i) = outputs.iter().position(|o| o == output) {
// N.B. this may produce false positives but those don't cause runtime errors
nullables[i] = Some(true);
}
}
}
}
if let Some(plans) = &plan.plans
&& matches!(plan.join_type.as_deref(), Some("Left" | "Right"))
{
for plan in plans {
visit_plan(plan, outputs, nullables);
}
}
}