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 Expression::Vector { elements, .. } => {
128 doc!("[", Document::join(elements.iter().map(|it| it.as_document()), doc!(",", soft_space)), "]")
129 }
130 }
131 }
132}
133
134#[cfg(feature = "oak-pretty-print")]
135impl AsDocument for BinaryOperator {
136 fn as_document(&self) -> Document<'_> {
137 match self {
138 BinaryOperator::Plus => Document::text("+"),
139 BinaryOperator::Minus => Document::text("-"),
140 BinaryOperator::Star => Document::text("*"),
141 BinaryOperator::Slash => Document::text("/"),
142 BinaryOperator::Percent => Document::text("%"),
143 BinaryOperator::And => Document::text("AND"),
144 BinaryOperator::Or => Document::text("OR"),
145 BinaryOperator::Equal => Document::text("="),
146 BinaryOperator::NotEqual => Document::text("<>"),
147 BinaryOperator::Less => Document::text("<"),
148 BinaryOperator::Greater => Document::text(">"),
149 BinaryOperator::LessEqual => Document::text("<="),
150 BinaryOperator::GreaterEqual => Document::text(">="),
151 BinaryOperator::Like => Document::text("LIKE"),
152 }
153 }
154}
155
156#[cfg(feature = "oak-pretty-print")]
157impl AsDocument for UnaryOperator {
158 fn as_document(&self) -> Document<'_> {
159 match self {
160 UnaryOperator::Plus => Document::text("+"),
161 UnaryOperator::Minus => Document::text("-"),
162 UnaryOperator::Not => Document::text("NOT"),
163 }
164 }
165}
166
167#[cfg(feature = "oak-pretty-print")]
168impl AsDocument for Literal {
169 fn as_document(&self) -> Document<'_> {
170 match self {
171 Literal::Number(n, _) => Document::text(n.as_ref()),
172 Literal::String(s, _) => doc!("'", s.as_ref(), "'"),
173 Literal::Boolean(b, _) => Document::text(if *b { "TRUE" } else { "FALSE" }),
174 Literal::Null(_) => Document::text("NULL"),
175 }
176 }
177}
178
179#[cfg(feature = "oak-pretty-print")]
180impl AsDocument for Identifier {
181 fn as_document(&self) -> Document<'_> {
182 Document::text(self.name.as_ref())
183 }
184}
185
186#[cfg(feature = "oak-pretty-print")]
187impl AsDocument for TableName {
188 fn as_document(&self) -> Document<'_> {
189 self.name.as_document()
190 }
191}
192
193#[cfg(feature = "oak-pretty-print")]
194impl AsDocument for ColumnName {
195 fn as_document(&self) -> Document<'_> {
196 self.name.as_document()
197 }
198}
199
200#[cfg(feature = "oak-pretty-print")]
201impl AsDocument for JoinClause {
202 fn as_document(&self) -> Document<'_> {
203 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 })
204 }
205}
206
207#[cfg(feature = "oak-pretty-print")]
208impl AsDocument for JoinType {
209 fn as_document(&self) -> Document<'_> {
210 match self {
211 JoinType::Inner => Document::text("INNER"),
212 JoinType::Left => Document::text("LEFT"),
213 JoinType::Right => Document::text("RIGHT"),
214 JoinType::Full => Document::text("FULL"),
215 }
216 }
217}
218
219#[cfg(feature = "oak-pretty-print")]
220impl AsDocument for GroupByClause {
221 fn as_document(&self) -> Document<'_> {
222 doc!("GROUP", soft_space, "BY", soft_space, Document::join(self.columns.iter().map(|it| it.as_document()), doc!(",", soft_space)))
223 }
224}
225
226#[cfg(feature = "oak-pretty-print")]
227impl AsDocument for HavingClause {
228 fn as_document(&self) -> Document<'_> {
229 doc!("HAVING", soft_space, self.condition.as_document())
230 }
231}
232
233#[cfg(feature = "oak-pretty-print")]
234impl AsDocument for OrderByClause {
235 fn as_document(&self) -> Document<'_> {
236 doc!("ORDER", soft_space, "BY", soft_space, Document::join(self.items.iter().map(|it| it.as_document()), doc!(",", soft_space)))
237 }
238}
239
240#[cfg(feature = "oak-pretty-print")]
241impl AsDocument for OrderByItem {
242 fn as_document(&self) -> Document<'_> {
243 doc!(self.expr.as_document(), soft_space, self.direction.as_document())
244 }
245}
246
247#[cfg(feature = "oak-pretty-print")]
248impl AsDocument for OrderDirection {
249 fn as_document(&self) -> Document<'_> {
250 match self {
251 OrderDirection::Asc => Document::text("ASC"),
252 OrderDirection::Desc => Document::text("DESC"),
253 }
254 }
255}
256
257#[cfg(feature = "oak-pretty-print")]
258impl AsDocument for LimitClause {
259 fn as_document(&self) -> Document<'_> {
260 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 })
261 }
262}
263
264#[cfg(feature = "oak-pretty-print")]
265impl AsDocument for CreateStatement {
266 fn as_document(&self) -> Document<'_> {
267 let mut parts = Vec::new();
268 parts.push(Document::text("CREATE"));
269 if let CreateBody::Index { unique: true, .. } = &self.body {
270 parts.push(soft_space);
271 parts.push(Document::text("UNIQUE"));
272 }
273 parts.push(soft_space);
274 parts.push(self.object_type.as_document());
275 if self.if_not_exists {
276 parts.push(soft_space);
277 parts.push(Document::text("IF NOT EXISTS"));
278 }
279 parts.push(soft_space);
280 parts.push(self.name.as_document());
281
282 match &self.body {
283 CreateBody::Table { columns, .. } => {
284 if !columns.is_empty() {
285 parts.push(soft_space);
286 parts.push(Document::text("("));
287 let cols = Document::join(columns.iter().map(|it| it.as_document()), doc!(",", line));
288 parts.push(indent(doc!(line, cols)));
289 parts.push(line);
290 parts.push(Document::text(")"));
291 }
292 }
293 CreateBody::View { query, .. } => {
294 parts.push(line);
295 parts.push(Document::text("AS"));
296 parts.push(soft_space);
297 parts.push(query.as_document());
298 }
299 CreateBody::Index { table_name, columns, unique: _, .. } => {
300 parts.push(soft_space);
301 parts.push(Document::text("ON"));
302 parts.push(soft_space);
303 parts.push(table_name.as_document());
304 parts.push(soft_space);
305 parts.push(Document::text("("));
306 let cols = Document::join(columns.iter().map(|it| it.as_document()), doc!(",", soft_space));
307 parts.push(cols);
308 parts.push(Document::text(")"));
309 }
310 CreateBody::Database { .. } => {}
311 }
312
313 Document::group(Document::Concat(parts))
314 }
315}
316
317#[cfg(feature = "oak-pretty-print")]
318impl AsDocument for ColumnDefinition {
319 fn as_document(&self) -> Document<'_> {
320 let data_type_str = self.data_type.to_string();
321 doc!(self.name.as_document(), soft_space, data_type_str, Document::join(self.constraints.iter().map(|it| doc!(soft_space, it.as_document())), nil))
322 }
323}
324
325#[cfg(feature = "oak-pretty-print")]
326impl AsDocument for ColumnConstraint {
327 fn as_document(&self) -> Document<'_> {
328 match self {
329 ColumnConstraint::PrimaryKey { .. } => Document::text("PRIMARY KEY"),
330 ColumnConstraint::NotNull { .. } => Document::text("NOT NULL"),
331 ColumnConstraint::Nullable { .. } => Document::text("NULL"),
332 ColumnConstraint::Unique { .. } => Document::text("UNIQUE"),
333 ColumnConstraint::Default(expr, _) => doc!("DEFAULT", soft_space, expr.as_document()),
334 ColumnConstraint::Check(expr, _) => doc!("CHECK", soft_space, "(", expr.as_document(), ")"),
335 ColumnConstraint::AutoIncrement { .. } => Document::text("AUTOINCREMENT"),
336 }
337 }
338}
339
340#[cfg(feature = "oak-pretty-print")]
341impl AsDocument for CreateObjectType {
342 fn as_document(&self) -> Document<'_> {
343 match self {
344 CreateObjectType::Table => Document::text("TABLE"),
345 CreateObjectType::View => Document::text("VIEW"),
346 CreateObjectType::Index => Document::text("INDEX"),
347 CreateObjectType::Database => Document::text("DATABASE"),
348 }
349 }
350}
351
352#[cfg(feature = "oak-pretty-print")]
353impl AsDocument for InsertStatement {
354 fn as_document(&self) -> Document<'_> {
355 let mut parts = Vec::new();
356 parts.push(Document::text("INSERT INTO"));
357 parts.push(soft_space);
358 parts.push(self.table_name.as_document());
359
360 if !self.columns.is_empty() {
361 parts.push(soft_space);
362 parts.push(Document::text("("));
363 parts.push(Document::join(self.columns.iter().map(|it| it.as_document()), doc!(",", soft_space)));
364 parts.push(Document::text(")"));
365 }
366
367 parts.push(line);
368 parts.push(Document::text("VALUES"));
369 parts.push(soft_space);
370 parts.push(Document::text("("));
371 parts.push(Document::join(self.values.iter().map(|it| it.as_document()), doc!(",", soft_space)));
372 parts.push(Document::text(")"));
373
374 Document::group(Document::Concat(parts))
375 }
376}
377
378#[cfg(feature = "oak-pretty-print")]
379impl AsDocument for UpdateStatement {
380 fn as_document(&self) -> Document<'_> {
381 let mut parts = Vec::new();
382 parts.push(Document::text("UPDATE"));
383 parts.push(soft_space);
384 parts.push(self.table_name.as_document());
385 parts.push(line);
386 parts.push(Document::text("SET"));
387 parts.push(soft_space);
388 parts.push(Document::join(self.assignments.iter().map(|it| it.as_document()), doc!(",", line)));
389
390 if let Some(selection) = &self.selection {
391 parts.push(line);
392 parts.push(Document::text("WHERE"));
393 parts.push(soft_space);
394 parts.push(selection.as_document());
395 }
396
397 Document::group(Document::Concat(parts))
398 }
399}
400
401#[cfg(feature = "oak-pretty-print")]
402impl AsDocument for Assignment {
403 fn as_document(&self) -> Document<'_> {
404 doc!(self.column.as_document(), soft_space, "=", soft_space, self.value.as_document())
405 }
406}
407
408#[cfg(feature = "oak-pretty-print")]
409impl AsDocument for DeleteStatement {
410 fn as_document(&self) -> Document<'_> {
411 let mut parts = Vec::new();
412 parts.push(Document::text("DELETE FROM"));
413 parts.push(soft_space);
414 parts.push(self.table_name.as_document());
415
416 if let Some(selection) = &self.selection {
417 parts.push(line);
418 parts.push(Document::text("WHERE"));
419 parts.push(soft_space);
420 parts.push(selection.as_document());
421 }
422
423 Document::group(Document::Concat(parts))
424 }
425}
426
427#[cfg(feature = "oak-pretty-print")]
428impl AsDocument for DropStatement {
429 fn as_document(&self) -> Document<'_> {
430 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())
431 }
432}
433
434#[cfg(feature = "oak-pretty-print")]
435impl AsDocument for DropObjectType {
436 fn as_document(&self) -> Document<'_> {
437 match self {
438 DropObjectType::Table => Document::text("TABLE"),
439 DropObjectType::View => Document::text("VIEW"),
440 DropObjectType::Index => Document::text("INDEX"),
441 DropObjectType::Database => Document::text("DATABASE"),
442 }
443 }
444}
445
446#[cfg(feature = "oak-pretty-print")]
447impl AsDocument for AlterStatement {
448 fn as_document(&self) -> Document<'_> {
449 let mut d = doc!("ALTER TABLE", soft_space, self.table_name.as_document());
450 if let Some(action) = &self.action {
451 d = doc!(d, soft_space, action.as_document());
452 }
453 d
454 }
455}
456
457#[cfg(feature = "oak-pretty-print")]
458impl AsDocument for AlterAction {
459 fn as_document(&self) -> Document<'_> {
460 match self {
461 AlterAction::AddColumn { name, data_type, .. } => {
462 let mut d = doc!("ADD COLUMN", soft_space, name.as_document());
463 if let Some(dt) = data_type {
464 let dt_str = dt.to_string();
465 d = doc!(d, soft_space, dt_str);
466 }
467 d
468 }
469 AlterAction::DropColumn { name, .. } => {
470 doc!("DROP COLUMN", soft_space, name.as_document())
471 }
472 AlterAction::RenameTo { new_name, .. } => {
473 doc!("RENAME TO", soft_space, new_name.as_document())
474 }
475 }
476 }
477}