gitql_engine/
engine_join.rs

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::collections::HashMap;

use gitql_ast::statement::Join;
use gitql_ast::statement::JoinKind;
use gitql_ast::statement::JoinOperand;
use gitql_ast::statement::TableSelection;
use gitql_core::environment::Environment;
use gitql_core::object::Row;
use gitql_core::values::base::Value;
use gitql_core::values::boolean::BoolValue;
use gitql_core::values::null::NullValue;

use crate::engine_evaluator::evaluate_expression;

#[inline(always)]
pub(crate) fn apply_join_operation(
    env: &mut Environment,
    all_rows: &mut Vec<Row>,
    joins: &Vec<Join>,
    tables_selections: &Vec<TableSelection>,
    selected_rows_per_table: &mut HashMap<String, Vec<Row>>,
    hidden_selection_per_table: &HashMap<String, usize>,
    titles: &[String],
) -> Result<(), String> {
    // If no join, just merge them, can be optimized to append only the first value in the map
    if joins.is_empty() {
        for table_selection in tables_selections {
            let table_rows = selected_rows_per_table
                .get_mut(&table_selection.table_name)
                .unwrap();
            all_rows.append(table_rows);
        }
        return Ok(());
    }

    let mut current_tables_rows: Vec<Row> = vec![];
    let mut all_rows_hidden_count = 0;

    // Apply join operator depend on the join type
    for join in joins {
        let mut current_join_rows: Vec<Row> = vec![];

        let left_rows: &Vec<Row>;
        let left_hidden_count: usize;

        let right_rows: &Vec<Row>;
        let right_hidden_count: usize;

        match &join.operand {
            JoinOperand::OuterAndInner(outer, inner) => {
                left_hidden_count = *hidden_selection_per_table.get(outer).unwrap_or(&0);
                right_hidden_count = *hidden_selection_per_table.get(inner).unwrap_or(&0);
                all_rows_hidden_count += left_hidden_count + right_hidden_count;

                left_rows = selected_rows_per_table.get(outer).unwrap();
                right_rows = selected_rows_per_table.get(inner).unwrap();
            }

            JoinOperand::Inner(inner) => {
                left_hidden_count = all_rows_hidden_count;
                right_hidden_count = *hidden_selection_per_table.get(inner).unwrap_or(&0);
                all_rows_hidden_count += right_hidden_count;

                left_rows = &current_tables_rows;
                right_rows = selected_rows_per_table.get(inner).unwrap();
            }
        }

        // Don't apply CROSS JOIN if left or right rows are empty
        if join.kind == JoinKind::Cross && (left_rows.is_empty() || right_rows.is_empty()) {
            continue;
        }

        // Perform nested loops straight forward join algorithm
        for outer in left_rows {
            for inner in right_rows {
                let row_len = outer.values.len() + inner.values.len();
                let mut joined_row: Vec<Box<dyn Value>> = Vec::with_capacity(row_len);
                joined_row.append(&mut outer.values.clone());

                let inner_rows = inner.values.clone();
                let inner_hidden_values = &inner_rows[0..right_hidden_count];
                joined_row.splice(
                    left_hidden_count..left_hidden_count,
                    inner_hidden_values.to_vec(),
                );

                let inner_other_values = &inner_rows[right_hidden_count..];
                joined_row.extend_from_slice(inner_other_values);

                // If join has predicate, insert the joined row only if the predicate value is true
                if let Some(predicate) = &join.predicate {
                    let predicate_value = evaluate_expression(env, predicate, titles, &joined_row)?;
                    if let Some(bool_value) = predicate_value.as_any().downcast_ref::<BoolValue>() {
                        if bool_value.value {
                            current_join_rows.push(Row { values: joined_row });
                            continue;
                        }
                    }

                    // For LEFT and RIGHT Join only if the predicate is false we need to create new joined row
                    // The new joined row will have nulls as LEFT table row values if the join type is `RIGHT OUTER` or
                    // Nulls as RGIHT table row values if the join type is `LEFT OUTER`
                    match join.kind {
                        JoinKind::Left => {
                            let mut left_joined_row: Vec<Box<dyn Value>> =
                                Vec::with_capacity(row_len);
                            // Push the LEFT values row
                            left_joined_row.append(&mut outer.values.clone());
                            // Push (N * NULL) values as RIGHT values row
                            for _ in 0..inner.values.len() {
                                left_joined_row.push(Box::new(NullValue));
                            }
                        }
                        JoinKind::Right => {
                            let mut right_joined_row: Vec<Box<dyn Value>> =
                                Vec::with_capacity(row_len);
                            // Push (N * NULL) values as LEFT values row
                            for _ in 0..outer.values.len() {
                                right_joined_row.push(Box::new(NullValue));
                            }
                            // Push the RIGHT values row
                            right_joined_row.append(&mut inner.values.clone());
                        }
                        _ => {}
                    }
                    continue;
                }

                // If the condition has no predicate, just insert it
                current_join_rows.push(Row { values: joined_row });
            }
        }

        // Clear the previous join rows if exists
        current_tables_rows.clear();
        // Set the current tables rows as the result of the join
        current_tables_rows.append(&mut current_join_rows);
    }

    // Push the result to the all_rows ref
    all_rows.append(&mut current_tables_rows);

    Ok(())
}