|
| 1 | +use crate::error::Error; |
| 2 | +use crate::query_as::query_as; |
| 3 | +use crate::sqlite::type_info::DataType; |
| 4 | +use crate::sqlite::{SqliteConnection, SqliteTypeInfo}; |
| 5 | +use hashbrown::HashMap; |
| 6 | + |
| 7 | +const OP_INIT: &str = "Init"; |
| 8 | +const OP_GOTO: &str = "Goto"; |
| 9 | +const OP_COLUMN: &str = "Column"; |
| 10 | +const OP_AGG_STEP: &str = "AggStep"; |
| 11 | +const OP_MOVE: &str = "Move"; |
| 12 | +const OP_COPY: &str = "Copy"; |
| 13 | +const OP_SCOPY: &str = "SCopy"; |
| 14 | +const OP_INT_COPY: &str = "IntCopy"; |
| 15 | +const OP_STRING8: &str = "String8"; |
| 16 | +const OP_INT64: &str = "Int64"; |
| 17 | +const OP_INTEGER: &str = "Integer"; |
| 18 | +const OP_REAL: &str = "Real"; |
| 19 | +const OP_NOT: &str = "Not"; |
| 20 | +const OP_BLOB: &str = "Blob"; |
| 21 | +const OP_COUNT: &str = "Count"; |
| 22 | +const OP_ROWID: &str = "Rowid"; |
| 23 | +const OP_OR: &str = "Or"; |
| 24 | +const OP_AND: &str = "And"; |
| 25 | +const OP_BIT_AND: &str = "BitAnd"; |
| 26 | +const OP_BIT_OR: &str = "BitOr"; |
| 27 | +const OP_SHIFT_LEFT: &str = "ShiftLeft"; |
| 28 | +const OP_SHIFT_RIGHT: &str = "ShiftRight"; |
| 29 | +const OP_ADD: &str = "Add"; |
| 30 | +const OP_SUBTRACT: &str = "Subtract"; |
| 31 | +const OP_MULTIPLY: &str = "Multiply"; |
| 32 | +const OP_DIVIDE: &str = "Divide"; |
| 33 | +const OP_REMAINDER: &str = "Remainder"; |
| 34 | +const OP_CONCAT: &str = "Concat"; |
| 35 | +const OP_RESULT_ROW: &str = "ResultRow"; |
| 36 | + |
| 37 | +fn to_type(op: &str) -> DataType { |
| 38 | + match op { |
| 39 | + OP_REAL => DataType::Float, |
| 40 | + OP_BLOB => DataType::Blob, |
| 41 | + OP_AND | OP_OR => DataType::Bool, |
| 42 | + OP_ROWID | OP_COUNT | OP_INT64 | OP_INTEGER => DataType::Int64, |
| 43 | + OP_STRING8 => DataType::Text, |
| 44 | + OP_COLUMN | _ => DataType::Null, |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +pub(super) async fn explain( |
| 49 | + conn: &mut SqliteConnection, |
| 50 | + query: &str, |
| 51 | +) -> Result<Vec<SqliteTypeInfo>, Error> { |
| 52 | + let mut r = HashMap::<i64, DataType>::with_capacity(6); |
| 53 | + |
| 54 | + let program = |
| 55 | + query_as::<_, (i64, String, i64, i64, i64, String)>(&*format!("EXPLAIN {}", query)) |
| 56 | + .fetch_all(&mut *conn) |
| 57 | + .await?; |
| 58 | + |
| 59 | + let mut program_i = 0; |
| 60 | + let program_size = program.len(); |
| 61 | + |
| 62 | + while program_i < program_size { |
| 63 | + let (_, ref opcode, p1, p2, p3, ref p4) = program[program_i]; |
| 64 | + |
| 65 | + match &**opcode { |
| 66 | + OP_INIT => { |
| 67 | + // start at <p2> |
| 68 | + program_i = p2 as usize; |
| 69 | + continue; |
| 70 | + } |
| 71 | + |
| 72 | + OP_GOTO => { |
| 73 | + // goto <p2> |
| 74 | + program_i = p2 as usize; |
| 75 | + continue; |
| 76 | + } |
| 77 | + |
| 78 | + OP_COLUMN => { |
| 79 | + // r[p3] = <value of column> |
| 80 | + r.insert(p3, DataType::Null); |
| 81 | + } |
| 82 | + |
| 83 | + OP_AGG_STEP => { |
| 84 | + if p4.starts_with("count(") { |
| 85 | + // count(_) -> INTEGER |
| 86 | + r.insert(p3, DataType::Int64); |
| 87 | + } else if let Some(v) = r.get(&p2).copied() { |
| 88 | + // r[p3] = AGG ( r[p2] ) |
| 89 | + r.insert(p3, v); |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + OP_COPY | OP_MOVE | OP_SCOPY | OP_INT_COPY => { |
| 94 | + // r[p2] = r[p1] |
| 95 | + if let Some(v) = r.get(&p1).copied() { |
| 96 | + r.insert(p2, v); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + OP_OR | OP_AND | OP_BLOB | OP_COUNT | OP_REAL | OP_STRING8 | OP_INTEGER | OP_ROWID => { |
| 101 | + // r[p2] = <value of constant> |
| 102 | + r.insert(p2, to_type(&opcode)); |
| 103 | + } |
| 104 | + |
| 105 | + OP_NOT => { |
| 106 | + // r[p2] = NOT r[p1] |
| 107 | + if let Some(a) = r.get(&p1).copied() { |
| 108 | + r.insert(p2, a); |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + OP_BIT_AND | OP_BIT_OR | OP_SHIFT_LEFT | OP_SHIFT_RIGHT | OP_ADD | OP_SUBTRACT |
| 113 | + | OP_MULTIPLY | OP_DIVIDE | OP_REMAINDER | OP_CONCAT => { |
| 114 | + // r[p3] = r[p1] + r[p2] |
| 115 | + match (r.get(&p1).copied(), r.get(&p2).copied()) { |
| 116 | + (Some(a), Some(b)) => { |
| 117 | + r.insert(p3, if matches!(a, DataType::Null) { b } else { a }); |
| 118 | + } |
| 119 | + |
| 120 | + (Some(v), None) => { |
| 121 | + r.insert(p3, v); |
| 122 | + } |
| 123 | + |
| 124 | + (None, Some(v)) => { |
| 125 | + r.insert(p3, v); |
| 126 | + } |
| 127 | + |
| 128 | + _ => {} |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + OP_RESULT_ROW => { |
| 133 | + // output = r[p1 .. p1 + p2] |
| 134 | + let mut output = Vec::with_capacity(p2 as usize); |
| 135 | + for i in p1..p1 + p2 { |
| 136 | + output.push(SqliteTypeInfo(r.remove(&i).unwrap_or(DataType::Null))); |
| 137 | + } |
| 138 | + |
| 139 | + return Ok(output); |
| 140 | + } |
| 141 | + |
| 142 | + _ => { |
| 143 | + // ignore unsupported operations |
| 144 | + // if we fail to find an r later, we just give up |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + program_i += 1; |
| 149 | + } |
| 150 | + |
| 151 | + // no rows |
| 152 | + Ok(vec![]) |
| 153 | +} |
0 commit comments