1#[cfg(feature = "oak-pretty-print")]
2use crate::ast::*;
3#[cfg(feature = "oak-pretty-print")]
4use oak_pretty_print::{AsDocument, Document, LINE as line, NIL as nil, SOFT_LINE_SPACE as soft_space, doc, indent};
5
6#[cfg(feature = "oak-pretty-print")]
7impl AsDocument for SqlRoot {
8 fn as_document(&self) -> Document<'_> {
9 Document::join(self.statements.iter().map(|it| it.as_document()), doc!(";", line))
10 }
11}
12
13#[cfg(feature = "oak-pretty-print")]
14impl AsDocument for SqlStatement {
15 fn as_document(&self) -> Document<'_> {
16 match self {
17 SqlStatement::Select(it) => it.as_document(),
18 SqlStatement::Insert(it) => it.as_document(),
19 SqlStatement::Update(it) => it.as_document(),
20 SqlStatement::Delete(it) => it.as_document(),
21 SqlStatement::Create(it) => it.as_document(),
22 SqlStatement::Drop(it) => it.as_document(),
23 SqlStatement::Alter(it) => it.as_document(),
24 SqlStatement::Error { .. } => nil,
25 SqlStatement::Unknown { .. } => nil,
26 }
27 }
28}
29
30#[cfg(feature = "oak-pretty-print")]
31impl AsDocument for SelectStatement {
32 fn as_document(&self) -> Document<'_> {
33 let mut parts = Vec::new();
34 parts.push(Document::text("SELECT"));
35
36 let items = Document::join(self.items.iter().map(|it| it.as_document()), doc!(",", soft_space));
37 parts.push(indent(doc!(line, items)));
38
39 if let Some(from) = &self.from {
40 parts.push(line);
41 parts.push(Document::text("FROM"));
42 parts.push(soft_space);
43 parts.push(from.as_document());
44 }
45
46 for join in &self.joins {
47 parts.push(line);
48 parts.push(join.as_document());
49 }
50
51 if let Some(expr) = &self.expr {
52 parts.push(line);
53 parts.push(Document::text("WHERE"));
54 parts.push(soft_space);
55 parts.push(expr.as_document());
56 }
57
58 if let Some(group_by) = &self.group_by {
59 parts.push(line);
60 parts.push(group_by.as_document());
61 }
62
63 if let Some(having) = &self.having {
64 parts.push(line);
65 parts.push(having.as_document());
66 }
67
68 if let Some(order_by) = &self.order_by {
69 parts.push(line);
70 parts.push(order_by.as_document());
71 }
72
73 if let Some(limit) = &self.limit {
74 parts.push(line);
75 parts.push(limit.as_document());
76 }
77
78 Document::group(Document::Concat(parts))
79 }
80}
81
82#[cfg(feature = "oak-pretty-print")]
83impl AsDocument for SelectItem {
84 fn as_document(&self) -> Document<'_> {
85 match self {
86 SelectItem::Star { .. } => Document::text("*"),
87 SelectItem::Expression { expr, alias, .. } => {
88 if let Some(alias) = alias {
89 doc!(expr.as_document(), soft_space, "AS", soft_space, alias.as_document())
90 }
91 else {
92 expr.as_document()
93 }
94 }
95 }
96 }
97}
98
99#[cfg(feature = "oak-pretty-print")]
100impl AsDocument for Expression {
101 fn as_document(&self) -> Document<'_> {
102 match self {
103 Expression::Identifier(it) => it.as_document(),
104 Expression::Literal(it) => it.as_document(),
105 Expression::Binary { left, op, right, .. } => {
106 doc!(left.as_document(), soft_space, op.as_document(), soft_space, right.as_document())
107 }
108 Expression::Unary { op, expr, .. } => {
109 doc!(op.as_document(), expr.as_document())
110 }
111 Expression::FunctionCall { name, args, .. } => {
112 doc!(name.as_document(), "(", Document::join(args.iter().map(|it| it.as_document()), doc!(",", soft_space)), ")")
113 }
114 Expression::InList { expr, list, negated, .. } => {
115 doc!(expr.as_document(), if *negated { doc!(soft_space, "NOT") } else { nil }, soft_space, "IN", soft_space, "(", Document::join(list.iter().map(|it| it.as_document()), doc!(",", soft_space)), ")")
116 }
117 Expression::Between { expr, low, high, negated, .. } => {
118 doc!(expr.as_document(), if *negated { doc!(soft_space, "NOT") } else { nil }, soft_space, "BETWEEN", soft_space, low.as_document(), soft_space, "AND", soft_space, high.as_document())
119 }
120 Expression::Subquery { query, .. } => {
121 doc!("(", query.as_document(), ")")
122 }
123 Expression::InSubquery { expr, query: subquery, negated, .. } => {
124 doc!(expr.as_document(), if *negated { doc!(soft_space, "NOT") } else { nil }, soft_space, "IN", soft_space, "(", subquery.as_document(), ")")
125 }
126 Expression::Error { .. } => nil,
127 }
128 }
129}
130
131#[cfg(feature = "oak-pretty-print")]
132impl AsDocument for BinaryOperator {
133 fn as_document(&self) -> Document<'_> {
134 match self {
135 BinaryOperator::Plus => Document::text("+"),
136 BinaryOperator::Minus => Document::text("-"),
137 BinaryOperator::Star => Document::text("*"),
138 BinaryOperator::Slash => Document::text("/"),
139 BinaryOperator::Percent => Document::text("%"),
140 BinaryOperator::And => Document::text("AND"),
141 BinaryOperator::Or => Document::text("OR"),
142 BinaryOperator::Equal => Document::text("="),
143 BinaryOperator::NotEqual => Document::text("<>"),
144 BinaryOperator::Less => Document::text("<"),
145 BinaryOperator::Greater => Document::text(">"),
146 BinaryOperator::LessEqual => Document::text("<="),
147 BinaryOperator::GreaterEqual => Document::text(">="),
148 BinaryOperator::Like => Document::text("LIKE"),
149 }
150 }
151}
152
153#[cfg(feature = "oak-pretty-print")]
154impl AsDocument for UnaryOperator {
155 fn as_document(&self) -> Document<'_> {
156 match self {
157 UnaryOperator::Plus => Document::text("+"),
158 UnaryOperator::Minus => Document::text("-"),
159 UnaryOperator::Not => Document::text("NOT"),
160 }
161 }
162}
163
164#[cfg(feature = "oak-pretty-print")]
165impl AsDocument for Literal {
166 fn as_document(&self) -> Document<'_> {
167 match self {
168 Literal::Number(n, _) => Document::text(n.as_ref()),
169 Literal::String(s, _) => doc!("'", s.as_ref(), "'"),
170 Literal::Boolean(b, _) => Document::text(if *b { "TRUE" } else { "FALSE" }),
171 Literal::Null(_) => Document::text("NULL"),
172 }
173 }
174}
175
176#[cfg(feature = "oak-pretty-print")]
177impl AsDocument for Identifier {
178 fn as_document(&self) -> Document<'_> {
179 Document::text(self.name.as_ref())
180 }
181}
182
183#[cfg(feature = "oak-pretty-print")]
184impl AsDocument for TableName {
185 fn as_document(&self) -> Document<'_> {
186 self.name.as_document()
187 }
188}
189
190#[cfg(feature = "oak-pretty-print")]
191impl AsDocument for ColumnName {
192 fn as_document(&self) -> Document<'_> {
193 self.name.as_document()
194 }
195}
196
197#[cfg(feature = "oak-pretty-print")]
198impl AsDocument for JoinClause {
199 fn as_document(&self) -> Document<'_> {
200 doc!(self.join_type.as_document(), soft_space, "JOIN", soft_space, self.table.as_document(), if let Some(on) = &self.on { doc!(soft_space, "ON", soft_space, on.as_document()) } else { nil })
201 }
202}
203
204#[cfg(feature = "oak-pretty-print")]
205impl AsDocument for JoinType {
206 fn as_document(&self) -> Document<'_> {
207 match self {
208 JoinType::Inner => Document::text("INNER"),
209 JoinType::Left => Document::text("LEFT"),
210 JoinType::Right => Document::text("RIGHT"),
211 JoinType::Full => Document::text("FULL"),
212 }
213 }
214}
215
216#[cfg(feature = "oak-pretty-print")]
217impl AsDocument for GroupByClause {
218 fn as_document(&self) -> Document<'_> {
219 doc!("GROUP", soft_space, "BY", soft_space, Document::join(self.columns.iter().map(|it| it.as_document()), doc!(",", soft_space)))
220 }
221}
222
223#[cfg(feature = "oak-pretty-print")]
224impl AsDocument for HavingClause {
225 fn as_document(&self) -> Document<'_> {
226 doc!("HAVING", soft_space, self.condition.as_document())
227 }
228}
229
230#[cfg(feature = "oak-pretty-print")]
231impl AsDocument for OrderByClause {
232 fn as_document(&self) -> Document<'_> {
233 doc!("ORDER", soft_space, "BY", soft_space, Document::join(self.items.iter().map(|it| it.as_document()), doc!(",", soft_space)))
234 }
235}
236
237#[cfg(feature = "oak-pretty-print")]
238impl AsDocument for OrderByItem {
239 fn as_document(&self) -> Document<'_> {
240 doc!(self.expr.as_document(), soft_space, self.direction.as_document())
241 }
242}
243
244#[cfg(feature = "oak-pretty-print")]
245impl AsDocument for OrderDirection {
246 fn as_document(&self) -> Document<'_> {
247 match self {
248 OrderDirection::Asc => Document::text("ASC"),
249 OrderDirection::Desc => Document::text("DESC"),
250 }
251 }
252}
253
254#[cfg(feature = "oak-pretty-print")]
255impl AsDocument for LimitClause {
256 fn as_document(&self) -> Document<'_> {
257 doc!("LIMIT", soft_space, self.limit.as_document(), if let Some(offset) = &self.offset { doc!(soft_space, "OFFSET", soft_space, offset.as_document()) } else { nil })
258 }
259}
260
261#[cfg(feature = "oak-pretty-print")]
262impl AsDocument for CreateStatement {
263 fn as_document(&self) -> Document<'_> {
264 let mut parts = Vec::new();
265 parts.push(Document::text("CREATE"));
266 if let CreateBody::Index { unique: true, .. } = &self.body {
267 parts.push(soft_space);
268 parts.push(Document::text("UNIQUE"));
269 }
270 parts.push(soft_space);
271 parts.push(self.object_type.as_document());
272 if self.if_not_exists {
273 parts.push(soft_space);
274 parts.push(Document::text("IF NOT EXISTS"));
275 }
276 parts.push(soft_space);
277 parts.push(self.name.as_document());
278
279 match &self.body {
280 CreateBody::Table { columns, .. } => {
281 if !columns.is_empty() {
282 parts.push(soft_space);
283 parts.push(Document::text("("));
284 let cols = Document::join(columns.iter().map(|it| it.as_document()), doc!(",", line));
285 parts.push(indent(doc!(line, cols)));
286 parts.push(line);
287 parts.push(Document::text(")"));
288 }
289 }
290 CreateBody::View { query, .. } => {
291 parts.push(line);
292 parts.push(Document::text("AS"));
293 parts.push(soft_space);
294 parts.push(query.as_document());
295 }
296 CreateBody::Index { table_name, columns, unique: _, .. } => {
297 parts.push(soft_space);
298 parts.push(Document::text("ON"));
299 parts.push(soft_space);
300 parts.push(table_name.as_document());
301 parts.push(soft_space);
302 parts.push(Document::text("("));
303 let cols = Document::join(columns.iter().map(|it| it.as_document()), doc!(",", soft_space));
304 parts.push(cols);
305 parts.push(Document::text(")"));
306 }
307 CreateBody::Database { .. } => {}
308 }
309
310 Document::group(Document::Concat(parts))
311 }
312}
313
314#[cfg(feature = "oak-pretty-print")]
315impl AsDocument for ColumnDefinition {
316 fn as_document(&self) -> Document<'_> {
317 doc!(self.name.as_document(), soft_space, self.data_type.clone(), Document::join(self.constraints.iter().map(|it| doc!(soft_space, it.as_document())), nil))
318 }
319}
320
321#[cfg(feature = "oak-pretty-print")]
322impl AsDocument for ColumnConstraint {
323 fn as_document(&self) -> Document<'_> {
324 match self {
325 ColumnConstraint::PrimaryKey { .. } => Document::text("PRIMARY KEY"),
326 ColumnConstraint::NotNull { .. } => Document::text("NOT NULL"),
327 ColumnConstraint::Nullable { .. } => Document::text("NULL"),
328 ColumnConstraint::Unique { .. } => Document::text("UNIQUE"),
329 ColumnConstraint::Default(expr, _) => doc!("DEFAULT", soft_space, expr.as_document()),
330 ColumnConstraint::Check(expr, _) => doc!("CHECK", soft_space, "(", expr.as_document(), ")"),
331 ColumnConstraint::AutoIncrement { .. } => Document::text("AUTOINCREMENT"),
332 }
333 }
334}
335
336#[cfg(feature = "oak-pretty-print")]
337impl AsDocument for CreateObjectType {
338 fn as_document(&self) -> Document<'_> {
339 match self {
340 CreateObjectType::Table => Document::text("TABLE"),
341 CreateObjectType::View => Document::text("VIEW"),
342 CreateObjectType::Index => Document::text("INDEX"),
343 CreateObjectType::Database => Document::text("DATABASE"),
344 }
345 }
346}
347
348#[cfg(feature = "oak-pretty-print")]
349impl AsDocument for InsertStatement {
350 fn as_document(&self) -> Document<'_> {
351 let mut parts = Vec::new();
352 parts.push(Document::text("INSERT INTO"));
353 parts.push(soft_space);
354 parts.push(self.table_name.as_document());
355
356 if !self.columns.is_empty() {
357 parts.push(soft_space);
358 parts.push(Document::text("("));
359 parts.push(Document::join(self.columns.iter().map(|it| it.as_document()), doc!(",", soft_space)));
360 parts.push(Document::text(")"));
361 }
362
363 parts.push(line);
364 parts.push(Document::text("VALUES"));
365 parts.push(soft_space);
366 parts.push(Document::text("("));
367 parts.push(Document::join(self.values.iter().map(|it| it.as_document()), doc!(",", soft_space)));
368 parts.push(Document::text(")"));
369
370 Document::group(Document::Concat(parts))
371 }
372}
373
374#[cfg(feature = "oak-pretty-print")]
375impl AsDocument for UpdateStatement {
376 fn as_document(&self) -> Document<'_> {
377 let mut parts = Vec::new();
378 parts.push(Document::text("UPDATE"));
379 parts.push(soft_space);
380 parts.push(self.table_name.as_document());
381 parts.push(line);
382 parts.push(Document::text("SET"));
383 parts.push(soft_space);
384 parts.push(Document::join(self.assignments.iter().map(|it| it.as_document()), doc!(",", line)));
385
386 if let Some(selection) = &self.selection {
387 parts.push(line);
388 parts.push(Document::text("WHERE"));
389 parts.push(soft_space);
390 parts.push(selection.as_document());
391 }
392
393 Document::group(Document::Concat(parts))
394 }
395}
396
397#[cfg(feature = "oak-pretty-print")]
398impl AsDocument for Assignment {
399 fn as_document(&self) -> Document<'_> {
400 doc!(self.column.as_document(), soft_space, "=", soft_space, self.value.as_document())
401 }
402}
403
404#[cfg(feature = "oak-pretty-print")]
405impl AsDocument for DeleteStatement {
406 fn as_document(&self) -> Document<'_> {
407 let mut parts = Vec::new();
408 parts.push(Document::text("DELETE FROM"));
409 parts.push(soft_space);
410 parts.push(self.table_name.as_document());
411
412 if let Some(selection) = &self.selection {
413 parts.push(line);
414 parts.push(Document::text("WHERE"));
415 parts.push(soft_space);
416 parts.push(selection.as_document());
417 }
418
419 Document::group(Document::Concat(parts))
420 }
421}
422
423#[cfg(feature = "oak-pretty-print")]
424impl AsDocument for DropStatement {
425 fn as_document(&self) -> Document<'_> {
426 doc!("DROP", soft_space, self.object_type.as_document(), if self.if_exists { doc!(soft_space, "IF EXISTS") } else { nil }, soft_space, self.name.as_document())
427 }
428}
429
430#[cfg(feature = "oak-pretty-print")]
431impl AsDocument for DropObjectType {
432 fn as_document(&self) -> Document<'_> {
433 match self {
434 DropObjectType::Table => Document::text("TABLE"),
435 DropObjectType::View => Document::text("VIEW"),
436 DropObjectType::Index => Document::text("INDEX"),
437 DropObjectType::Database => Document::text("DATABASE"),
438 }
439 }
440}
441
442#[cfg(feature = "oak-pretty-print")]
443impl AsDocument for AlterStatement {
444 fn as_document(&self) -> Document<'_> {
445 let mut d = doc!("ALTER TABLE", soft_space, self.table_name.as_document());
446 if let Some(action) = &self.action {
447 d = doc!(d, soft_space, action.as_document());
448 }
449 d
450 }
451}
452
453#[cfg(feature = "oak-pretty-print")]
454impl AsDocument for AlterAction {
455 fn as_document(&self) -> Document<'_> {
456 match self {
457 AlterAction::AddColumn { name, data_type, .. } => {
458 let mut d = doc!("ADD COLUMN", soft_space, name.as_document());
459 if let Some(dt) = data_type {
460 d = doc!(d, soft_space, dt.clone());
461 }
462 d
463 }
464 AlterAction::DropColumn { name, .. } => {
465 doc!("DROP COLUMN", soft_space, name.as_document())
466 }
467 AlterAction::RenameTo { new_name, .. } => {
468 doc!("RENAME TO", soft_space, new_name.as_document())
469 }
470 }
471 }
472}