reddb_server/storage/query/
renderer.rs1use crate::storage::query::ast::{
14 CompareOp, FieldRef, Filter, InsertQuery, Projection, QueryExpr, QueueCommand, TableQuery,
15};
16use crate::storage::schema::Value;
17
18pub fn render(expr: &QueryExpr) -> String {
22 match expr {
23 QueryExpr::Table(tq) => render_table(tq),
24 QueryExpr::Insert(iq) => render_insert(iq),
25 QueryExpr::QueueCommand(qc) => render_queue_command(qc),
26 _ => String::new(),
27 }
28}
29
30fn render_table(tq: &TableQuery) -> String {
31 let cols = if tq.columns.is_empty() {
32 "*".to_string()
33 } else {
34 tq.columns
35 .iter()
36 .map(render_projection)
37 .collect::<Vec<_>>()
38 .join(", ")
39 };
40 let mut sql = format!("SELECT {} FROM {}", cols, tq.table);
41 if let Some(filter) = &tq.filter {
42 sql.push_str(" WHERE ");
43 sql.push_str(&render_filter(filter));
44 }
45 sql
46}
47
48fn render_insert(iq: &InsertQuery) -> String {
49 let cols = iq.columns.join(", ");
50 let rows: Vec<String> = iq
51 .values
52 .iter()
53 .map(|row| {
54 let vals = row
55 .iter()
56 .map(render_value_sql)
57 .collect::<Vec<_>>()
58 .join(", ");
59 format!("({})", vals)
60 })
61 .collect();
62 format!(
63 "INSERT INTO {} ({}) VALUES {}",
64 iq.table,
65 cols,
66 rows.join(", ")
67 )
68}
69
70fn render_queue_command(qc: &QueueCommand) -> String {
71 match qc {
72 QueueCommand::Push { queue, value, .. } => {
73 format!("QUEUE PUSH {} {}", queue, render_value_sql(value))
74 }
75 _ => String::new(),
76 }
77}
78
79fn render_projection(p: &Projection) -> String {
80 match p {
81 Projection::All => "*".to_string(),
82 Projection::Column(col) => col.clone(),
83 Projection::Alias(col, alias) => format!("{} AS {}", col, alias),
84 Projection::Field(field, alias) => {
85 let col = render_field_ref(field);
86 match alias {
87 Some(a) => format!("{} AS {}", col, a),
88 None => col,
89 }
90 }
91 _ => "*".to_string(),
92 }
93}
94
95pub(crate) fn render_field_ref(f: &FieldRef) -> String {
96 match f {
97 FieldRef::TableColumn { table, column } if table.is_empty() => column.clone(),
98 FieldRef::TableColumn { table, column } => format!("{}.{}", table, column),
99 _ => "field".to_string(),
100 }
101}
102
103fn render_filter(filter: &Filter) -> String {
104 match filter {
105 Filter::Compare { field, op, value } => {
106 format!(
107 "{} {} {}",
108 render_field_ref(field),
109 op,
110 render_value_sql(value)
111 )
112 }
113 Filter::And(a, b) => format!("({}) AND ({})", render_filter(a), render_filter(b)),
114 Filter::Or(a, b) => format!("({}) OR ({})", render_filter(a), render_filter(b)),
115 _ => "1=1".to_string(),
116 }
117}
118
119pub(crate) fn render_value_sql(v: &Value) -> String {
122 match v {
123 Value::Null => "NULL".to_string(),
124 Value::Integer(i) => i.to_string(),
125 Value::UnsignedInteger(u) => u.to_string(),
126 Value::Float(f) => {
127 if f.fract() == 0.0 {
129 format!("{:.1}", f)
130 } else {
131 format!("{}", f)
132 }
133 }
134 Value::Boolean(b) => {
135 if *b {
136 "true".to_string()
137 } else {
138 "false".to_string()
139 }
140 }
141 Value::Text(s) => format!("'{}'", s.replace('\'', "''")),
142 Value::Json(bytes) => String::from_utf8_lossy(bytes).to_string(),
145 _ => "NULL".to_string(),
146 }
147}