1use std::fmt::Write as FmtWrite;
44
45use super::{QueryBuilder, SqlWriter};
46use crate::{
47 expr::{Condition, SimpleExpr},
48 query::{
49 AlterIndexStatement, AlterTableOperation, AlterTableStatement, CheckTableStatement,
50 CreateIndexStatement, CreateTableStatement, CreateTriggerStatement, CreateViewStatement,
51 DeleteStatement, DropIndexStatement, DropTableStatement, DropTriggerStatement,
52 DropViewStatement, InsertStatement, OptimizeTableStatement, ReindexStatement,
53 RepairTableStatement, SelectStatement, TruncateTableStatement, UpdateStatement,
54 },
55 types::{BinOper, ColumnRef, TableRef, TriggerBody},
56 value::Values,
57};
58
59#[derive(Debug, Clone, Default)]
80pub struct PostgresQueryBuilder;
81
82impl PostgresQueryBuilder {
83 pub fn new() -> Self {
85 Self
86 }
87
88 fn escape_iden(&self, ident: &str) -> String {
100 let escaped = ident.replace('"', "\"\"");
102 format!("\"{}\"", escaped)
103 }
104
105 fn placeholder(&self, index: usize) -> String {
117 format!("${}", index)
118 }
119
120 fn adjust_placeholder_offsets(sql: &str, num_params: usize, offset: usize) -> String {
129 if offset == 0 || num_params == 0 {
130 return sql.to_string();
131 }
132
133 let mut result = String::with_capacity(sql.len() + num_params * 2);
134 let bytes = sql.as_bytes();
135 let len = bytes.len();
136 let mut i = 0;
137 let mut in_single_quote = false;
139 let mut in_double_quote = false;
140
141 while i < len {
142 let ch = bytes[i];
143
144 if ch == b'\'' && !in_double_quote {
146 if in_single_quote && i + 1 < len && bytes[i + 1] == b'\'' {
148 result.push('\'');
149 result.push('\'');
150 i += 2;
151 continue;
152 }
153 in_single_quote = !in_single_quote;
154 result.push('\'');
155 i += 1;
156 continue;
157 }
158
159 if ch == b'"' && !in_single_quote {
161 if in_double_quote && i + 1 < len && bytes[i + 1] == b'"' {
163 result.push('"');
164 result.push('"');
165 i += 2;
166 continue;
167 }
168 in_double_quote = !in_double_quote;
169 result.push('"');
170 i += 1;
171 continue;
172 }
173
174 if ch == b'$' && !in_single_quote && !in_double_quote {
179 if let Some((delimiter, delim_end)) =
181 Self::try_parse_dollar_quote_delimiter(bytes, i, len)
182 {
183 result.push_str(&sql[i..delim_end]);
185 let body_start = delim_end;
187 if let Some(close_pos) = sql[body_start..].find(&delimiter) {
188 let close_end = body_start + close_pos + delimiter.len();
189 result.push_str(&sql[body_start..close_end]);
190 i = close_end;
191 } else {
192 result.push_str(&sql[body_start..]);
194 i = len;
195 }
196 continue;
197 }
198
199 let start = i + 1;
201 let mut end = start;
202 while end < len && bytes[end].is_ascii_digit() {
203 end += 1;
204 }
205 if end > start
206 && let Ok(n) = sql[start..end].parse::<usize>()
207 && n >= 1 && n <= num_params
208 {
209 use std::fmt::Write;
211 let _ = write!(result, "${}", n + offset);
212 i = end;
213 continue;
214 }
215 }
216
217 let ch_len = utf8_char_width(ch);
220 result.push_str(&sql[i..i + ch_len]);
221 i += ch_len;
222 }
223
224 result
225 }
226
227 fn try_parse_dollar_quote_delimiter(
233 bytes: &[u8],
234 pos: usize,
235 len: usize,
236 ) -> Option<(String, usize)> {
237 debug_assert!(bytes[pos] == b'$');
238 let mut j = pos + 1;
239
240 if j < len && bytes[j] == b'$' {
242 return Some(("$$".to_string(), j + 1));
243 }
244
245 if j < len && (bytes[j].is_ascii_alphabetic() || bytes[j] == b'_') {
247 j += 1;
248 while j < len && (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_') {
249 j += 1;
250 }
251 if j < len && bytes[j] == b'$' {
253 let delimiter = std::str::from_utf8(&bytes[pos..=j])
255 .expect("dollar-quote tag is ASCII")
256 .to_string();
257 return Some((delimiter, j + 1));
258 }
259 }
260
261 None
262 }
263
264 fn write_table_ref(&self, writer: &mut SqlWriter, table_ref: &TableRef) {
266 match table_ref {
267 TableRef::Table(iden) => {
268 writer.push_identifier(&iden.to_string(), |s| self.escape_iden(s));
269 }
270 TableRef::SchemaTable(schema, table) => {
271 writer.push_identifier(&schema.to_string(), |s| self.escape_iden(s));
272 writer.push(".");
273 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
274 }
275 TableRef::DatabaseSchemaTable(db, schema, table) => {
276 writer.push_identifier(&db.to_string(), |s| self.escape_iden(s));
277 writer.push(".");
278 writer.push_identifier(&schema.to_string(), |s| self.escape_iden(s));
279 writer.push(".");
280 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
281 }
282 TableRef::TableAlias(table, alias) => {
283 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
284 writer.push_keyword("AS");
285 writer.push_space();
286 writer.push_identifier(&alias.to_string(), |s| self.escape_iden(s));
287 }
288 TableRef::SchemaTableAlias(schema, table, alias) => {
289 writer.push_identifier(&schema.to_string(), |s| self.escape_iden(s));
290 writer.push(".");
291 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
292 writer.push_keyword("AS");
293 writer.push_space();
294 writer.push_identifier(&alias.to_string(), |s| self.escape_iden(s));
295 }
296 TableRef::SubQuery(query, alias) => {
297 let (subquery_sql, subquery_values) = self.build_select(query);
298
299 let offset = writer.param_index() - 1;
301 let adjusted_sql =
302 Self::adjust_placeholder_offsets(&subquery_sql, subquery_values.len(), offset);
303
304 writer.push("(");
305 writer.push(&adjusted_sql);
306 writer.push(")");
307 writer.push_keyword("AS");
308 writer.push_space();
309 writer.push_identifier(&alias.to_string(), |s| self.escape_iden(s));
310
311 writer.append_values(&subquery_values);
313 }
314 }
315 }
316
317 fn write_column_ref(&self, writer: &mut SqlWriter, col_ref: &ColumnRef) {
319 match col_ref {
320 ColumnRef::Column(iden) => {
321 writer.push_identifier(&iden.to_string(), |s| self.escape_iden(s));
322 }
323 ColumnRef::TableColumn(table, col) => {
324 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
325 writer.push(".");
326 writer.push_identifier(&col.to_string(), |s| self.escape_iden(s));
327 }
328 ColumnRef::SchemaTableColumn(schema, table, col) => {
329 writer.push_identifier(&schema.to_string(), |s| self.escape_iden(s));
330 writer.push(".");
331 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
332 writer.push(".");
333 writer.push_identifier(&col.to_string(), |s| self.escape_iden(s));
334 }
335 ColumnRef::Asterisk => {
336 writer.push("*");
337 }
338 ColumnRef::TableAsterisk(table) => {
339 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
340 writer.push(".*");
341 }
342 }
343 }
344
345 fn write_simple_expr(&self, writer: &mut SqlWriter, expr: &SimpleExpr) {
347 match expr {
348 SimpleExpr::Column(col_ref) => {
349 self.write_column_ref(writer, col_ref);
350 }
351 SimpleExpr::Value(value) => {
352 writer.push_value(value.clone(), |i| self.placeholder(i));
353 }
354 SimpleExpr::Binary(left, op, right) => match (op, right.as_ref()) {
355 (BinOper::Between | BinOper::NotBetween, SimpleExpr::Tuple(items))
356 if items.len() == 2 =>
357 {
358 self.write_simple_expr(writer, left);
359 writer.push_space();
360 writer.push(op.as_str());
361 writer.push_space();
362 self.write_simple_expr(writer, &items[0]);
363 writer.push(" AND ");
364 self.write_simple_expr(writer, &items[1]);
365 }
366 (BinOper::In | BinOper::NotIn, SimpleExpr::Tuple(items)) => {
367 self.write_simple_expr(writer, left);
368 writer.push_space();
369 writer.push(op.as_str());
370 writer.push(" (");
371 writer.push_list(items, ", ", |w, item| {
372 self.write_simple_expr(w, item);
373 });
374 writer.push(")");
375 }
376 _ => {
377 self.write_simple_expr(writer, left);
378 writer.push_space();
379 writer.push(op.as_str());
380 writer.push_space();
381 self.write_simple_expr(writer, right);
382 }
383 },
384 SimpleExpr::Unary(op, expr) => {
385 writer.push(op.as_str());
386 writer.push_space();
387 self.write_simple_expr(writer, expr);
388 }
389 SimpleExpr::FunctionCall(func_name, args) => {
390 writer.push(&func_name.to_string());
391 writer.push("(");
392 writer.push_list(args, ", ", |w, arg| {
393 self.write_simple_expr(w, arg);
394 });
395 writer.push(")");
396 }
397 SimpleExpr::Constant(val) => {
398 writer.push(val.as_str());
399 }
400 SimpleExpr::SubQuery(op, select_stmt) => {
401 use crate::expr::SubQueryOper;
402
403 if let Some(operator) = op {
405 match operator {
406 SubQueryOper::Exists => {
407 writer.push("EXISTS");
408 writer.push_space();
409 }
410 SubQueryOper::NotExists => {
411 writer.push("NOT EXISTS");
412 writer.push_space();
413 }
414 SubQueryOper::In | SubQueryOper::NotIn => {
415 }
417 SubQueryOper::All => {
418 writer.push("ALL");
419 writer.push_space();
420 }
421 SubQueryOper::Any => {
422 writer.push("ANY");
423 writer.push_space();
424 }
425 SubQueryOper::Some => {
426 writer.push("SOME");
427 writer.push_space();
428 }
429 }
430 }
431
432 writer.push("(");
434
435 let (subquery_sql, subquery_values) = self.build_select(select_stmt);
437
438 let offset = writer.param_index() - 1;
440 let adjusted_sql =
441 Self::adjust_placeholder_offsets(&subquery_sql, subquery_values.len(), offset);
442
443 writer.push(&adjusted_sql);
444 writer.push(")");
445
446 writer.append_values(&subquery_values);
448 }
449 SimpleExpr::Window { func, window } => {
450 self.write_simple_expr(writer, func);
452 writer.push_space();
453 writer.push_keyword("OVER");
454 writer.push_space();
455 writer.push("(");
456 self.write_window_statement(writer, window);
457 writer.push(")");
458 }
459 SimpleExpr::WindowNamed { func, name } => {
460 self.write_simple_expr(writer, func);
462 writer.push_space();
463 writer.push_keyword("OVER");
464 writer.push_space();
465 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
466 }
467 SimpleExpr::Tuple(items) => {
468 writer.push("(");
469 writer.push_list(items, ", ", |w, item| {
470 self.write_simple_expr(w, item);
471 });
472 writer.push(")");
473 }
474 SimpleExpr::Case(case) => {
475 writer.push_keyword("CASE");
476 for (condition, result) in &case.when_clauses {
477 writer.push_space();
478 writer.push_keyword("WHEN");
479 writer.push_space();
480 self.write_simple_expr(writer, condition);
481 writer.push_space();
482 writer.push_keyword("THEN");
483 writer.push_space();
484 self.write_simple_expr(writer, result);
485 }
486 if let Some(else_result) = &case.else_clause {
487 writer.push_space();
488 writer.push_keyword("ELSE");
489 writer.push_space();
490 self.write_simple_expr(writer, else_result);
491 }
492 writer.push_space();
493 writer.push_keyword("END");
494 }
495 SimpleExpr::Custom(sql) => {
496 writer.push(sql);
497 }
498 SimpleExpr::CustomWithExpr(template, exprs) => {
499 let mut parts = template.split('?');
501 if let Some(first) = parts.next() {
502 writer.push(first);
503 }
504 let mut expr_iter = exprs.iter();
505 for part in parts {
506 if let Some(expr) = expr_iter.next() {
507 self.write_simple_expr(writer, expr);
508 }
509 writer.push(part);
510 }
511 }
512 SimpleExpr::Asterisk => {
513 writer.push("*");
514 }
515 SimpleExpr::TableColumn(table, col) => {
516 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
517 writer.push(".");
518 writer.push_identifier(&col.to_string(), |s| self.escape_iden(s));
519 }
520 SimpleExpr::AsEnum(name, expr) => {
521 self.write_simple_expr(writer, expr);
522 writer.push("::");
523 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
524 }
525 SimpleExpr::ExprAlias(expr, alias) => {
526 self.write_simple_expr(writer, expr);
527 writer.push_keyword("AS");
528 writer.push_space();
529 writer.push_identifier(&alias.to_string(), |s| self.escape_iden(s));
530 }
531 SimpleExpr::Cast(expr, type_name) => {
532 writer.push("CAST(");
533 self.write_simple_expr(writer, expr);
534 writer.push(" AS ");
535 writer.push_identifier(&type_name.to_string(), |s| self.escape_iden(s));
536 writer.push(")");
537 }
538 }
539 }
540
541 fn write_simple_expr_unquoted(&self, writer: &mut SqlWriter, expr: &SimpleExpr) {
546 use crate::types::BinOper;
547 use crate::value::Value;
548
549 match expr {
550 SimpleExpr::Column(col_ref) => {
551 self.write_column_ref(writer, col_ref);
552 }
553 SimpleExpr::Value(value) => {
554 match value {
556 Value::Int(Some(n)) => {
557 writer.push(&n.to_string());
558 }
559 Value::BigInt(Some(n)) => {
560 writer.push(&n.to_string());
561 }
562 Value::TinyInt(Some(n)) => {
563 writer.push(&n.to_string());
564 }
565 Value::SmallInt(Some(n)) => {
566 writer.push(&n.to_string());
567 }
568 Value::Unsigned(Some(n)) => {
569 writer.push(&n.to_string());
570 }
571 Value::SmallUnsigned(Some(n)) => {
572 writer.push(&n.to_string());
573 }
574 Value::TinyUnsigned(Some(n)) => {
575 writer.push(&n.to_string());
576 }
577 Value::BigUnsigned(Some(n)) => {
578 writer.push(&n.to_string());
579 }
580 Value::String(Some(s)) => {
581 let escaped = s.as_str().replace('\'', "''");
582 writer.push(&format!("'{}'", escaped));
583 }
584 Value::Bool(Some(b)) => {
585 writer.push(if *b { "TRUE" } else { "FALSE" });
586 }
587 Value::Float(Some(f)) => {
588 writer.push(&f.to_string());
589 }
590 Value::Double(Some(d)) => {
591 writer.push(&d.to_string());
592 }
593 Value::Char(Some(c)) => {
594 let escaped = c.to_string().replace('\'', "''");
595 writer.push(&format!("'{}'", escaped));
596 }
597 Value::Bytes(Some(b)) => {
598 let mut hex = String::with_capacity(b.as_ref().len() * 2);
599 for byte in b.as_ref() {
600 write!(hex, "{:02x}", byte).unwrap();
601 }
602 writer.push("E'\\\\x");
603 writer.push(&hex);
604 writer.push("'");
605 }
606 #[cfg(feature = "with-chrono")]
607 Value::ChronoDate(Some(d)) => {
608 writer.push(&format!("'{}'", d));
609 }
610 #[cfg(feature = "with-chrono")]
611 Value::ChronoTime(Some(t)) => {
612 writer.push(&format!("'{}'", t));
613 }
614 #[cfg(feature = "with-chrono")]
615 Value::ChronoDateTime(Some(dt)) => {
616 writer.push(&format!("'{}'", dt));
617 }
618 #[cfg(feature = "with-chrono")]
619 Value::ChronoDateTimeUtc(Some(dt)) => {
620 writer.push(&format!("'{}'", dt));
621 }
622 #[cfg(feature = "with-chrono")]
623 Value::ChronoDateTimeLocal(Some(dt)) => {
624 writer.push(&format!("'{}'", dt));
625 }
626 #[cfg(feature = "with-chrono")]
627 Value::ChronoDateTimeWithTimeZone(Some(dt)) => {
628 writer.push(&format!("'{}'", dt));
629 }
630 #[cfg(feature = "with-uuid")]
631 Value::Uuid(Some(u)) => {
632 writer.push(&format!("'{}'", u));
633 }
634 #[cfg(feature = "with-json")]
635 Value::Json(Some(j)) => {
636 let escaped = j.to_string().replace('\'', "''");
637 writer.push(&format!("'{}'", escaped));
638 }
639 #[cfg(feature = "with-rust_decimal")]
640 Value::Decimal(Some(d)) => {
641 writer.push(&d.to_string());
642 }
643 #[cfg(feature = "with-bigdecimal")]
644 Value::BigDecimal(Some(d)) => {
645 writer.push(&d.to_string());
646 }
647 _ => {
649 writer.push("NULL");
650 }
651 }
652 }
653 SimpleExpr::Binary(left, op, right) => match (op, right.as_ref()) {
654 (BinOper::Between | BinOper::NotBetween, SimpleExpr::Tuple(items))
655 if items.len() == 2 =>
656 {
657 self.write_simple_expr_unquoted(writer, left);
658 writer.push_space();
659 writer.push(op.as_str());
660 writer.push_space();
661 self.write_simple_expr_unquoted(writer, &items[0]);
662 writer.push(" AND ");
663 self.write_simple_expr_unquoted(writer, &items[1]);
664 }
665 (BinOper::In | BinOper::NotIn, SimpleExpr::Tuple(items)) => {
666 self.write_simple_expr_unquoted(writer, left);
667 writer.push_space();
668 writer.push(op.as_str());
669 writer.push(" (");
670 writer.push_list(items, ", ", |w, item| {
671 self.write_simple_expr_unquoted(w, item);
672 });
673 writer.push(")");
674 }
675 _ => {
676 self.write_simple_expr_unquoted(writer, left);
677 writer.push_space();
678 writer.push(op.as_str());
679 writer.push_space();
680 self.write_simple_expr_unquoted(writer, right);
681 }
682 },
683 SimpleExpr::Unary(op, expr) => {
684 writer.push(op.as_str());
685 writer.push_space();
686 self.write_simple_expr_unquoted(writer, expr);
687 }
688 SimpleExpr::FunctionCall(func_name, args) => {
689 writer.push(&func_name.to_string());
690 writer.push("(");
691 writer.push_list(args, ", ", |w, arg| {
692 self.write_simple_expr_unquoted(w, arg);
693 });
694 writer.push(")");
695 }
696 SimpleExpr::Constant(val) => {
697 writer.push(val.as_str());
698 }
699 SimpleExpr::Tuple(items) => {
700 writer.push("(");
701 writer.push_list(items, ", ", |w, item| {
702 self.write_simple_expr_unquoted(w, item);
703 });
704 writer.push(")");
705 }
706 SimpleExpr::Case(case) => {
707 writer.push_keyword("CASE");
708 for (condition, result) in &case.when_clauses {
709 writer.push_space();
710 writer.push_keyword("WHEN");
711 writer.push_space();
712 self.write_simple_expr_unquoted(writer, condition);
713 writer.push_space();
714 writer.push_keyword("THEN");
715 writer.push_space();
716 self.write_simple_expr_unquoted(writer, result);
717 }
718 if let Some(else_result) = &case.else_clause {
719 writer.push_space();
720 writer.push_keyword("ELSE");
721 writer.push_space();
722 self.write_simple_expr_unquoted(writer, else_result);
723 }
724 writer.push_space();
725 writer.push_keyword("END");
726 }
727 SimpleExpr::SubQuery(_, _) => {
729 writer.push("(TRUE)");
730 }
731 SimpleExpr::Window { .. } | SimpleExpr::WindowNamed { .. } => {
733 writer.push("(TRUE)");
734 }
735 SimpleExpr::Custom(sql) => {
736 writer.push(sql);
737 }
738 SimpleExpr::CustomWithExpr(template, exprs) => {
739 let mut parts = template.split('?');
740 if let Some(first) = parts.next() {
741 writer.push(first);
742 }
743 let mut expr_iter = exprs.iter();
744 for part in parts {
745 if let Some(expr) = expr_iter.next() {
746 self.write_simple_expr_unquoted(writer, expr);
747 }
748 writer.push(part);
749 }
750 }
751 SimpleExpr::Asterisk => {
752 writer.push("*");
753 }
754 SimpleExpr::TableColumn(table, col) => {
755 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
756 writer.push(".");
757 writer.push_identifier(&col.to_string(), |s| self.escape_iden(s));
758 }
759 SimpleExpr::AsEnum(name, expr) => {
760 self.write_simple_expr_unquoted(writer, expr);
761 writer.push("::");
762 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
763 }
764 SimpleExpr::ExprAlias(expr, alias) => {
765 self.write_simple_expr_unquoted(writer, expr);
766 writer.push_keyword("AS");
767 writer.push_space();
768 writer.push_identifier(&alias.to_string(), |s| self.escape_iden(s));
769 }
770 SimpleExpr::Cast(expr, type_name) => {
771 writer.push("CAST(");
772 self.write_simple_expr_unquoted(writer, expr);
773 writer.push(" AS ");
774 writer.push_identifier(&type_name.to_string(), |s| self.escape_iden(s));
775 writer.push(")");
776 }
777 }
778 }
779
780 fn write_condition(&self, writer: &mut SqlWriter, condition: &Condition) {
782 use crate::expr::ConditionType;
783
784 if condition.conditions.is_empty() {
785 return;
786 }
787
788 if condition.negate {
789 writer.push("NOT ");
790 }
791
792 if condition.conditions.len() == 1 {
793 self.write_condition_expr(writer, &condition.conditions[0]);
794 return;
795 }
796
797 writer.push("(");
798 let separator = match condition.condition_type {
799 ConditionType::All => " AND ",
800 ConditionType::Any => " OR ",
801 };
802 writer.push_list(&condition.conditions, separator, |w, cond_expr| {
803 self.write_condition_expr(w, cond_expr);
804 });
805 writer.push(")");
806 }
807
808 fn write_condition_expr(
810 &self,
811 writer: &mut SqlWriter,
812 cond_expr: &crate::expr::ConditionExpression,
813 ) {
814 use crate::expr::ConditionExpression;
815
816 match cond_expr {
817 ConditionExpression::Condition(cond) => {
818 self.write_condition(writer, cond);
819 }
820 ConditionExpression::SimpleExpr(expr) => {
821 self.write_simple_expr(writer, expr);
822 }
823 }
824 }
825
826 fn write_join_expr(&self, writer: &mut SqlWriter, join: &crate::types::JoinExpr) {
828 use crate::types::JoinOn;
829
830 writer.push_keyword(join.join.as_str());
832 writer.push_space();
833
834 self.write_table_ref(writer, &join.table);
836
837 if let Some(on) = &join.on {
839 match on {
840 JoinOn::Columns(pair) => {
841 writer.push_keyword("ON");
842 writer.push_space();
843 self.write_column_spec(writer, &pair.left);
844 writer.push(" = ");
845 self.write_column_spec(writer, &pair.right);
846 }
847 JoinOn::Condition(cond) => {
848 writer.push_keyword("ON");
849 writer.push_space();
850 self.write_condition(writer, cond);
851 }
852 JoinOn::Using(cols) => {
853 writer.push_keyword("USING");
854 writer.push_space();
855 writer.push("(");
856 writer.push_list(cols, ", ", |w, col| {
857 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
858 });
859 writer.push(")");
860 }
861 }
862 }
863 }
864
865 fn write_column_spec(&self, writer: &mut SqlWriter, spec: &crate::types::ColumnSpec) {
867 match spec {
868 crate::types::ColumnSpec::Column(iden) => {
869 writer.push_identifier(&iden.to_string(), |s| self.escape_iden(s));
870 }
871 crate::types::ColumnSpec::TableColumn(table, col) => {
872 writer.push_identifier(&table.to_string(), |s| self.escape_iden(s));
873 writer.push(".");
874 writer.push_identifier(&col.to_string(), |s| self.escape_iden(s));
875 }
876 }
877 }
878
879 fn write_window_statement(
881 &self,
882 writer: &mut SqlWriter,
883 window: &crate::types::WindowStatement,
884 ) {
885 if !window.partition_by.is_empty() {
887 writer.push_keyword("PARTITION BY");
888 writer.push_space();
889 writer.push_list(&window.partition_by, ", ", |w, expr| {
890 self.write_simple_expr(w, expr);
891 });
892 writer.push_space();
893 }
894
895 if !window.order_by.is_empty() {
897 writer.push_keyword("ORDER BY");
898 writer.push_space();
899 writer.push_list(&window.order_by, ", ", |w, order_expr| {
900 use crate::types::OrderExprKind;
901 match &order_expr.expr {
902 OrderExprKind::Column(iden) => {
903 w.push_identifier(&iden.to_string(), |s| self.escape_iden(s));
904 }
905 OrderExprKind::TableColumn(table, col) => {
906 w.push_identifier(&table.to_string(), |s| self.escape_iden(s));
907 w.push(".");
908 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
909 }
910 OrderExprKind::Expr(expr) => {
911 self.write_simple_expr(w, expr);
912 }
913 }
914 match order_expr.order {
915 crate::types::Order::Asc => {
916 w.push_keyword("ASC");
917 }
918 crate::types::Order::Desc => {
919 w.push_keyword("DESC");
920 }
921 }
922 if let Some(nulls) = order_expr.nulls {
923 w.push_space();
924 w.push(nulls.as_str());
925 }
926 });
927 writer.push_space();
928 }
929
930 if let Some(frame) = &window.frame {
932 self.write_frame_clause(writer, frame);
933 }
934 }
935
936 fn write_frame_clause(&self, writer: &mut SqlWriter, frame: &crate::types::FrameClause) {
938 use crate::types::FrameType;
939
940 match frame.frame_type {
942 FrameType::Rows => writer.push_keyword("ROWS"),
943 FrameType::Range => writer.push_keyword("RANGE"),
944 FrameType::Groups => writer.push_keyword("GROUPS"),
945 }
946 writer.push_space();
947
948 if let Some(end) = &frame.end {
950 writer.push_keyword("BETWEEN");
951 writer.push_space();
952 self.write_frame_boundary(writer, &frame.start);
953 writer.push_keyword("AND");
954 writer.push_space();
955 self.write_frame_boundary(writer, end);
956 } else {
957 self.write_frame_boundary(writer, &frame.start);
958 }
959 }
960
961 fn write_frame_boundary(&self, writer: &mut SqlWriter, frame: &crate::types::Frame) {
963 use crate::types::Frame;
964 match frame {
965 Frame::UnboundedPreceding => writer.push("UNBOUNDED PRECEDING"),
966 Frame::Preceding(n) => {
967 writer.push(&n.to_string());
968 writer.push(" PRECEDING");
969 }
970 Frame::CurrentRow => writer.push("CURRENT ROW"),
971 Frame::Following(n) => {
972 writer.push(&n.to_string());
973 writer.push(" FOLLOWING");
974 }
975 Frame::UnboundedFollowing => writer.push("UNBOUNDED FOLLOWING"),
976 }
977 }
978}
979
980impl QueryBuilder for PostgresQueryBuilder {
981 fn build_select(&self, stmt: &SelectStatement) -> (String, Values) {
982 let mut writer = SqlWriter::new();
983
984 if !stmt.ctes.is_empty() {
986 let has_recursive = stmt.ctes.iter().any(|cte| cte.recursive);
988
989 writer.push_keyword("WITH");
991 writer.push_space();
992 if has_recursive {
993 writer.push_keyword("RECURSIVE");
994 writer.push_space();
995 }
996
997 writer.push_list(&stmt.ctes, ", ", |w, cte| {
999 w.push_identifier(&cte.name.to_string(), |s| self.escape_iden(s));
1001 w.push_space();
1002 w.push_keyword("AS");
1003 w.push_space();
1004 w.push("(");
1005
1006 let (cte_sql, cte_values) = self.build_select(&cte.query);
1008
1009 let offset = w.param_index() - 1;
1011 let adjusted_sql =
1012 Self::adjust_placeholder_offsets(&cte_sql, cte_values.len(), offset);
1013
1014 w.push(&adjusted_sql);
1015 w.push(")");
1016
1017 w.append_values(&cte_values);
1019 });
1020
1021 writer.push_space();
1022 }
1023
1024 writer.push("SELECT");
1026 writer.push_space();
1027
1028 if let Some(distinct) = &stmt.distinct {
1030 use crate::query::SelectDistinct;
1031 match distinct {
1032 SelectDistinct::All => {
1033 }
1035 SelectDistinct::Distinct => {
1036 writer.push_keyword("DISTINCT");
1037 writer.push_space();
1038 }
1039 SelectDistinct::DistinctRow => {
1040 panic!("PostgreSQL does not support DISTINCT ROW. Use DISTINCT instead.");
1041 }
1042 SelectDistinct::DistinctOn(cols) => {
1043 writer.push_keyword("DISTINCT ON");
1044 writer.push_space();
1045 writer.push("(");
1046 writer.push_list(cols, ", ", |w, col_ref| {
1047 self.write_column_ref(w, col_ref);
1048 });
1049 writer.push(")");
1050 writer.push_space();
1051 }
1052 }
1053 }
1054
1055 if stmt.selects.is_empty() {
1056 writer.push("*");
1057 } else {
1058 writer.push_list(&stmt.selects, ", ", |w, select_expr| {
1059 self.write_simple_expr(w, &select_expr.expr);
1060 if let Some(alias) = &select_expr.alias {
1061 w.push_keyword("AS");
1062 w.push_space();
1063 w.push_identifier(&alias.to_string(), |s| self.escape_iden(s));
1064 }
1065 });
1066 }
1067
1068 if !stmt.from.is_empty() {
1070 writer.push_keyword("FROM");
1071 writer.push_space();
1072 writer.push_list(&stmt.from, ", ", |w, table_ref| {
1073 self.write_table_ref(w, table_ref);
1074 });
1075 }
1076
1077 for join in &stmt.join {
1079 writer.push_space();
1080 self.write_join_expr(&mut writer, join);
1081 }
1082
1083 if !stmt.r#where.is_empty() {
1085 writer.push_keyword("WHERE");
1086 writer.push_space();
1087 writer.push_list(&stmt.r#where.conditions, " AND ", |w, cond_expr| {
1089 self.write_condition_expr(w, cond_expr);
1090 });
1091 }
1092
1093 if !stmt.groups.is_empty() {
1095 writer.push_keyword("GROUP BY");
1096 writer.push_space();
1097 writer.push_list(&stmt.groups, ", ", |w, expr| {
1098 self.write_simple_expr(w, expr);
1099 });
1100 }
1101
1102 if !stmt.having.conditions.is_empty() {
1104 writer.push_keyword("HAVING");
1105 writer.push_space();
1106 writer.push_list(&stmt.having.conditions, " AND ", |w, cond_expr| {
1108 self.write_condition_expr(w, cond_expr);
1109 });
1110 }
1111
1112 if !stmt.orders.is_empty() {
1114 writer.push_keyword("ORDER BY");
1115 writer.push_space();
1116 writer.push_list(&stmt.orders, ", ", |w, order_expr| {
1117 use crate::types::OrderExprKind;
1118 match &order_expr.expr {
1119 OrderExprKind::Column(iden) => {
1120 w.push_identifier(&iden.to_string(), |s| self.escape_iden(s));
1121 }
1122 OrderExprKind::TableColumn(table, col) => {
1123 w.push_identifier(&table.to_string(), |s| self.escape_iden(s));
1124 w.push(".");
1125 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
1126 }
1127 OrderExprKind::Expr(expr) => {
1128 self.write_simple_expr(w, expr);
1129 }
1130 }
1131 match order_expr.order {
1132 crate::types::Order::Asc => {
1133 w.push_keyword("ASC");
1134 }
1135 crate::types::Order::Desc => {
1136 w.push_keyword("DESC");
1137 }
1138 }
1139 if let Some(nulls) = order_expr.nulls {
1140 w.push_space();
1141 w.push(nulls.as_str());
1142 }
1143 });
1144 }
1145
1146 if !stmt.windows.is_empty() {
1148 writer.push_keyword("WINDOW");
1149 writer.push_space();
1150 writer.push_list(&stmt.windows, ", ", |w, (name, window)| {
1151 w.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1152 w.push_space();
1153 w.push_keyword("AS");
1154 w.push_space();
1155 w.push("(");
1156 self.write_window_statement(w, window);
1157 w.push(")");
1158 });
1159 }
1160
1161 if let Some(limit) = &stmt.limit {
1163 writer.push_keyword("LIMIT");
1164 writer.push_space();
1165 writer.push_value(limit.clone(), |i| self.placeholder(i));
1166 }
1167
1168 if let Some(offset) = &stmt.offset {
1170 writer.push_keyword("OFFSET");
1171 writer.push_space();
1172 writer.push_value(offset.clone(), |i| self.placeholder(i));
1173 }
1174
1175 for (union_type, union_stmt) in &stmt.unions {
1177 writer.push_space();
1178 use crate::query::UnionType;
1179 match union_type {
1180 UnionType::Distinct => {
1181 writer.push_keyword("UNION");
1182 }
1183 UnionType::All => {
1184 writer.push_keyword("UNION ALL");
1185 }
1186 UnionType::Intersect => {
1187 writer.push_keyword("INTERSECT");
1188 }
1189 UnionType::Except => {
1190 writer.push_keyword("EXCEPT");
1191 }
1192 }
1193 writer.push_space();
1194
1195 let (union_sql, union_values) = self.build_select(union_stmt);
1197
1198 let offset = writer.param_index() - 1;
1200 let union_sql =
1201 Self::adjust_placeholder_offsets(&union_sql, union_values.len(), offset);
1202
1203 if !union_stmt.unions.is_empty() {
1205 writer.push("(");
1206 writer.push(&union_sql);
1207 writer.push(")");
1208 } else {
1209 writer.push(&union_sql);
1210 }
1211
1212 writer.append_values(&union_values);
1214 }
1215
1216 writer.finish()
1217 }
1218
1219 fn build_insert(&self, stmt: &InsertStatement) -> (String, Values) {
1220 use crate::query::insert::InsertSource;
1221
1222 let mut writer = SqlWriter::new();
1223
1224 writer.push("INSERT INTO");
1226 writer.push_space();
1227
1228 if let Some(table) = &stmt.table {
1229 self.write_table_ref(&mut writer, table);
1230 } else {
1231 writer.push("(NO_TABLE)");
1233 }
1234
1235 if !stmt.columns.is_empty() {
1237 writer.push_space();
1238 writer.push("(");
1239 writer.push_list(&stmt.columns, ", ", |w, col| {
1240 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
1241 });
1242 writer.push(")");
1243 }
1244
1245 match &stmt.source {
1247 InsertSource::Values(values) if !values.is_empty() => {
1248 writer.push_keyword("VALUES");
1249 writer.push_space();
1250
1251 writer.push_list(values, ", ", |w, row| {
1252 w.push("(");
1253 w.push_list(row, ", ", |w2, value| {
1254 w2.push_value(value.clone(), |i| self.placeholder(i));
1255 });
1256 w.push(")");
1257 });
1258 }
1259 InsertSource::Subquery(select) => {
1260 writer.push_space();
1261 let (select_sql, select_values) = self.build_select(select);
1262 writer.push(&select_sql);
1263 writer.append_values(&select_values);
1264 }
1265 _ => {
1266 }
1268 }
1269
1270 if let Some(on_conflict) = &stmt.on_conflict {
1272 use crate::query::{OnConflictAction, OnConflictTarget};
1273 writer.push_keyword("ON CONFLICT");
1274 writer.push_space();
1275
1276 writer.push("(");
1278 match &on_conflict.target {
1279 OnConflictTarget::Column(col) => {
1280 writer.push_identifier(&col.to_string(), |s| self.escape_iden(s));
1281 }
1282 OnConflictTarget::Columns(cols) => {
1283 writer.push_list(cols, ", ", |w, col| {
1284 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
1285 });
1286 }
1287 }
1288 writer.push(")");
1289
1290 match &on_conflict.action {
1292 OnConflictAction::DoNothing => {
1293 writer.push_keyword("DO NOTHING");
1294 }
1295 OnConflictAction::DoUpdate(cols) => {
1296 writer.push_keyword("DO UPDATE SET");
1297 writer.push_space();
1298 writer.push_list(cols, ", ", |w, col| {
1299 let col_str = col.to_string();
1300 w.push_identifier(&col_str, |s| self.escape_iden(s));
1301 w.push(" = EXCLUDED.");
1302 w.push_identifier(&col_str, |s| self.escape_iden(s));
1303 });
1304 }
1305 }
1306 }
1307
1308 if let Some(returning) = &stmt.returning {
1310 writer.push_keyword("RETURNING");
1311 writer.push_space();
1312
1313 use crate::query::ReturningClause;
1314 match returning {
1315 ReturningClause::All => {
1316 writer.push("*");
1317 }
1318 ReturningClause::Columns(cols) => {
1319 writer.push_list(cols, ", ", |w, col| {
1320 self.write_column_ref(w, col);
1321 });
1322 }
1323 }
1324 }
1325
1326 writer.finish()
1327 }
1328
1329 fn build_update(&self, stmt: &UpdateStatement) -> (String, Values) {
1330 let mut writer = SqlWriter::new();
1331
1332 writer.push("UPDATE");
1334 writer.push_space();
1335
1336 if let Some(table) = &stmt.table {
1337 self.write_table_ref(&mut writer, table);
1338 } else {
1339 writer.push("(NO_TABLE)");
1340 }
1341
1342 if !stmt.values.is_empty() {
1344 writer.push_keyword("SET");
1345 writer.push_space();
1346
1347 writer.push_list(&stmt.values, ", ", |w, (col, value)| {
1348 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
1349 w.push(" = ");
1350 self.write_simple_expr(w, value);
1351 });
1352 }
1353
1354 if !stmt.r#where.is_empty() {
1356 writer.push_keyword("WHERE");
1357 writer.push_space();
1358 writer.push_list(&stmt.r#where.conditions, " AND ", |w, cond_expr| {
1359 self.write_condition_expr(w, cond_expr);
1360 });
1361 }
1362
1363 if let Some(returning) = &stmt.returning {
1365 writer.push_keyword("RETURNING");
1366 writer.push_space();
1367
1368 use crate::query::ReturningClause;
1369 match returning {
1370 ReturningClause::All => {
1371 writer.push("*");
1372 }
1373 ReturningClause::Columns(cols) => {
1374 writer.push_list(cols, ", ", |w, col| {
1375 self.write_column_ref(w, col);
1376 });
1377 }
1378 }
1379 }
1380
1381 writer.finish()
1382 }
1383
1384 fn build_delete(&self, stmt: &DeleteStatement) -> (String, Values) {
1385 let mut writer = SqlWriter::new();
1386
1387 writer.push("DELETE FROM");
1389 writer.push_space();
1390
1391 if let Some(table) = &stmt.table {
1392 self.write_table_ref(&mut writer, table);
1393 } else {
1394 writer.push("(NO_TABLE)");
1395 }
1396
1397 if !stmt.r#where.is_empty() {
1399 writer.push_keyword("WHERE");
1400 writer.push_space();
1401 writer.push_list(&stmt.r#where.conditions, " AND ", |w, cond_expr| {
1402 self.write_condition_expr(w, cond_expr);
1403 });
1404 }
1405
1406 if let Some(returning) = &stmt.returning {
1408 writer.push_keyword("RETURNING");
1409 writer.push_space();
1410
1411 use crate::query::ReturningClause;
1412 match returning {
1413 ReturningClause::All => {
1414 writer.push("*");
1415 }
1416 ReturningClause::Columns(cols) => {
1417 writer.push_list(cols, ", ", |w, col| {
1418 self.write_column_ref(w, col);
1419 });
1420 }
1421 }
1422 }
1423
1424 writer.finish()
1425 }
1426
1427 fn build_create_table(&self, stmt: &CreateTableStatement) -> (String, Values) {
1428 let mut writer = SqlWriter::new();
1429
1430 writer.push("CREATE TABLE");
1431 writer.push_space();
1432
1433 if stmt.if_not_exists {
1434 writer.push_keyword("IF NOT EXISTS");
1435 writer.push_space();
1436 }
1437
1438 if let Some(table) = &stmt.table {
1439 self.write_table_ref(&mut writer, table);
1440 }
1441
1442 writer.push_space();
1443 writer.push("(");
1444
1445 let mut first = true;
1447 for column in &stmt.columns {
1448 if !first {
1449 writer.push(", ");
1450 }
1451 first = false;
1452
1453 writer.push_identifier(&column.name.to_string(), |s| self.escape_iden(s));
1455 writer.push_space();
1456
1457 if let Some(col_type) = &column.column_type {
1459 if column.auto_increment {
1461 use crate::types::ColumnType;
1462 let serial_type = match col_type {
1463 ColumnType::SmallInteger => "SMALLSERIAL",
1464 ColumnType::Integer => "SERIAL",
1465 ColumnType::BigInteger => "BIGSERIAL",
1466 _ => &self.column_type_to_sql(col_type),
1467 };
1468 writer.push(serial_type);
1469 } else {
1470 writer.push(&self.column_type_to_sql(col_type));
1471 }
1472 }
1473
1474 if column.not_null {
1476 writer.push_space();
1477 writer.push_keyword("NOT NULL");
1478 }
1479
1480 if column.unique {
1482 writer.push_space();
1483 writer.push_keyword("UNIQUE");
1484 }
1485
1486 if column.primary_key {
1488 writer.push_space();
1489 writer.push_keyword("PRIMARY KEY");
1490 }
1491
1492 if let Some(default_expr) = &column.default {
1494 writer.push_space();
1495 writer.push_keyword("DEFAULT");
1496 writer.push_space();
1497 self.write_simple_expr(&mut writer, default_expr);
1498 }
1499
1500 if let Some(check_expr) = &column.check {
1502 writer.push_space();
1503 writer.push_keyword("CHECK");
1504 writer.push_space();
1505 writer.push("(");
1506 self.write_simple_expr_unquoted(&mut writer, check_expr);
1507 writer.push(")");
1508 }
1509 }
1510
1511 for constraint in &stmt.constraints {
1513 writer.push(", ");
1514 self.write_table_constraint(&mut writer, constraint);
1515 }
1516
1517 writer.push(")");
1518
1519 writer.finish()
1520 }
1521
1522 fn build_alter_table(&self, stmt: &AlterTableStatement) -> (String, Values) {
1523 let mut writer = SqlWriter::new();
1524
1525 writer.push("ALTER TABLE");
1527 writer.push_space();
1528 if let Some(table) = &stmt.table {
1529 self.write_table_ref(&mut writer, table);
1530 }
1531
1532 let mut first = true;
1534 for operation in &stmt.operations {
1535 if !first {
1536 writer.push(",");
1537 }
1538 first = false;
1539 writer.push_space();
1540
1541 match operation {
1542 AlterTableOperation::AddColumn(column_def) => {
1543 writer.push("ADD COLUMN");
1544 writer.push_space();
1545 writer.push_identifier(&column_def.name.to_string(), |s| self.escape_iden(s));
1546 writer.push_space();
1547 if let Some(col_type) = &column_def.column_type {
1548 writer.push(&self.column_type_to_sql(col_type));
1549 }
1550 if column_def.not_null {
1551 writer.push(" NOT NULL");
1552 }
1553 if column_def.unique {
1554 writer.push(" UNIQUE");
1555 }
1556 if let Some(default) = &column_def.default {
1557 writer.push(" DEFAULT ");
1558 self.write_simple_expr(&mut writer, default);
1559 }
1560 if let Some(check) = &column_def.check {
1561 writer.push(" CHECK (");
1562 self.write_simple_expr_unquoted(&mut writer, check);
1563 writer.push(")");
1564 }
1565 }
1566 AlterTableOperation::DropColumn { name, if_exists } => {
1567 writer.push("DROP COLUMN");
1568 writer.push_space();
1569 if *if_exists {
1570 writer.push("IF EXISTS");
1571 writer.push_space();
1572 }
1573 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1574 }
1575 AlterTableOperation::RenameColumn { old, new } => {
1576 writer.push("RENAME COLUMN");
1577 writer.push_space();
1578 writer.push_identifier(&old.to_string(), |s| self.escape_iden(s));
1579 writer.push_space();
1580 writer.push("TO");
1581 writer.push_space();
1582 writer.push_identifier(&new.to_string(), |s| self.escape_iden(s));
1583 }
1584 AlterTableOperation::ModifyColumn(column_def) => {
1585 writer.push("ALTER COLUMN");
1587 writer.push_space();
1588 writer.push_identifier(&column_def.name.to_string(), |s| self.escape_iden(s));
1589 writer.push_space();
1590
1591 if let Some(col_type) = &column_def.column_type {
1593 writer.push("TYPE");
1594 writer.push_space();
1595 writer.push(&self.column_type_to_sql(col_type));
1596 }
1597
1598 if column_def.not_null {
1600 writer.push(", ALTER COLUMN ");
1601 writer
1602 .push_identifier(&column_def.name.to_string(), |s| self.escape_iden(s));
1603 writer.push(" SET NOT NULL");
1604 }
1605
1606 if let Some(default) = &column_def.default {
1608 writer.push(", ALTER COLUMN ");
1609 writer
1610 .push_identifier(&column_def.name.to_string(), |s| self.escape_iden(s));
1611 writer.push(" SET DEFAULT ");
1612 self.write_simple_expr(&mut writer, default);
1613 }
1614 }
1615 AlterTableOperation::AddConstraint(constraint) => {
1616 writer.push("ADD ");
1617 self.write_table_constraint(&mut writer, constraint);
1618 }
1619 AlterTableOperation::DropConstraint { name, if_exists } => {
1620 writer.push("DROP CONSTRAINT");
1621 writer.push_space();
1622 if *if_exists {
1623 writer.push("IF EXISTS");
1624 writer.push_space();
1625 }
1626 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1627 }
1628 AlterTableOperation::RenameTable(new_name) => {
1629 writer.push("RENAME TO");
1630 writer.push_space();
1631 writer.push_identifier(&new_name.to_string(), |s| self.escape_iden(s));
1632 }
1633 }
1634 }
1635
1636 writer.finish()
1637 }
1638
1639 fn build_drop_table(&self, stmt: &DropTableStatement) -> (String, Values) {
1640 let mut writer = SqlWriter::new();
1641
1642 writer.push("DROP TABLE");
1644 writer.push_space();
1645
1646 if stmt.if_exists {
1648 writer.push_keyword("IF EXISTS");
1649 writer.push_space();
1650 }
1651
1652 writer.push_list(&stmt.tables, ", ", |w, table_ref| {
1654 self.write_table_ref(w, table_ref);
1655 });
1656
1657 if stmt.cascade {
1659 writer.push_space();
1660 writer.push_keyword("CASCADE");
1661 } else if stmt.restrict {
1662 writer.push_space();
1663 writer.push_keyword("RESTRICT");
1664 }
1665
1666 writer.finish()
1667 }
1668
1669 fn build_create_index(&self, stmt: &CreateIndexStatement) -> (String, Values) {
1670 let mut writer = SqlWriter::new();
1671
1672 writer.push("CREATE");
1674 writer.push_space();
1675 if stmt.unique {
1676 writer.push_keyword("UNIQUE");
1677 writer.push_space();
1678 }
1679 writer.push_keyword("INDEX");
1680 writer.push_space();
1681 if stmt.if_not_exists {
1682 writer.push_keyword("IF NOT EXISTS");
1683 writer.push_space();
1684 }
1685
1686 if let Some(name) = &stmt.name {
1688 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1689 writer.push_space();
1690 }
1691
1692 writer.push_keyword("ON");
1694 writer.push_space();
1695 if let Some(table) = &stmt.table {
1696 self.write_table_ref(&mut writer, table);
1697 }
1698 writer.push_space();
1699
1700 if let Some(method) = &stmt.using {
1702 writer.push_keyword("USING");
1703 writer.push_space();
1704 writer.push(self.index_method_to_sql(method));
1705 writer.push_space();
1706 }
1707
1708 writer.push("(");
1710 let mut first = true;
1711 for col in &stmt.columns {
1712 if !first {
1713 writer.push(", ");
1714 }
1715 first = false;
1716 writer.push_identifier(&col.name.to_string(), |s| self.escape_iden(s));
1717 if let Some(order) = &col.order {
1718 writer.push_space();
1719 match order {
1720 crate::types::Order::Asc => writer.push("ASC"),
1721 crate::types::Order::Desc => writer.push("DESC"),
1722 }
1723 }
1724 }
1725 writer.push(")");
1726
1727 if let Some(where_expr) = &stmt.r#where {
1729 writer.push_space();
1730 writer.push_keyword("WHERE");
1731 writer.push_space();
1732 self.write_simple_expr(&mut writer, where_expr);
1733 }
1734
1735 writer.finish()
1736 }
1737
1738 fn build_drop_index(&self, stmt: &DropIndexStatement) -> (String, Values) {
1739 let mut writer = SqlWriter::new();
1740
1741 writer.push("DROP INDEX");
1743 writer.push_space();
1744
1745 if stmt.if_exists {
1747 writer.push_keyword("IF EXISTS");
1748 writer.push_space();
1749 }
1750
1751 if let Some(name) = &stmt.name {
1753 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1754 }
1755
1756 if stmt.cascade {
1758 writer.push_space();
1759 writer.push_keyword("CASCADE");
1760 } else if stmt.restrict {
1761 writer.push_space();
1762 writer.push_keyword("RESTRICT");
1763 }
1764
1765 writer.finish()
1766 }
1767
1768 fn build_create_view(&self, stmt: &CreateViewStatement) -> (String, Values) {
1769 let mut writer = SqlWriter::new();
1770
1771 writer.push("CREATE");
1772
1773 if stmt.or_replace {
1774 writer.push_keyword("OR REPLACE");
1775 }
1776
1777 if stmt.materialized {
1778 writer.push_keyword("MATERIALIZED");
1779 }
1780
1781 writer.push_keyword("VIEW");
1782
1783 if stmt.if_not_exists {
1784 writer.push_keyword("IF NOT EXISTS");
1785 }
1786
1787 if let Some(name) = &stmt.name {
1788 writer.push_space();
1789 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1790 }
1791
1792 if !stmt.columns.is_empty() {
1793 writer.push_space();
1794 writer.push("(");
1795 writer.push_list(stmt.columns.iter(), ", ", |w, col| {
1796 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
1797 });
1798 writer.push(")");
1799 }
1800
1801 writer.push_keyword("AS");
1802
1803 if let Some(select) = &stmt.select {
1804 let (select_sql, select_values) = self.build_select(select);
1805 writer.push_space();
1806 writer.push(&select_sql);
1807 writer.append_values(&select_values);
1808 }
1809
1810 writer.finish()
1811 }
1812
1813 fn build_drop_view(&self, stmt: &DropViewStatement) -> (String, Values) {
1814 let mut writer = SqlWriter::new();
1815
1816 writer.push("DROP");
1817
1818 if stmt.materialized {
1819 writer.push_keyword("MATERIALIZED");
1820 }
1821
1822 writer.push_keyword("VIEW");
1823
1824 if stmt.if_exists {
1825 writer.push_keyword("IF EXISTS");
1826 }
1827
1828 writer.push_space();
1829 writer.push_list(stmt.names.iter(), ", ", |w, name| {
1830 w.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1831 });
1832
1833 if stmt.cascade {
1834 writer.push_keyword("CASCADE");
1835 } else if stmt.restrict {
1836 writer.push_keyword("RESTRICT");
1837 }
1838
1839 writer.finish()
1840 }
1841
1842 fn build_truncate_table(&self, stmt: &TruncateTableStatement) -> (String, Values) {
1843 let mut writer = SqlWriter::new();
1844
1845 writer.push("TRUNCATE TABLE");
1847 writer.push_space();
1848
1849 writer.push_list(&stmt.tables, ", ", |w, table_ref| {
1851 self.write_table_ref(w, table_ref);
1852 });
1853
1854 if stmt.restart_identity {
1856 writer.push_space();
1857 writer.push_keyword("RESTART IDENTITY");
1858 }
1859
1860 if stmt.cascade {
1862 writer.push_space();
1863 writer.push_keyword("CASCADE");
1864 } else if stmt.restrict {
1865 writer.push_space();
1866 writer.push_keyword("RESTRICT");
1867 }
1868
1869 writer.finish()
1870 }
1871
1872 fn build_create_trigger(&self, stmt: &CreateTriggerStatement) -> (String, Values) {
1873 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
1874
1875 let mut writer = SqlWriter::new();
1876
1877 writer.push("CREATE TRIGGER");
1879
1880 if let Some(name) = &stmt.name {
1882 writer.push_space();
1883 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1884 }
1885
1886 if let Some(timing) = stmt.timing {
1888 writer.push_space();
1889 match timing {
1890 TriggerTiming::Before => writer.push("BEFORE"),
1891 TriggerTiming::After => writer.push("AFTER"),
1892 TriggerTiming::InsteadOf => writer.push("INSTEAD OF"),
1893 }
1894 }
1895
1896 if !stmt.events.is_empty() {
1898 writer.push_space();
1899 let mut first = true;
1900 for event in &stmt.events {
1901 if !first {
1902 writer.push(" OR ");
1903 }
1904 first = false;
1905
1906 match event {
1907 TriggerEvent::Insert => writer.push("INSERT"),
1908 TriggerEvent::Update { columns } => {
1909 writer.push("UPDATE");
1910 if let Some(cols) = columns {
1911 writer.push(" OF ");
1912 writer.push_list(cols.iter(), ", ", |w, col| {
1913 w.push_identifier(col, |s| self.escape_iden(s));
1914 });
1915 }
1916 }
1917 TriggerEvent::Delete => writer.push("DELETE"),
1918 }
1919 }
1920 }
1921
1922 writer.push_keyword("ON");
1924 if let Some(table) = &stmt.table {
1925 writer.push_space();
1926 self.write_table_ref(&mut writer, table);
1927 }
1928
1929 if let Some(scope) = stmt.scope {
1931 writer.push_space();
1932 match scope {
1933 TriggerScope::Row => writer.push("FOR EACH ROW"),
1934 TriggerScope::Statement => writer.push("FOR EACH STATEMENT"),
1935 }
1936 }
1937
1938 if let Some(when_cond) = &stmt.when_condition {
1940 writer.push_keyword("WHEN");
1941 writer.push(" (");
1942 self.write_simple_expr(&mut writer, when_cond);
1943 writer.push(")");
1944 }
1945
1946 if let Some(body) = &stmt.body {
1948 writer.push_space();
1949 match body {
1950 TriggerBody::PostgresFunction(func_name) => {
1951 writer.push("EXECUTE FUNCTION ");
1952 writer.push_identifier(func_name.as_str(), |s| self.escape_iden(s));
1953 writer.push("()");
1954 }
1955 TriggerBody::Single(_) | TriggerBody::Multiple(_) => {
1956 panic!(
1957 "PostgreSQL triggers require EXECUTE FUNCTION, not inline SQL statements"
1958 );
1959 }
1960 }
1961 }
1962
1963 writer.finish()
1964 }
1965
1966 fn build_drop_trigger(&self, stmt: &DropTriggerStatement) -> (String, Values) {
1967 let mut writer = SqlWriter::new();
1968
1969 writer.push("DROP TRIGGER");
1971
1972 if stmt.if_exists {
1974 writer.push_keyword("IF EXISTS");
1975 }
1976
1977 if let Some(name) = &stmt.name {
1979 writer.push_space();
1980 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
1981 }
1982
1983 if let Some(table) = &stmt.table {
1985 writer.push_keyword("ON");
1986 writer.push_space();
1987 self.write_table_ref(&mut writer, table);
1988 }
1989
1990 if stmt.cascade {
1992 writer.push_keyword("CASCADE");
1993 } else if stmt.restrict {
1994 writer.push_keyword("RESTRICT");
1995 }
1996
1997 writer.finish()
1998 }
1999
2000 fn build_alter_index(&self, stmt: &AlterIndexStatement) -> (String, Values) {
2001 use crate::types::Iden;
2002
2003 let mut writer = SqlWriter::new();
2004 writer.push_keyword("ALTER INDEX");
2005 writer.push_space();
2006
2007 if let Some(ref name) = stmt.name {
2008 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2009 } else {
2010 panic!("ALTER INDEX requires an index name");
2011 }
2012
2013 if let Some(ref new_name) = stmt.rename_to {
2015 writer.push_space();
2016 writer.push_keyword("RENAME TO");
2017 writer.push_space();
2018 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| self.escape_iden(s));
2019 }
2020
2021 if let Some(ref tablespace) = stmt.set_tablespace {
2023 writer.push_space();
2024 writer.push_keyword("SET TABLESPACE");
2025 writer.push_space();
2026 writer.push_identifier(&Iden::to_string(tablespace.as_ref()), |s| {
2027 self.escape_iden(s)
2028 });
2029 }
2030
2031 writer.finish()
2032 }
2033
2034 fn build_reindex(&self, stmt: &ReindexStatement) -> (String, Values) {
2035 use crate::types::Iden;
2036
2037 let mut writer = SqlWriter::new();
2038 writer.push_keyword("REINDEX");
2039
2040 let mut options = Vec::new();
2042 if stmt.concurrently {
2043 options.push("CONCURRENTLY".to_string());
2044 }
2045 if stmt.verbose {
2046 options.push("VERBOSE".to_string());
2047 }
2048 if let Some(ref tablespace) = stmt.tablespace {
2049 let escaped = self.escape_iden(&Iden::to_string(tablespace.as_ref()));
2050 options.push(format!("TABLESPACE {}", escaped));
2051 }
2052
2053 if !options.is_empty() {
2054 writer.push_space();
2055 writer.push("(");
2056 writer.push(&options.join(", "));
2057 writer.push(")");
2058 }
2059
2060 writer.push_space();
2062 if let Some(target) = stmt.target {
2063 use crate::query::ReindexTarget;
2064 match target {
2065 ReindexTarget::Index => writer.push_keyword("INDEX"),
2066 ReindexTarget::Table => writer.push_keyword("TABLE"),
2067 ReindexTarget::Schema => writer.push_keyword("SCHEMA"),
2068 ReindexTarget::Database => writer.push_keyword("DATABASE"),
2069 ReindexTarget::System => writer.push_keyword("SYSTEM"),
2070 }
2071 } else {
2072 panic!("REINDEX requires a target");
2073 }
2074
2075 writer.push_space();
2077 if let Some(ref name) = stmt.name {
2078 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2079 } else {
2080 panic!("REINDEX requires a name");
2081 }
2082
2083 writer.finish()
2084 }
2085
2086 fn build_optimize_table(&self, _stmt: &OptimizeTableStatement) -> (String, Values) {
2087 panic!(
2088 "OPTIMIZE TABLE is MySQL-specific. PostgreSQL users should use VACUUM ANALYZE instead."
2089 );
2090 }
2091
2092 fn build_repair_table(&self, _stmt: &RepairTableStatement) -> (String, Values) {
2093 panic!(
2094 "REPAIR TABLE is not supported in PostgreSQL. PostgreSQL automatically repairs corrupted data during normal operation."
2095 );
2096 }
2097
2098 fn build_check_table(&self, _stmt: &CheckTableStatement) -> (String, Values) {
2099 panic!(
2100 "CHECK TABLE is not supported in PostgreSQL. Use pg_catalog system views or pg_stat_* functions to monitor table health."
2101 );
2102 }
2103
2104 fn build_create_function(
2105 &self,
2106 stmt: &crate::query::CreateFunctionStatement,
2107 ) -> (String, Values) {
2108 use crate::types::{
2109 Iden,
2110 function::{FunctionBehavior, FunctionLanguage, FunctionSecurity},
2111 };
2112
2113 let mut writer = SqlWriter::new();
2114
2115 writer.push_keyword("CREATE");
2117 if stmt.function_def.or_replace {
2118 writer.push_keyword("OR REPLACE");
2119 }
2120 writer.push_keyword("FUNCTION");
2121
2122 writer.push_space();
2124 writer.push_identifier(&Iden::to_string(stmt.function_def.name.as_ref()), |s| {
2125 self.escape_iden(s)
2126 });
2127
2128 writer.push("(");
2130 let mut first = true;
2131 for param in &stmt.function_def.parameters {
2132 if !first {
2133 writer.push(", ");
2134 }
2135 first = false;
2136
2137 if let Some(mode) = ¶m.mode {
2139 use crate::types::function::ParameterMode;
2140 match mode {
2141 ParameterMode::In => writer.push("IN "),
2142 ParameterMode::Out => writer.push("OUT "),
2143 ParameterMode::InOut => writer.push("INOUT "),
2144 ParameterMode::Variadic => writer.push("VARIADIC "),
2145 }
2146 }
2147
2148 if let Some(name) = ¶m.name {
2150 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2151 writer.push(" ");
2152 }
2153
2154 if let Some(param_type) = ¶m.param_type {
2156 writer.push(param_type);
2157 }
2158
2159 if let Some(default) = ¶m.default_value {
2161 writer.push(" DEFAULT ");
2162 writer.push(default);
2163 }
2164 }
2165 writer.push(")");
2166
2167 if let Some(returns) = &stmt.function_def.returns {
2169 writer.push_keyword("RETURNS");
2170 writer.push_space();
2171 writer.push(returns);
2172 }
2173
2174 if let Some(language) = &stmt.function_def.language {
2176 writer.push_keyword("LANGUAGE");
2177 writer.push_space();
2178 match language {
2179 FunctionLanguage::Sql => writer.push("SQL"),
2180 FunctionLanguage::PlPgSql => writer.push("PLPGSQL"),
2181 FunctionLanguage::C => writer.push("C"),
2182 FunctionLanguage::Custom(lang) => writer.push(lang),
2183 }
2184 }
2185
2186 if let Some(behavior) = &stmt.function_def.behavior {
2188 writer.push_space();
2189 match behavior {
2190 FunctionBehavior::Immutable => writer.push_keyword("IMMUTABLE"),
2191 FunctionBehavior::Stable => writer.push_keyword("STABLE"),
2192 FunctionBehavior::Volatile => writer.push_keyword("VOLATILE"),
2193 }
2194 }
2195
2196 if let Some(security) = &stmt.function_def.security {
2198 writer.push_space();
2199 match security {
2200 FunctionSecurity::Definer => writer.push_keyword("SECURITY DEFINER"),
2201 FunctionSecurity::Invoker => writer.push_keyword("SECURITY INVOKER"),
2202 }
2203 }
2204
2205 if let Some(body) = &stmt.function_def.body {
2207 writer.push_keyword("AS");
2208 writer.push_space();
2209 let delimiter = generate_safe_dollar_quote_delimiter(body);
2210 writer.push(&delimiter);
2211 writer.push(body);
2212 writer.push(&delimiter);
2213 }
2214
2215 writer.finish()
2216 }
2217
2218 fn build_alter_function(
2219 &self,
2220 stmt: &crate::query::AlterFunctionStatement,
2221 ) -> (String, Values) {
2222 use crate::query::function::alter_function::AlterFunctionOperation;
2223 use crate::types::{
2224 Iden,
2225 function::{FunctionBehavior, FunctionSecurity},
2226 };
2227
2228 let mut writer = SqlWriter::new();
2229
2230 writer.push_keyword("ALTER FUNCTION");
2232
2233 if let Some(name) = &stmt.name {
2235 writer.push_space();
2236 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2237 }
2238
2239 if !stmt.parameters.is_empty() {
2241 writer.push("(");
2242 let mut first = true;
2243 for param in &stmt.parameters {
2244 if !first {
2245 writer.push(", ");
2246 }
2247 first = false;
2248
2249 if let Some(name) = ¶m.name {
2251 let name_str = Iden::to_string(name.as_ref());
2252 if !name_str.is_empty() {
2253 writer.push_identifier(&name_str, |s| self.escape_iden(s));
2254 writer.push(" ");
2255 }
2256 }
2257
2258 if let Some(param_type) = ¶m.param_type {
2260 writer.push(param_type);
2261 }
2262 }
2263 writer.push(")");
2264 }
2265
2266 if let Some(operation) = &stmt.operation {
2268 writer.push_space();
2269 match operation {
2270 AlterFunctionOperation::RenameTo(new_name) => {
2271 writer.push_keyword("RENAME TO");
2272 writer.push_space();
2273 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
2274 self.escape_iden(s)
2275 });
2276 }
2277 AlterFunctionOperation::OwnerTo(new_owner) => {
2278 writer.push_keyword("OWNER TO");
2279 writer.push_space();
2280 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
2281 self.escape_iden(s)
2282 });
2283 }
2284 AlterFunctionOperation::SetSchema(new_schema) => {
2285 writer.push_keyword("SET SCHEMA");
2286 writer.push_space();
2287 writer.push_identifier(&Iden::to_string(new_schema.as_ref()), |s| {
2288 self.escape_iden(s)
2289 });
2290 }
2291 AlterFunctionOperation::SetBehavior(behavior) => match behavior {
2292 FunctionBehavior::Immutable => writer.push_keyword("IMMUTABLE"),
2293 FunctionBehavior::Stable => writer.push_keyword("STABLE"),
2294 FunctionBehavior::Volatile => writer.push_keyword("VOLATILE"),
2295 },
2296 AlterFunctionOperation::SetSecurity(security) => match security {
2297 FunctionSecurity::Definer => writer.push_keyword("SECURITY DEFINER"),
2298 FunctionSecurity::Invoker => writer.push_keyword("SECURITY INVOKER"),
2299 },
2300 }
2301 }
2302
2303 writer.finish()
2304 }
2305
2306 fn build_drop_function(&self, stmt: &crate::query::DropFunctionStatement) -> (String, Values) {
2307 use crate::types::Iden;
2308
2309 let mut writer = SqlWriter::new();
2310
2311 writer.push_keyword("DROP FUNCTION");
2313
2314 if stmt.if_exists {
2316 writer.push_keyword("IF EXISTS");
2317 }
2318
2319 if let Some(name) = &stmt.name {
2321 writer.push_space();
2322 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2323 }
2324
2325 if !stmt.parameters.is_empty() {
2327 writer.push("(");
2328 let mut first = true;
2329 for param in &stmt.parameters {
2330 if !first {
2331 writer.push(", ");
2332 }
2333 first = false;
2334
2335 if let Some(name) = ¶m.name {
2337 let name_str = Iden::to_string(name.as_ref());
2338 if !name_str.is_empty() {
2339 writer.push_identifier(&name_str, |s| self.escape_iden(s));
2340 writer.push(" ");
2341 }
2342 }
2343
2344 if let Some(param_type) = ¶m.param_type {
2346 writer.push(param_type);
2347 }
2348 }
2349 writer.push(")");
2350 }
2351
2352 if stmt.cascade {
2354 writer.push_keyword("CASCADE");
2355 }
2356
2357 writer.finish()
2358 }
2359
2360 fn build_grant(&self, stmt: &crate::dcl::GrantStatement) -> (String, Values) {
2361 use crate::dcl::Grantee;
2362
2363 let mut writer = SqlWriter::new();
2364
2365 writer.push("GRANT");
2367 writer.push_space();
2368
2369 writer.push_list(&stmt.privileges, ", ", |w, privilege| {
2371 w.push(privilege.as_sql());
2372 });
2373
2374 writer.push_keyword("ON");
2376 writer.push_space();
2377 writer.push(stmt.object_type.as_sql());
2378 writer.push_space();
2379
2380 writer.push_list(&stmt.objects, ", ", |w, obj| {
2382 w.push_identifier(&obj.to_string(), |s| self.escape_iden(s));
2383 });
2384
2385 writer.push_keyword("TO");
2387 writer.push_space();
2388
2389 writer.push_list(&stmt.grantees, ", ", |w, grantee| {
2391 match grantee {
2392 Grantee::Role(name) => {
2393 w.push_identifier(name, |s| self.escape_iden(s));
2394 }
2395 Grantee::User(_, _) => {
2396 w.push_identifier("(UNSUPPORTED_USER)", |s| self.escape_iden(s));
2398 }
2399 Grantee::Public => {
2400 w.push("PUBLIC");
2401 }
2402 Grantee::CurrentRole => {
2403 w.push("CURRENT_ROLE");
2404 }
2405 Grantee::CurrentUser => {
2406 w.push("CURRENT_USER");
2407 }
2408 Grantee::SessionUser => {
2409 w.push("SESSION_USER");
2410 }
2411 }
2412 });
2413
2414 if stmt.with_grant_option {
2416 writer.push_keyword("WITH GRANT OPTION");
2417 }
2418
2419 if let Some(grantor) = &stmt.granted_by {
2421 writer.push_keyword("GRANTED BY");
2422 writer.push_space();
2423 match grantor {
2424 Grantee::Role(name) => {
2425 writer.push_identifier(name, |s| self.escape_iden(s));
2426 }
2427 Grantee::User(_, _) => {
2428 writer.push_identifier("(UNSUPPORTED_USER)", |s| self.escape_iden(s));
2429 }
2430 Grantee::Public => {
2431 writer.push("PUBLIC");
2432 }
2433 Grantee::CurrentRole => {
2434 writer.push("CURRENT_ROLE");
2435 }
2436 Grantee::CurrentUser => {
2437 writer.push("CURRENT_USER");
2438 }
2439 Grantee::SessionUser => {
2440 writer.push("SESSION_USER");
2441 }
2442 }
2443 }
2444
2445 writer.finish()
2446 }
2447
2448 fn build_revoke(&self, stmt: &crate::dcl::RevokeStatement) -> (String, Values) {
2449 use crate::dcl::Grantee;
2450
2451 let mut writer = SqlWriter::new();
2452
2453 writer.push("REVOKE");
2455 writer.push_space();
2456
2457 if stmt.grant_option_for {
2459 writer.push("GRANT OPTION FOR");
2460 writer.push_space();
2461 }
2462
2463 writer.push_list(&stmt.privileges, ", ", |w, privilege| {
2465 w.push(privilege.as_sql());
2466 });
2467
2468 writer.push_keyword("ON");
2470 writer.push_space();
2471 writer.push(stmt.object_type.as_sql());
2472 writer.push_space();
2473
2474 writer.push_list(&stmt.objects, ", ", |w, obj| {
2476 w.push_identifier(&obj.to_string(), |s| self.escape_iden(s));
2477 });
2478
2479 writer.push_keyword("FROM");
2481 writer.push_space();
2482
2483 writer.push_list(&stmt.grantees, ", ", |w, grantee| {
2485 match grantee {
2486 Grantee::Role(name) => {
2487 w.push_identifier(name, |s| self.escape_iden(s));
2488 }
2489 Grantee::User(_, _) => {
2490 w.push_identifier("(UNSUPPORTED_USER)", |s| self.escape_iden(s));
2492 }
2493 Grantee::Public => {
2494 w.push("PUBLIC");
2495 }
2496 Grantee::CurrentRole => {
2497 w.push("CURRENT_ROLE");
2498 }
2499 Grantee::CurrentUser => {
2500 w.push("CURRENT_USER");
2501 }
2502 Grantee::SessionUser => {
2503 w.push("SESSION_USER");
2504 }
2505 }
2506 });
2507
2508 if stmt.cascade {
2510 writer.push_keyword("CASCADE");
2511 }
2512
2513 writer.finish()
2514 }
2515
2516 fn build_grant_role(&self, stmt: &crate::dcl::GrantRoleStatement) -> (String, Values) {
2517 let mut writer = SqlWriter::new();
2518
2519 writer.push("GRANT");
2521 writer.push_space();
2522
2523 writer.push_list(&stmt.roles, ", ", |w, role| {
2525 w.push_identifier(role, |s| self.escape_iden(s));
2526 });
2527
2528 writer.push_keyword("TO");
2530 writer.push_space();
2531
2532 writer.push_list(&stmt.grantees, ", ", |w, grantee| {
2534 w.push(Self::format_role_specification(grantee));
2535 });
2536
2537 if stmt.with_admin_option {
2539 writer.push_keyword("WITH ADMIN OPTION");
2540 }
2541
2542 if let Some(ref grantor) = stmt.granted_by {
2544 writer.push_keyword("GRANTED BY");
2545 writer.push_space();
2546 writer.push(Self::format_role_specification(grantor));
2547 }
2548
2549 writer.finish()
2550 }
2551
2552 fn build_revoke_role(&self, stmt: &crate::dcl::RevokeRoleStatement) -> (String, Values) {
2553 use crate::dcl::DropBehavior;
2554
2555 let mut writer = SqlWriter::new();
2556
2557 writer.push("REVOKE");
2559 writer.push_space();
2560
2561 if stmt.admin_option_for {
2563 writer.push("ADMIN OPTION FOR");
2564 writer.push_space();
2565 }
2566
2567 writer.push_list(&stmt.roles, ", ", |w, role| {
2569 w.push_identifier(role, |s| self.escape_iden(s));
2570 });
2571
2572 writer.push_keyword("FROM");
2574 writer.push_space();
2575
2576 writer.push_list(&stmt.grantees, ", ", |w, grantee| {
2578 w.push(Self::format_role_specification(grantee));
2579 });
2580
2581 if let Some(ref grantor) = stmt.granted_by {
2583 writer.push_keyword("GRANTED BY");
2584 writer.push_space();
2585 writer.push(Self::format_role_specification(grantor));
2586 }
2587
2588 if let Some(behavior) = stmt.drop_behavior {
2590 match behavior {
2591 DropBehavior::Cascade => writer.push_keyword("CASCADE"),
2592 DropBehavior::Restrict => writer.push_keyword("RESTRICT"),
2593 }
2594 }
2595
2596 writer.finish()
2597 }
2598
2599 fn build_create_role(&self, stmt: &crate::dcl::CreateRoleStatement) -> (String, Values) {
2600 use crate::dcl::RoleAttribute;
2601 use crate::value::Value;
2602
2603 let mut writer = SqlWriter::new();
2604
2605 writer.push("CREATE ROLE");
2607 writer.push_space();
2608
2609 writer.push_identifier(&stmt.role_name, |s| self.escape_iden(s));
2611
2612 if !stmt.attributes.is_empty() {
2614 writer.push_keyword("WITH");
2615 }
2616
2617 for attr in &stmt.attributes {
2619 writer.push_space();
2620 match attr {
2621 RoleAttribute::SuperUser => writer.push("SUPERUSER"),
2622 RoleAttribute::NoSuperUser => writer.push("NOSUPERUSER"),
2623 RoleAttribute::CreateDb => writer.push("CREATEDB"),
2624 RoleAttribute::NoCreateDb => writer.push("NOCREATEDB"),
2625 RoleAttribute::CreateRole => writer.push("CREATEROLE"),
2626 RoleAttribute::NoCreateRole => writer.push("NOCREATEROLE"),
2627 RoleAttribute::Inherit => writer.push("INHERIT"),
2628 RoleAttribute::NoInherit => writer.push("NOINHERIT"),
2629 RoleAttribute::Login => writer.push("LOGIN"),
2630 RoleAttribute::NoLogin => writer.push("NOLOGIN"),
2631 RoleAttribute::Replication => writer.push("REPLICATION"),
2632 RoleAttribute::NoReplication => writer.push("NOREPLICATION"),
2633 RoleAttribute::BypassRls => writer.push("BYPASSRLS"),
2634 RoleAttribute::NoBypassRls => writer.push("NOBYPASSRLS"),
2635 RoleAttribute::ConnectionLimit(limit) => {
2636 writer.push("CONNECTION LIMIT");
2637 writer.push_space();
2638 writer.push(&limit.to_string());
2639 }
2640 RoleAttribute::Password(pwd) => {
2641 writer.push("PASSWORD");
2642 writer.push_space();
2643 writer.push_value(Value::String(Some(Box::new(pwd.clone()))), |i| {
2644 self.placeholder(i)
2645 });
2646 }
2647 RoleAttribute::EncryptedPassword(pwd) => {
2648 writer.push("ENCRYPTED PASSWORD");
2649 writer.push_space();
2650 writer.push_value(Value::String(Some(Box::new(pwd.clone()))), |i| {
2651 self.placeholder(i)
2652 });
2653 }
2654 RoleAttribute::UnencryptedPassword(pwd) => {
2655 writer.push("UNENCRYPTED PASSWORD");
2656 writer.push_space();
2657 writer.push_value(Value::String(Some(Box::new(pwd.clone()))), |i| {
2658 self.placeholder(i)
2659 });
2660 }
2661 RoleAttribute::ValidUntil(timestamp) => {
2662 let escaped = timestamp.replace('\'', "''");
2665 writer.push("VALID UNTIL");
2666 writer.push_space();
2667 writer.push(&format!("'{}'", escaped));
2668 }
2669 RoleAttribute::InRole(roles) => {
2670 writer.push("IN ROLE");
2671 writer.push_space();
2672 writer.push_list(roles, ", ", |w, role| {
2673 w.push_identifier(role, |s| self.escape_iden(s));
2674 });
2675 }
2676 RoleAttribute::Role(roles) => {
2677 writer.push("ROLE");
2678 writer.push_space();
2679 writer.push_list(roles, ", ", |w, role| {
2680 w.push_identifier(role, |s| self.escape_iden(s));
2681 });
2682 }
2683 RoleAttribute::Admin(roles) => {
2684 writer.push("ADMIN");
2685 writer.push_space();
2686 writer.push_list(roles, ", ", |w, role| {
2687 w.push_identifier(role, |s| self.escape_iden(s));
2688 });
2689 }
2690 }
2691 }
2692
2693 writer.finish()
2694 }
2695
2696 fn build_drop_role(&self, stmt: &crate::dcl::DropRoleStatement) -> (String, Values) {
2697 let mut writer = SqlWriter::new();
2698
2699 writer.push("DROP ROLE");
2701 writer.push_space();
2702
2703 if stmt.if_exists {
2705 writer.push("IF EXISTS");
2706 writer.push_space();
2707 }
2708
2709 writer.push_list(&stmt.role_names, ", ", |w, role_name| {
2711 w.push_identifier(role_name, |s| self.escape_iden(s));
2712 });
2713
2714 writer.finish()
2715 }
2716
2717 fn build_alter_role(&self, stmt: &crate::dcl::AlterRoleStatement) -> (String, Values) {
2718 use crate::dcl::RoleAttribute;
2719 use crate::value::Value;
2720
2721 let mut writer = SqlWriter::new();
2722
2723 if let Some(ref new_name) = stmt.rename_to {
2725 writer.push("ALTER ROLE");
2726 writer.push_space();
2727 writer.push_identifier(&stmt.role_name, |s| self.escape_iden(s));
2728 writer.push_keyword("RENAME TO");
2729 writer.push_space();
2730 writer.push_identifier(new_name, |s| self.escape_iden(s));
2731 return writer.finish();
2732 }
2733
2734 writer.push("ALTER ROLE");
2736 writer.push_space();
2737
2738 writer.push_identifier(&stmt.role_name, |s| self.escape_iden(s));
2740
2741 if !stmt.attributes.is_empty() {
2743 writer.push_keyword("WITH");
2744 }
2745
2746 for attr in &stmt.attributes {
2748 writer.push_space();
2749 match attr {
2750 RoleAttribute::SuperUser => writer.push("SUPERUSER"),
2751 RoleAttribute::NoSuperUser => writer.push("NOSUPERUSER"),
2752 RoleAttribute::CreateDb => writer.push("CREATEDB"),
2753 RoleAttribute::NoCreateDb => writer.push("NOCREATEDB"),
2754 RoleAttribute::CreateRole => writer.push("CREATEROLE"),
2755 RoleAttribute::NoCreateRole => writer.push("NOCREATEROLE"),
2756 RoleAttribute::Inherit => writer.push("INHERIT"),
2757 RoleAttribute::NoInherit => writer.push("NOINHERIT"),
2758 RoleAttribute::Login => writer.push("LOGIN"),
2759 RoleAttribute::NoLogin => writer.push("NOLOGIN"),
2760 RoleAttribute::Replication => writer.push("REPLICATION"),
2761 RoleAttribute::NoReplication => writer.push("NOREPLICATION"),
2762 RoleAttribute::BypassRls => writer.push("BYPASSRLS"),
2763 RoleAttribute::NoBypassRls => writer.push("NOBYPASSRLS"),
2764 RoleAttribute::ConnectionLimit(limit) => {
2765 writer.push("CONNECTION LIMIT");
2766 writer.push_space();
2767 writer.push(&limit.to_string());
2768 }
2769 RoleAttribute::Password(pwd) => {
2770 writer.push("PASSWORD");
2771 writer.push_space();
2772 writer.push_value(Value::String(Some(Box::new(pwd.clone()))), |i| {
2773 self.placeholder(i)
2774 });
2775 }
2776 RoleAttribute::EncryptedPassword(pwd) => {
2777 writer.push("ENCRYPTED PASSWORD");
2778 writer.push_space();
2779 writer.push_value(Value::String(Some(Box::new(pwd.clone()))), |i| {
2780 self.placeholder(i)
2781 });
2782 }
2783 RoleAttribute::UnencryptedPassword(pwd) => {
2784 writer.push("UNENCRYPTED PASSWORD");
2785 writer.push_space();
2786 writer.push_value(Value::String(Some(Box::new(pwd.clone()))), |i| {
2787 self.placeholder(i)
2788 });
2789 }
2790 RoleAttribute::ValidUntil(timestamp) => {
2791 let escaped = timestamp.replace('\'', "''");
2794 writer.push("VALID UNTIL");
2795 writer.push_space();
2796 writer.push(&format!("'{}'", escaped));
2797 }
2798 RoleAttribute::InRole(roles) => {
2799 writer.push("IN ROLE");
2800 writer.push_space();
2801 writer.push_list(roles, ", ", |w, role| {
2802 w.push_identifier(role, |s| self.escape_iden(s));
2803 });
2804 }
2805 RoleAttribute::Role(roles) => {
2806 writer.push("ROLE");
2807 writer.push_space();
2808 writer.push_list(roles, ", ", |w, role| {
2809 w.push_identifier(role, |s| self.escape_iden(s));
2810 });
2811 }
2812 RoleAttribute::Admin(roles) => {
2813 writer.push("ADMIN");
2814 writer.push_space();
2815 writer.push_list(roles, ", ", |w, role| {
2816 w.push_identifier(role, |s| self.escape_iden(s));
2817 });
2818 }
2819 }
2820 }
2821
2822 writer.finish()
2823 }
2824
2825 fn build_create_user(&self, stmt: &crate::dcl::CreateUserStatement) -> (String, Values) {
2826 use crate::dcl::{CreateRoleStatement, RoleAttribute};
2827
2828 let mut create_role = CreateRoleStatement::new()
2830 .role(&stmt.user_name)
2831 .attribute(RoleAttribute::Login);
2832
2833 for attr in &stmt.attributes {
2835 create_role = create_role.attribute(attr.clone());
2836 }
2837
2838 self.build_create_role(&create_role)
2840 }
2841
2842 fn build_drop_user(&self, stmt: &crate::dcl::DropUserStatement) -> (String, Values) {
2843 use crate::dcl::DropRoleStatement;
2844
2845 let mut drop_role = DropRoleStatement::new();
2847 drop_role.role_names = stmt.user_names.clone();
2848 drop_role.if_exists = stmt.if_exists;
2849
2850 self.build_drop_role(&drop_role)
2852 }
2853
2854 fn build_alter_user(&self, stmt: &crate::dcl::AlterUserStatement) -> (String, Values) {
2855 use crate::dcl::AlterRoleStatement;
2856
2857 let mut alter_role = AlterRoleStatement::new().role(&stmt.user_name);
2859
2860 for attr in &stmt.attributes {
2862 alter_role = alter_role.attribute(attr.clone());
2863 }
2864
2865 self.build_alter_role(&alter_role)
2867 }
2868
2869 fn build_rename_user(&self, _stmt: &crate::dcl::RenameUserStatement) -> (String, Values) {
2870 panic!("RENAME USER is not supported by PostgreSQL. Use ALTER USER ... RENAME TO instead.");
2871 }
2872
2873 fn build_set_role(&self, stmt: &crate::dcl::SetRoleStatement) -> (String, Values) {
2874 use crate::dcl::RoleTarget;
2875
2876 let mut writer = SqlWriter::new();
2877
2878 writer.push("SET ROLE");
2879 writer.push_space();
2880
2881 match &stmt.target {
2882 Some(RoleTarget::Named(name)) => {
2883 writer.push_identifier(name, |s| self.escape_iden(s));
2884 }
2885 Some(RoleTarget::None) => {
2886 writer.push("NONE");
2887 }
2888 Some(RoleTarget::All) => {
2889 panic!("SET ROLE ALL is not supported by PostgreSQL (MySQL only)");
2890 }
2891 Some(RoleTarget::AllExcept(_)) => {
2892 panic!("SET ROLE ALL EXCEPT is not supported by PostgreSQL (MySQL only)");
2893 }
2894 Some(RoleTarget::Default) => {
2895 panic!("SET ROLE DEFAULT is not supported by PostgreSQL (MySQL only)");
2896 }
2897 None => {
2898 panic!("SET ROLE requires a role target");
2899 }
2900 }
2901
2902 writer.finish()
2903 }
2904
2905 fn build_reset_role(&self, _stmt: &crate::dcl::ResetRoleStatement) -> (String, Values) {
2906 let mut writer = SqlWriter::new();
2907 writer.push("RESET ROLE");
2908 writer.finish()
2909 }
2910
2911 fn build_set_default_role(
2912 &self,
2913 _stmt: &crate::dcl::SetDefaultRoleStatement,
2914 ) -> (String, Values) {
2915 panic!("SET DEFAULT ROLE is not supported by PostgreSQL (MySQL only)");
2916 }
2917
2918 fn escape_identifier(&self, ident: &str) -> String {
2919 self.escape_iden(ident)
2920 }
2921
2922 fn format_placeholder(&self, index: usize) -> String {
2923 self.placeholder(index)
2924 }
2925
2926 fn build_create_schema(&self, stmt: &crate::query::CreateSchemaStatement) -> (String, Values) {
2927 use crate::types::Iden;
2928
2929 let mut writer = SqlWriter::new();
2930
2931 writer.push_keyword("CREATE SCHEMA");
2933
2934 if stmt.if_not_exists {
2936 writer.push_keyword("IF NOT EXISTS");
2937 }
2938
2939 if let Some(name) = &stmt.schema_name {
2941 writer.push_space();
2942 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2943 }
2944
2945 if let Some(owner) = &stmt.authorization {
2947 writer.push_keyword("AUTHORIZATION");
2948 writer.push_space();
2949 writer.push_identifier(&Iden::to_string(owner.as_ref()), |s| self.escape_iden(s));
2950 }
2951
2952 writer.finish()
2953 }
2954
2955 fn build_alter_schema(&self, stmt: &crate::query::AlterSchemaStatement) -> (String, Values) {
2956 use crate::query::AlterSchemaOperation;
2957 use crate::types::Iden;
2958
2959 let mut writer = SqlWriter::new();
2960
2961 writer.push_keyword("ALTER SCHEMA");
2963
2964 if let Some(name) = &stmt.schema_name {
2966 writer.push_space();
2967 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
2968 }
2969
2970 if let Some(operation) = &stmt.operation {
2972 writer.push_space();
2973 match operation {
2974 AlterSchemaOperation::RenameTo(new_name) => {
2975 writer.push_keyword("RENAME TO");
2976 writer.push_space();
2977 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
2978 self.escape_iden(s)
2979 });
2980 }
2981 AlterSchemaOperation::OwnerTo(new_owner) => {
2982 writer.push_keyword("OWNER TO");
2983 writer.push_space();
2984 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
2985 self.escape_iden(s)
2986 });
2987 }
2988 }
2989 }
2990
2991 writer.finish()
2992 }
2993
2994 fn build_drop_schema(&self, stmt: &crate::query::DropSchemaStatement) -> (String, Values) {
2995 use crate::types::Iden;
2996
2997 let mut writer = SqlWriter::new();
2998
2999 writer.push_keyword("DROP SCHEMA");
3001
3002 if stmt.if_exists {
3004 writer.push_keyword("IF EXISTS");
3005 }
3006
3007 if let Some(name) = &stmt.schema_name {
3009 writer.push_space();
3010 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3011 }
3012
3013 if stmt.cascade {
3015 writer.push_keyword("CASCADE");
3016 }
3017
3018 writer.finish()
3019 }
3020
3021 fn build_create_sequence(
3022 &self,
3023 stmt: &crate::query::CreateSequenceStatement,
3024 ) -> (String, Values) {
3025 use crate::types::{Iden, sequence::OwnedBy};
3026
3027 let mut writer = SqlWriter::new();
3028 let seq_def = &stmt.sequence_def;
3029
3030 writer.push_keyword("CREATE SEQUENCE");
3032
3033 if seq_def.if_not_exists {
3035 writer.push_keyword("IF NOT EXISTS");
3036 }
3037
3038 writer.push_space();
3040 writer.push_identifier(&Iden::to_string(seq_def.name.as_ref()), |s| {
3041 self.escape_iden(s)
3042 });
3043
3044 if let Some(increment) = seq_def.increment {
3046 writer.push_keyword("INCREMENT BY");
3047 writer.push_space();
3048 writer.push(&increment.to_string());
3049 }
3050
3051 if let Some(min_value) = &seq_def.min_value {
3053 writer.push_space();
3054 match min_value {
3055 Some(val) => {
3056 writer.push_keyword("MINVALUE");
3057 writer.push_space();
3058 writer.push(&val.to_string());
3059 }
3060 None => {
3061 writer.push_keyword("NO MINVALUE");
3062 }
3063 }
3064 }
3065
3066 if let Some(max_value) = &seq_def.max_value {
3068 writer.push_space();
3069 match max_value {
3070 Some(val) => {
3071 writer.push_keyword("MAXVALUE");
3072 writer.push_space();
3073 writer.push(&val.to_string());
3074 }
3075 None => {
3076 writer.push_keyword("NO MAXVALUE");
3077 }
3078 }
3079 }
3080
3081 if let Some(start) = seq_def.start {
3083 writer.push_keyword("START WITH");
3084 writer.push_space();
3085 writer.push(&start.to_string());
3086 }
3087
3088 if let Some(cache) = seq_def.cache {
3090 writer.push_keyword("CACHE");
3091 writer.push_space();
3092 writer.push(&cache.to_string());
3093 }
3094
3095 if let Some(cycle) = seq_def.cycle {
3097 writer.push_space();
3098 if cycle {
3099 writer.push_keyword("CYCLE");
3100 } else {
3101 writer.push_keyword("NO CYCLE");
3102 }
3103 }
3104
3105 if let Some(owned_by) = &seq_def.owned_by {
3107 writer.push_keyword("OWNED BY");
3108 writer.push_space();
3109 match owned_by {
3110 OwnedBy::Column { table, column } => {
3111 writer
3112 .push_identifier(&Iden::to_string(table.as_ref()), |s| self.escape_iden(s));
3113 writer.push(".");
3114 writer.push_identifier(&Iden::to_string(column.as_ref()), |s| {
3115 self.escape_iden(s)
3116 });
3117 }
3118 OwnedBy::None => {
3119 writer.push_keyword("NONE");
3120 }
3121 }
3122 }
3123
3124 writer.finish()
3125 }
3126
3127 fn build_alter_sequence(
3128 &self,
3129 stmt: &crate::query::AlterSequenceStatement,
3130 ) -> (String, Values) {
3131 use crate::types::{
3132 Iden,
3133 sequence::{OwnedBy, SequenceOption},
3134 };
3135
3136 let mut writer = SqlWriter::new();
3137
3138 writer.push_keyword("ALTER SEQUENCE");
3140
3141 writer.push_space();
3143 writer.push_identifier(&Iden::to_string(stmt.name.as_ref()), |s| {
3144 self.escape_iden(s)
3145 });
3146
3147 for option in &stmt.options {
3149 writer.push_space();
3150 match option {
3151 SequenceOption::Restart(value) => {
3152 writer.push_keyword("RESTART");
3153 if let Some(val) = value {
3154 writer.push_keyword("WITH");
3155 writer.push_space();
3156 writer.push(&val.to_string());
3157 }
3158 }
3159 SequenceOption::IncrementBy(value) => {
3160 writer.push_keyword("INCREMENT BY");
3161 writer.push_space();
3162 writer.push(&value.to_string());
3163 }
3164 SequenceOption::MinValue(value) => {
3165 writer.push_keyword("MINVALUE");
3166 writer.push_space();
3167 writer.push(&value.to_string());
3168 }
3169 SequenceOption::NoMinValue => {
3170 writer.push_keyword("NO MINVALUE");
3171 }
3172 SequenceOption::MaxValue(value) => {
3173 writer.push_keyword("MAXVALUE");
3174 writer.push_space();
3175 writer.push(&value.to_string());
3176 }
3177 SequenceOption::NoMaxValue => {
3178 writer.push_keyword("NO MAXVALUE");
3179 }
3180 SequenceOption::Cache(value) => {
3181 writer.push_keyword("CACHE");
3182 writer.push_space();
3183 writer.push(&value.to_string());
3184 }
3185 SequenceOption::Cycle => {
3186 writer.push_keyword("CYCLE");
3187 }
3188 SequenceOption::NoCycle => {
3189 writer.push_keyword("NO CYCLE");
3190 }
3191 SequenceOption::OwnedBy(owned_by) => {
3192 writer.push_keyword("OWNED BY");
3193 writer.push_space();
3194 match owned_by {
3195 OwnedBy::Column { table, column } => {
3196 writer.push_identifier(&Iden::to_string(table.as_ref()), |s| {
3197 self.escape_iden(s)
3198 });
3199 writer.push(".");
3200 writer.push_identifier(&Iden::to_string(column.as_ref()), |s| {
3201 self.escape_iden(s)
3202 });
3203 }
3204 OwnedBy::None => {
3205 writer.push_keyword("NONE");
3206 }
3207 }
3208 }
3209 }
3210 }
3211
3212 writer.finish()
3213 }
3214
3215 fn build_drop_sequence(&self, stmt: &crate::query::DropSequenceStatement) -> (String, Values) {
3216 use crate::types::Iden;
3217
3218 let mut writer = SqlWriter::new();
3219
3220 writer.push_keyword("DROP SEQUENCE");
3222
3223 if stmt.if_exists {
3225 writer.push_keyword("IF EXISTS");
3226 }
3227
3228 writer.push_space();
3230 writer.push_identifier(&Iden::to_string(stmt.name.as_ref()), |s| {
3231 self.escape_iden(s)
3232 });
3233
3234 if stmt.cascade {
3236 writer.push_keyword("CASCADE");
3237 } else if stmt.restrict {
3238 writer.push_keyword("RESTRICT");
3239 }
3240
3241 writer.finish()
3242 }
3243
3244 fn build_comment(&self, stmt: &crate::query::CommentStatement) -> (String, Values) {
3245 use crate::types::{CommentTarget, Iden};
3246
3247 let mut writer = SqlWriter::new();
3248
3249 writer.push_keyword("COMMENT ON");
3251
3252 if let Some(target) = &stmt.target {
3254 writer.push_space();
3255 match target {
3256 CommentTarget::Table(table) => {
3257 writer.push_keyword("TABLE");
3258 writer.push_space();
3259 writer
3260 .push_identifier(&Iden::to_string(table.as_ref()), |s| self.escape_iden(s));
3261 }
3262 CommentTarget::Column(table, column) => {
3263 writer.push_keyword("COLUMN");
3264 writer.push_space();
3265 writer
3266 .push_identifier(&Iden::to_string(table.as_ref()), |s| self.escape_iden(s));
3267 writer.push(".");
3268 writer.push_identifier(&Iden::to_string(column.as_ref()), |s| {
3269 self.escape_iden(s)
3270 });
3271 }
3272 CommentTarget::Index(index) => {
3273 writer.push_keyword("INDEX");
3274 writer.push_space();
3275 writer
3276 .push_identifier(&Iden::to_string(index.as_ref()), |s| self.escape_iden(s));
3277 }
3278 CommentTarget::View(view) => {
3279 writer.push_keyword("VIEW");
3280 writer.push_space();
3281 writer
3282 .push_identifier(&Iden::to_string(view.as_ref()), |s| self.escape_iden(s));
3283 }
3284 CommentTarget::MaterializedView(view) => {
3285 writer.push_keyword("MATERIALIZED VIEW");
3286 writer.push_space();
3287 writer
3288 .push_identifier(&Iden::to_string(view.as_ref()), |s| self.escape_iden(s));
3289 }
3290 CommentTarget::Sequence(seq) => {
3291 writer.push_keyword("SEQUENCE");
3292 writer.push_space();
3293 writer.push_identifier(&Iden::to_string(seq.as_ref()), |s| self.escape_iden(s));
3294 }
3295 CommentTarget::Schema(schema) => {
3296 writer.push_keyword("SCHEMA");
3297 writer.push_space();
3298 writer.push_identifier(&Iden::to_string(schema.as_ref()), |s| {
3299 self.escape_iden(s)
3300 });
3301 }
3302 CommentTarget::Database(db) => {
3303 writer.push_keyword("DATABASE");
3304 writer.push_space();
3305 writer.push_identifier(&Iden::to_string(db.as_ref()), |s| self.escape_iden(s));
3306 }
3307 CommentTarget::Function(func) => {
3308 writer.push_keyword("FUNCTION");
3309 writer.push_space();
3310 writer
3311 .push_identifier(&Iden::to_string(func.as_ref()), |s| self.escape_iden(s));
3312 }
3313 CommentTarget::Trigger(trigger, table) => {
3314 writer.push_keyword("TRIGGER");
3315 writer.push_space();
3316 writer.push_identifier(&Iden::to_string(trigger.as_ref()), |s| {
3317 self.escape_iden(s)
3318 });
3319 writer.push_keyword("ON");
3320 writer.push_space();
3321 writer
3322 .push_identifier(&Iden::to_string(table.as_ref()), |s| self.escape_iden(s));
3323 }
3324 CommentTarget::Type(typ) => {
3325 writer.push_keyword("TYPE");
3326 writer.push_space();
3327 writer.push_identifier(&Iden::to_string(typ.as_ref()), |s| self.escape_iden(s));
3328 }
3329 }
3330 }
3331
3332 writer.push_keyword("IS");
3334 writer.push_space();
3335 if stmt.is_null {
3336 writer.push_keyword("NULL");
3337 } else if let Some(comment) = &stmt.comment {
3338 let escaped = comment.replace('\'', "''");
3340 writer.push(&format!("'{}'", escaped));
3341 }
3342
3343 writer.finish()
3344 }
3345
3346 fn build_create_database(
3347 &self,
3348 stmt: &crate::query::CreateDatabaseStatement,
3349 ) -> (String, Values) {
3350 use crate::types::Iden;
3351
3352 let mut writer = SqlWriter::new();
3353
3354 writer.push_keyword("CREATE DATABASE");
3356
3357 if let Some(name) = &stmt.database_name {
3364 writer.push_space();
3365 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3366 }
3367
3368 if let Some(owner) = &stmt.owner {
3370 writer.push_keyword("OWNER");
3371 writer.push_space();
3372 writer.push_identifier(&Iden::to_string(owner.as_ref()), |s| self.escape_iden(s));
3373 }
3374
3375 if let Some(template) = &stmt.template {
3377 writer.push_keyword("TEMPLATE");
3378 writer.push_space();
3379 writer.push_identifier(&Iden::to_string(template.as_ref()), |s| self.escape_iden(s));
3380 }
3381
3382 if let Some(encoding) = &stmt.encoding {
3384 writer.push_keyword("ENCODING");
3385 writer.push_space();
3386 let escaped = encoding.replace('\'', "''");
3387 writer.push(&format!("'{}'", escaped));
3388 }
3389
3390 if let Some(lc_collate) = &stmt.lc_collate {
3392 writer.push_keyword("LC_COLLATE");
3393 writer.push_space();
3394 let escaped = lc_collate.replace('\'', "''");
3395 writer.push(&format!("'{}'", escaped));
3396 }
3397
3398 if let Some(lc_ctype) = &stmt.lc_ctype {
3400 writer.push_keyword("LC_CTYPE");
3401 writer.push_space();
3402 let escaped = lc_ctype.replace('\'', "''");
3403 writer.push(&format!("'{}'", escaped));
3404 }
3405
3406 writer.finish()
3407 }
3408
3409 fn build_alter_database(
3410 &self,
3411 stmt: &crate::query::AlterDatabaseStatement,
3412 ) -> (String, Values) {
3413 use crate::types::{DatabaseOperation, Iden};
3414
3415 let mut writer = SqlWriter::new();
3416
3417 writer.push_keyword("ALTER DATABASE");
3419
3420 if let Some(name) = &stmt.database_name {
3422 writer.push_space();
3423 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3424 }
3425
3426 for (i, operation) in stmt.operations.iter().enumerate() {
3428 if i == 0 {
3429 writer.push_space();
3430 } else {
3431 writer.push(", ");
3432 }
3433 match operation {
3434 DatabaseOperation::RenameDatabase(new_name) => {
3435 writer.push_keyword("RENAME TO");
3436 writer.push_space();
3437 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
3438 self.escape_iden(s)
3439 });
3440 }
3441 DatabaseOperation::OwnerTo(new_owner) => {
3442 writer.push_keyword("OWNER TO");
3443 writer.push_space();
3444 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
3445 self.escape_iden(s)
3446 });
3447 }
3448 DatabaseOperation::AddRegion(region) => {
3450 writer.push_keyword("ADD REGION");
3452 writer.push_space();
3453 let escaped = region.replace('\'', "''");
3454 writer.push(&format!("'{}'", escaped));
3455 }
3456 DatabaseOperation::DropRegion(region) => {
3457 writer.push_keyword("DROP REGION");
3459 writer.push_space();
3460 let escaped = region.replace('\'', "''");
3461 writer.push(&format!("'{}'", escaped));
3462 }
3463 DatabaseOperation::SetPrimaryRegion(region) => {
3464 writer.push_keyword("PRIMARY REGION");
3466 writer.push_space();
3467 let escaped = region.replace('\'', "''");
3468 writer.push(&format!("'{}'", escaped));
3469 }
3470 DatabaseOperation::ConfigureZone(zone_config) => {
3471 writer.push_keyword("CONFIGURE ZONE USING");
3473 writer.push_space();
3474
3475 let mut parts = Vec::new();
3476
3477 if let Some(num_replicas) = zone_config.num_replicas {
3478 parts.push(format!("num_replicas = {}", num_replicas));
3479 }
3480
3481 if !zone_config.constraints.is_empty() {
3482 let constraints: Vec<String> = zone_config
3483 .constraints
3484 .iter()
3485 .map(ToString::to_string)
3486 .collect();
3487 parts.push(format!("constraints = '[{}]'", constraints.join(", ")));
3488 }
3489
3490 if !zone_config.lease_preferences.is_empty() {
3491 let prefs: Vec<String> = zone_config
3492 .lease_preferences
3493 .iter()
3494 .map(|p| format!("[{}]", p))
3495 .collect();
3496 parts.push(format!("lease_preferences = '[{}]'", prefs.join(", ")));
3497 }
3498
3499 writer.push(&parts.join(", "));
3500 }
3501 }
3502 }
3503
3504 writer.finish()
3505 }
3506
3507 fn build_drop_database(&self, stmt: &crate::query::DropDatabaseStatement) -> (String, Values) {
3508 use crate::types::Iden;
3509
3510 let mut writer = SqlWriter::new();
3511
3512 writer.push_keyword("DROP DATABASE");
3514
3515 if stmt.if_exists {
3517 writer.push_keyword("IF EXISTS");
3518 }
3519
3520 if let Some(name) = &stmt.database_name {
3522 writer.push_space();
3523 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3524 }
3525
3526 if stmt.force {
3528 writer.push_space();
3529 writer.push_keyword("WITH");
3530 writer.push(" (");
3531 writer.push("FORCE");
3532 writer.push(")");
3533 }
3534
3535 writer.finish()
3536 }
3537
3538 fn build_analyze(&self, stmt: &crate::query::AnalyzeStatement) -> (String, Values) {
3539 use crate::types::Iden;
3540 let mut writer = SqlWriter::new();
3541
3542 writer.push_keyword("ANALYZE");
3543
3544 if stmt.verbose {
3545 writer.push_keyword("VERBOSE");
3546 }
3547
3548 if !stmt.tables.is_empty() {
3550 writer.push_space();
3551 writer.push_list(&stmt.tables, ", ", |w, table| {
3552 w.push_identifier(&Iden::to_string(table.table.as_ref()), |s| {
3553 self.escape_iden(s)
3554 });
3555 if !table.columns.is_empty() {
3556 w.push(" (");
3557 w.push_list(&table.columns, ", ", |w2, col| {
3558 w2.push_identifier(&Iden::to_string(col.as_ref()), |s| self.escape_iden(s));
3559 });
3560 w.push(")");
3561 }
3562 });
3563 }
3564
3565 writer.finish()
3566 }
3567
3568 fn build_vacuum(&self, stmt: &crate::query::VacuumStatement) -> (String, Values) {
3569 use crate::types::Iden;
3570 let mut writer = SqlWriter::new();
3571
3572 writer.push_keyword("VACUUM");
3573
3574 if stmt.full {
3576 writer.push_keyword("FULL");
3577 }
3578 if stmt.freeze {
3579 writer.push_keyword("FREEZE");
3580 }
3581 if stmt.verbose {
3582 writer.push_keyword("VERBOSE");
3583 }
3584 if stmt.analyze {
3585 writer.push_keyword("ANALYZE");
3586 }
3587
3588 if !stmt.tables.is_empty() {
3590 writer.push_space();
3591 writer.push_list(&stmt.tables, ", ", |w, table| {
3592 w.push_identifier(&Iden::to_string(table.as_ref()), |s| self.escape_iden(s));
3593 });
3594 }
3595
3596 writer.finish()
3597 }
3598
3599 fn build_create_materialized_view(
3600 &self,
3601 stmt: &crate::query::CreateMaterializedViewStatement,
3602 ) -> (String, Values) {
3603 use crate::types::Iden;
3604 let mut writer = SqlWriter::new();
3605
3606 writer.push_keyword("CREATE MATERIALIZED VIEW");
3607
3608 if stmt.def.if_not_exists {
3610 writer.push_keyword("IF NOT EXISTS");
3611 }
3612
3613 writer.push_space();
3615 writer.push_identifier(&Iden::to_string(stmt.def.name.as_ref()), |s| {
3616 self.escape_iden(s)
3617 });
3618
3619 if !stmt.def.columns.is_empty() {
3621 writer.push_space();
3622 writer.push("(");
3623 writer.push_list(&stmt.def.columns, ", ", |w, col| {
3624 w.push_identifier(&Iden::to_string(col.as_ref()), |s| self.escape_iden(s));
3625 });
3626 writer.push(")");
3627 }
3628
3629 if let Some(ref tablespace) = stmt.def.tablespace {
3631 writer.push_keyword("TABLESPACE");
3632 writer.push_space();
3633 writer.push_identifier(&Iden::to_string(tablespace.as_ref()), |s| {
3634 self.escape_iden(s)
3635 });
3636 }
3637
3638 if let Some(ref select) = stmt.select {
3640 writer.push_keyword("AS");
3641 writer.push_space();
3642 let (select_sql, select_values) = self.build_select(select);
3643 writer.push(&select_sql);
3644
3645 if let Some(with_data) = stmt.def.with_data {
3647 writer.push_space();
3648 if with_data {
3649 writer.push_keyword("WITH DATA");
3650 } else {
3651 writer.push_keyword("WITH NO DATA");
3652 }
3653 }
3654
3655 let (sql, _) = writer.finish();
3656 return (sql, select_values);
3657 }
3658
3659 writer.finish()
3660 }
3661
3662 fn build_alter_materialized_view(
3663 &self,
3664 stmt: &crate::query::AlterMaterializedViewStatement,
3665 ) -> (String, Values) {
3666 use crate::types::{Iden, MaterializedViewOperation};
3667 let mut writer = SqlWriter::new();
3668
3669 writer.push_keyword("ALTER MATERIALIZED VIEW");
3670
3671 if let Some(ref name) = stmt.name {
3673 writer.push_space();
3674 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3675 }
3676
3677 for operation in &stmt.operations {
3679 writer.push_space();
3680 match operation {
3681 MaterializedViewOperation::Rename(new_name) => {
3682 writer.push_keyword("RENAME TO");
3683 writer.push_space();
3684 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
3685 self.escape_iden(s)
3686 });
3687 }
3688 MaterializedViewOperation::OwnerTo(new_owner) => {
3689 writer.push_keyword("OWNER TO");
3690 writer.push_space();
3691 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
3692 self.escape_iden(s)
3693 });
3694 }
3695 MaterializedViewOperation::SetSchema(schema_name) => {
3696 writer.push_keyword("SET SCHEMA");
3697 writer.push_space();
3698 writer.push_identifier(&Iden::to_string(schema_name.as_ref()), |s| {
3699 self.escape_iden(s)
3700 });
3701 }
3702 }
3703 }
3704
3705 writer.finish()
3706 }
3707
3708 fn build_drop_materialized_view(
3709 &self,
3710 stmt: &crate::query::DropMaterializedViewStatement,
3711 ) -> (String, Values) {
3712 use crate::types::Iden;
3713 let mut writer = SqlWriter::new();
3714
3715 writer.push_keyword("DROP MATERIALIZED VIEW");
3716
3717 if stmt.if_exists {
3719 writer.push_keyword("IF EXISTS");
3720 }
3721
3722 writer.push_space();
3724 writer.push_list(&stmt.names, ", ", |w, name| {
3725 w.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3726 });
3727
3728 if stmt.cascade {
3730 writer.push_keyword("CASCADE");
3731 } else if stmt.restrict {
3732 writer.push_keyword("RESTRICT");
3733 }
3734
3735 writer.finish()
3736 }
3737
3738 fn build_refresh_materialized_view(
3739 &self,
3740 stmt: &crate::query::RefreshMaterializedViewStatement,
3741 ) -> (String, Values) {
3742 use crate::types::Iden;
3743 let mut writer = SqlWriter::new();
3744
3745 writer.push_keyword("REFRESH MATERIALIZED VIEW");
3746
3747 if stmt.concurrently {
3749 writer.push_keyword("CONCURRENTLY");
3750 }
3751
3752 if let Some(ref name) = stmt.name {
3754 writer.push_space();
3755 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3756 }
3757
3758 if let Some(with_data) = stmt.with_data {
3760 writer.push_space();
3761 if with_data {
3762 writer.push_keyword("WITH DATA");
3763 } else {
3764 writer.push_keyword("WITH NO DATA");
3765 }
3766 }
3767
3768 writer.finish()
3769 }
3770
3771 fn build_create_procedure(
3772 &self,
3773 stmt: &crate::query::CreateProcedureStatement,
3774 ) -> (String, Values) {
3775 use crate::types::{
3776 Iden,
3777 function::{FunctionBehavior, FunctionLanguage, FunctionSecurity},
3778 };
3779
3780 let mut writer = SqlWriter::new();
3781
3782 writer.push_keyword("CREATE");
3784 if stmt.procedure_def.or_replace {
3785 writer.push_keyword("OR REPLACE");
3786 }
3787 writer.push_keyword("PROCEDURE");
3788
3789 writer.push_space();
3791 writer.push_identifier(&Iden::to_string(stmt.procedure_def.name.as_ref()), |s| {
3792 self.escape_iden(s)
3793 });
3794
3795 writer.push("(");
3797 let mut first = true;
3798 for param in &stmt.procedure_def.parameters {
3799 if !first {
3800 writer.push(", ");
3801 }
3802 first = false;
3803
3804 if let Some(mode) = ¶m.mode {
3806 use crate::types::function::ParameterMode;
3807 match mode {
3808 ParameterMode::In => writer.push("IN "),
3809 ParameterMode::Out => writer.push("OUT "),
3810 ParameterMode::InOut => writer.push("INOUT "),
3811 ParameterMode::Variadic => writer.push("VARIADIC "),
3812 }
3813 }
3814
3815 if let Some(name) = ¶m.name {
3817 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3818 writer.push(" ");
3819 }
3820
3821 if let Some(param_type) = ¶m.param_type {
3823 writer.push(param_type);
3824 }
3825
3826 if let Some(default) = ¶m.default_value {
3828 writer.push(" DEFAULT ");
3829 writer.push(default);
3830 }
3831 }
3832 writer.push(")");
3833
3834 if let Some(language) = &stmt.procedure_def.language {
3836 writer.push_keyword("LANGUAGE");
3837 writer.push_space();
3838 match language {
3839 FunctionLanguage::Sql => writer.push("SQL"),
3840 FunctionLanguage::PlPgSql => writer.push("PLPGSQL"),
3841 FunctionLanguage::C => writer.push("C"),
3842 FunctionLanguage::Custom(lang) => writer.push(lang),
3843 }
3844 }
3845
3846 if let Some(behavior) = &stmt.procedure_def.behavior {
3848 writer.push_space();
3849 match behavior {
3850 FunctionBehavior::Immutable => writer.push_keyword("IMMUTABLE"),
3851 FunctionBehavior::Stable => writer.push_keyword("STABLE"),
3852 FunctionBehavior::Volatile => writer.push_keyword("VOLATILE"),
3853 }
3854 }
3855
3856 if let Some(security) = &stmt.procedure_def.security {
3858 writer.push_space();
3859 match security {
3860 FunctionSecurity::Definer => writer.push_keyword("SECURITY DEFINER"),
3861 FunctionSecurity::Invoker => writer.push_keyword("SECURITY INVOKER"),
3862 }
3863 }
3864
3865 if let Some(body) = &stmt.procedure_def.body {
3867 writer.push_keyword("AS");
3868 writer.push_space();
3869 let delimiter = generate_safe_dollar_quote_delimiter(body);
3870 writer.push(&delimiter);
3871 writer.push(body);
3872 writer.push(&delimiter);
3873 }
3874
3875 writer.finish()
3876 }
3877
3878 fn build_alter_procedure(
3879 &self,
3880 stmt: &crate::query::AlterProcedureStatement,
3881 ) -> (String, Values) {
3882 use crate::types::{
3883 Iden,
3884 function::{FunctionBehavior, FunctionSecurity},
3885 procedure::ProcedureOperation,
3886 };
3887
3888 let mut writer = SqlWriter::new();
3889
3890 writer.push_keyword("ALTER PROCEDURE");
3892
3893 if let Some(name) = &stmt.name {
3895 writer.push_space();
3896 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3897 }
3898
3899 if !stmt.parameters.is_empty() {
3901 writer.push("(");
3902 let mut first = true;
3903 for param in &stmt.parameters {
3904 if !first {
3905 writer.push(", ");
3906 }
3907 first = false;
3908
3909 if let Some(name) = ¶m.name {
3911 let name_str = Iden::to_string(name.as_ref());
3912 if !name_str.is_empty() {
3913 writer.push_identifier(&name_str, |s| self.escape_iden(s));
3914 writer.push(" ");
3915 }
3916 }
3917
3918 if let Some(param_type) = ¶m.param_type {
3920 writer.push(param_type);
3921 }
3922 }
3923 writer.push(")");
3924 }
3925
3926 if let Some(operation) = &stmt.operation {
3928 writer.push_space();
3929 match operation {
3930 ProcedureOperation::RenameTo(new_name) => {
3931 writer.push_keyword("RENAME TO");
3932 writer.push_space();
3933 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
3934 self.escape_iden(s)
3935 });
3936 }
3937 ProcedureOperation::OwnerTo(new_owner) => {
3938 writer.push_keyword("OWNER TO");
3939 writer.push_space();
3940 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
3941 self.escape_iden(s)
3942 });
3943 }
3944 ProcedureOperation::SetSchema(new_schema) => {
3945 writer.push_keyword("SET SCHEMA");
3946 writer.push_space();
3947 writer.push_identifier(&Iden::to_string(new_schema.as_ref()), |s| {
3948 self.escape_iden(s)
3949 });
3950 }
3951 ProcedureOperation::SetBehavior(behavior) => match behavior {
3952 FunctionBehavior::Immutable => writer.push_keyword("IMMUTABLE"),
3953 FunctionBehavior::Stable => writer.push_keyword("STABLE"),
3954 FunctionBehavior::Volatile => writer.push_keyword("VOLATILE"),
3955 },
3956 ProcedureOperation::SetSecurity(security) => match security {
3957 FunctionSecurity::Definer => writer.push_keyword("SECURITY DEFINER"),
3958 FunctionSecurity::Invoker => writer.push_keyword("SECURITY INVOKER"),
3959 },
3960 }
3961 }
3962
3963 writer.finish()
3964 }
3965
3966 fn build_drop_procedure(
3967 &self,
3968 stmt: &crate::query::DropProcedureStatement,
3969 ) -> (String, Values) {
3970 use crate::types::Iden;
3971
3972 let mut writer = SqlWriter::new();
3973
3974 writer.push_keyword("DROP PROCEDURE");
3976
3977 if stmt.if_exists {
3979 writer.push_keyword("IF EXISTS");
3980 }
3981
3982 if let Some(name) = &stmt.name {
3984 writer.push_space();
3985 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3986 }
3987
3988 if !stmt.parameters.is_empty() {
3990 writer.push("(");
3991 let mut first = true;
3992 for param in &stmt.parameters {
3993 if !first {
3994 writer.push(", ");
3995 }
3996 first = false;
3997
3998 if let Some(name) = ¶m.name {
4000 let name_str = Iden::to_string(name.as_ref());
4001 if !name_str.is_empty() {
4002 writer.push_identifier(&name_str, |s| self.escape_iden(s));
4003 writer.push(" ");
4004 }
4005 }
4006
4007 if let Some(param_type) = ¶m.param_type {
4009 writer.push(param_type);
4010 }
4011 }
4012 writer.push(")");
4013 }
4014
4015 if stmt.cascade {
4017 writer.push_keyword("CASCADE");
4018 }
4019
4020 writer.finish()
4021 }
4022 fn build_create_type(&self, stmt: &crate::query::CreateTypeStatement) -> (String, Values) {
4024 let mut writer = SqlWriter::new();
4025
4026 writer.push_keyword("CREATE TYPE");
4027 writer.push_space();
4028
4029 if let Some(name) = &stmt.name {
4031 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
4032 }
4033
4034 if let Some(kind) = &stmt.kind {
4036 use crate::types::type_def::TypeKind;
4037 match kind {
4038 TypeKind::Enum { values } => {
4039 writer.push_space();
4040 writer.push_keyword("AS ENUM");
4041 writer.push_space();
4042 writer.push("(");
4043 writer.push_list(values, ", ", |w, value| {
4044 w.push("'");
4045 w.push(&value.replace('\'', "''"));
4046 w.push("'");
4047 });
4048 writer.push(")");
4049 }
4050 TypeKind::Composite { attributes } => {
4051 writer.push_space();
4052 writer.push_keyword("AS");
4053 writer.push_space();
4054 writer.push("(");
4055 writer.push_list(attributes, ", ", |w, (name, type_name)| {
4056 w.push_identifier(name, |s| self.escape_iden(s));
4057 w.push_space();
4058 w.push(type_name);
4059 });
4060 writer.push(")");
4061 }
4062 TypeKind::Domain {
4063 base_type,
4064 constraint,
4065 default,
4066 not_null,
4067 } => {
4068 writer.push_space();
4069 writer.push_keyword("AS");
4070 writer.push_space();
4071 writer.push(base_type);
4072
4073 if let Some(default_val) = default {
4075 writer.push_space();
4076 writer.push_keyword("DEFAULT");
4077 writer.push_space();
4078 writer.push(default_val);
4079 }
4080
4081 if let Some(check) = constraint {
4083 writer.push_space();
4084 writer.push(check);
4085 }
4086
4087 if *not_null {
4089 writer.push_space();
4090 writer.push_keyword("NOT NULL");
4091 }
4092 }
4093 TypeKind::Range {
4094 subtype,
4095 subtype_diff,
4096 canonical,
4097 } => {
4098 writer.push_space();
4099 writer.push_keyword("AS RANGE");
4100 writer.push_space();
4101 writer.push("(");
4102 writer.push("SUBTYPE = ");
4103 writer.push(subtype);
4104
4105 if let Some(diff_fn) = subtype_diff {
4107 writer.push(", SUBTYPE_DIFF = ");
4108 writer.push(diff_fn);
4109 }
4110
4111 if let Some(canonical_fn) = canonical {
4113 writer.push(", CANONICAL = ");
4114 writer.push(canonical_fn);
4115 }
4116
4117 writer.push(")");
4118 }
4119 }
4120 }
4121
4122 writer.finish()
4123 }
4124
4125 fn build_alter_type(&self, stmt: &crate::query::AlterTypeStatement) -> (String, Values) {
4126 let mut writer = SqlWriter::new();
4127
4128 writer.push_keyword("ALTER TYPE");
4129 writer.push_space();
4130 writer.push_identifier(&stmt.name.to_string(), |s| self.escape_iden(s));
4131
4132 for operation in &stmt.operations {
4134 writer.push_space();
4135 use crate::types::type_def::TypeOperation;
4136 match operation {
4137 TypeOperation::RenameTo(new_name) => {
4138 writer.push_keyword("RENAME TO");
4139 writer.push_space();
4140 writer.push_identifier(&new_name.to_string(), |s| self.escape_iden(s));
4141 }
4142 TypeOperation::OwnerTo(owner) => {
4143 writer.push_keyword("OWNER TO");
4144 writer.push_space();
4145 writer.push_identifier(&owner.to_string(), |s| self.escape_iden(s));
4146 }
4147 TypeOperation::SetSchema(schema) => {
4148 writer.push_keyword("SET SCHEMA");
4149 writer.push_space();
4150 writer.push_identifier(&schema.to_string(), |s| self.escape_iden(s));
4151 }
4152 TypeOperation::AddValue(value, position) => {
4153 writer.push_keyword("ADD VALUE");
4154 writer.push_space();
4155 writer.push("'");
4156 writer.push(&value.replace('\'', "''"));
4157 writer.push("'");
4158
4159 if let Some(pos) = position {
4160 writer.push_space();
4161 writer.push_keyword("BEFORE");
4162 writer.push_space();
4163 writer.push("'");
4164 writer.push(&pos.replace('\'', "''"));
4165 writer.push("'");
4166 }
4167 }
4168 TypeOperation::RenameValue(old_value, new_value) => {
4169 writer.push_keyword("RENAME VALUE");
4170 writer.push_space();
4171 writer.push("'");
4172 writer.push(&old_value.replace('\'', "''"));
4173 writer.push("'");
4174 writer.push_space();
4175 writer.push_keyword("TO");
4176 writer.push_space();
4177 writer.push("'");
4178 writer.push(&new_value.replace('\'', "''"));
4179 writer.push("'");
4180 }
4181 TypeOperation::AddConstraint(name, check) => {
4182 writer.push_keyword("ADD CONSTRAINT");
4183 writer.push_space();
4184 writer.push_identifier(name, |s| self.escape_iden(s));
4185 writer.push_space();
4186 writer.push(check);
4187 }
4188 TypeOperation::DropConstraint(name, if_exists) => {
4189 writer.push_keyword("DROP CONSTRAINT");
4190 if *if_exists {
4191 writer.push_space();
4192 writer.push_keyword("IF EXISTS");
4193 }
4194 writer.push_space();
4195 writer.push_identifier(name, |s| self.escape_iden(s));
4196 }
4197 TypeOperation::SetDefault(value) => {
4198 writer.push_keyword("SET DEFAULT");
4199 writer.push_space();
4200 writer.push(value);
4201 }
4202 TypeOperation::DropDefault => {
4203 writer.push_keyword("DROP DEFAULT");
4204 }
4205 TypeOperation::SetNotNull => {
4206 writer.push_keyword("SET NOT NULL");
4207 }
4208 TypeOperation::DropNotNull => {
4209 writer.push_keyword("DROP NOT NULL");
4210 }
4211 }
4212 }
4213
4214 writer.finish()
4215 }
4216
4217 fn build_drop_type(&self, stmt: &crate::query::DropTypeStatement) -> (String, Values) {
4218 let mut writer = SqlWriter::new();
4219
4220 writer.push_keyword("DROP TYPE");
4221 writer.push_space();
4222
4223 if stmt.if_exists {
4225 writer.push_keyword("IF EXISTS");
4226 writer.push_space();
4227 }
4228
4229 writer.push_identifier(&stmt.name.to_string(), |s| self.escape_iden(s));
4231
4232 if stmt.cascade {
4234 writer.push_space();
4235 writer.push_keyword("CASCADE");
4236 } else if stmt.restrict {
4237 writer.push_space();
4238 writer.push_keyword("RESTRICT");
4239 }
4240
4241 writer.finish()
4242 }
4243}
4244
4245impl PostgresQueryBuilder {
4247 #[allow(clippy::only_used_in_recursion)]
4254 fn column_type_to_sql(&self, col_type: &crate::types::ColumnType) -> String {
4255 use crate::types::ColumnType;
4256 match col_type {
4257 ColumnType::Char(len) => format!("CHAR({})", len.unwrap_or(1)),
4258 ColumnType::String(len) => {
4259 if let Some(l) = len {
4260 format!("VARCHAR({})", l)
4261 } else {
4262 "VARCHAR".to_string()
4263 }
4264 }
4265 ColumnType::Text => "TEXT".to_string(),
4266 ColumnType::TinyInteger => "SMALLINT".to_string(),
4267 ColumnType::SmallInteger => "SMALLINT".to_string(),
4268 ColumnType::Integer => "INTEGER".to_string(),
4269 ColumnType::BigInteger => "BIGINT".to_string(),
4270 ColumnType::Float => "REAL".to_string(),
4271 ColumnType::Double => "DOUBLE PRECISION".to_string(),
4272 ColumnType::Decimal(precision) => {
4273 if let Some((p, s)) = precision {
4275 format!("NUMERIC({}, {})", p, s)
4276 } else {
4277 "NUMERIC".to_string()
4278 }
4279 }
4280 ColumnType::DateTime => "TIMESTAMP".to_string(),
4281 ColumnType::Timestamp => "TIMESTAMP".to_string(),
4282 ColumnType::TimestampWithTimeZone => "TIMESTAMP WITH TIME ZONE".to_string(),
4283 ColumnType::Time => "TIME".to_string(),
4284 ColumnType::Date => "DATE".to_string(),
4285 ColumnType::Binary(_len) => {
4286 "BYTEA".to_string()
4288 }
4289 ColumnType::VarBinary(_len) => {
4290 "BYTEA".to_string()
4292 }
4293 ColumnType::Blob => "BYTEA".to_string(),
4294 ColumnType::Boolean => "BOOLEAN".to_string(),
4295 ColumnType::Json => "JSON".to_string(),
4296 ColumnType::JsonBinary => "JSONB".to_string(),
4297 ColumnType::Uuid => "UUID".to_string(),
4298 ColumnType::Array(inner_type) => {
4299 format!("{}[]", self.column_type_to_sql(inner_type))
4300 }
4301 ColumnType::Custom(name) => name.clone(),
4302 }
4303 }
4304
4305 fn write_table_constraint(
4306 &self,
4307 writer: &mut SqlWriter,
4308 constraint: &crate::types::TableConstraint,
4309 ) {
4310 use crate::types::TableConstraint;
4311 match constraint {
4312 TableConstraint::PrimaryKey { name, columns } => {
4313 if let Some(n) = name {
4314 writer.push_keyword("CONSTRAINT");
4315 writer.push_space();
4316 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4317 writer.push_space();
4318 }
4319 writer.push_keyword("PRIMARY KEY");
4320 writer.push_space();
4321 writer.push("(");
4322 writer.push_list(columns, ", ", |w, col| {
4323 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4324 });
4325 writer.push(")");
4326 }
4327 TableConstraint::Unique { name, columns } => {
4328 if let Some(n) = name {
4329 writer.push_keyword("CONSTRAINT");
4330 writer.push_space();
4331 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4332 writer.push_space();
4333 }
4334 writer.push_keyword("UNIQUE");
4335 writer.push_space();
4336 writer.push("(");
4337 writer.push_list(columns, ", ", |w, col| {
4338 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4339 });
4340 writer.push(")");
4341 }
4342 TableConstraint::ForeignKey {
4343 name,
4344 columns,
4345 ref_table,
4346 ref_columns,
4347 on_delete,
4348 on_update,
4349 } => {
4350 if let Some(n) = name {
4351 writer.push_keyword("CONSTRAINT");
4352 writer.push_space();
4353 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4354 writer.push_space();
4355 }
4356 writer.push_keyword("FOREIGN KEY");
4357 writer.push_space();
4358 writer.push("(");
4359 writer.push_list(columns, ", ", |w, col| {
4360 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4361 });
4362 writer.push(")");
4363 writer.push_space();
4364 writer.push_keyword("REFERENCES");
4365 writer.push_space();
4366 self.write_table_ref(writer, ref_table);
4367 writer.push_space();
4368 writer.push("(");
4369 writer.push_list(ref_columns, ", ", |w, col| {
4370 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4371 });
4372 writer.push(")");
4373 if let Some(action) = on_delete {
4374 writer.push_space();
4375 writer.push_keyword("ON DELETE");
4376 writer.push_space();
4377 writer.push_keyword(self.foreign_key_action_to_sql(action));
4378 }
4379 if let Some(action) = on_update {
4380 writer.push_space();
4381 writer.push_keyword("ON UPDATE");
4382 writer.push_space();
4383 writer.push_keyword(self.foreign_key_action_to_sql(action));
4384 }
4385 }
4386 TableConstraint::Check { name, expr } => {
4387 if let Some(n) = name {
4388 writer.push_keyword("CONSTRAINT");
4389 writer.push_space();
4390 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4391 writer.push_space();
4392 }
4393 writer.push_keyword("CHECK");
4394 writer.push_space();
4395 writer.push("(");
4396 self.write_simple_expr(writer, expr);
4397 writer.push(")");
4398 }
4399 }
4400 }
4401
4402 fn foreign_key_action_to_sql(&self, action: &crate::types::ForeignKeyAction) -> &'static str {
4403 use crate::types::ForeignKeyAction;
4404 match action {
4405 ForeignKeyAction::Restrict => "RESTRICT",
4406 ForeignKeyAction::Cascade => "CASCADE",
4407 ForeignKeyAction::SetNull => "SET NULL",
4408 ForeignKeyAction::SetDefault => "SET DEFAULT",
4409 ForeignKeyAction::NoAction => "NO ACTION",
4410 }
4411 }
4412
4413 fn index_method_to_sql(&self, method: &crate::query::IndexMethod) -> &'static str {
4414 use crate::query::IndexMethod;
4415 match method {
4416 IndexMethod::BTree => "BTREE",
4417 IndexMethod::Hash => "HASH",
4418 IndexMethod::Gist => "GIST",
4419 IndexMethod::Gin => "GIN",
4420 IndexMethod::Brin => "BRIN",
4421 IndexMethod::FullText => "GIN", IndexMethod::Spatial => "GIST", }
4424 }
4425}
4426
4427impl PostgresQueryBuilder {
4428 fn format_role_specification(spec: &crate::dcl::RoleSpecification) -> &str {
4438 use crate::dcl::RoleSpecification;
4439
4440 match spec {
4441 RoleSpecification::RoleName(name) => name,
4442 RoleSpecification::CurrentRole => "CURRENT_ROLE",
4443 RoleSpecification::CurrentUser => "CURRENT_USER",
4444 RoleSpecification::SessionUser => "SESSION_USER",
4445 }
4446 }
4447}
4448
4449fn collect_dollar_quote_delimiters(body: &str) -> std::collections::HashSet<String> {
4460 let mut delimiters = std::collections::HashSet::new();
4461 let bytes = body.as_bytes();
4462 let len = bytes.len();
4463 let mut i = 0;
4464
4465 while i < len {
4466 if bytes[i] == b'$' {
4467 let start = i;
4469 i += 1;
4470
4471 if i < len && bytes[i] == b'$' {
4473 delimiters.insert("$$".to_string());
4474 i += 1;
4475 continue;
4476 }
4477
4478 if i < len && (bytes[i].is_ascii_alphabetic() || bytes[i] == b'_') {
4480 let tag_start = i;
4481 i += 1;
4482 while i < len && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
4483 i += 1;
4484 }
4485 if i < len && bytes[i] == b'$' {
4487 let delimiter = &body[start..=i];
4488 delimiters.insert(delimiter.to_string());
4489 i += 1;
4490 continue;
4491 }
4492 i = tag_start;
4494 continue;
4495 }
4496
4497 continue;
4499 }
4500 i += 1;
4501 }
4502
4503 delimiters
4504}
4505
4506fn generate_safe_dollar_quote_delimiter(body: &str) -> String {
4513 let existing = collect_dollar_quote_delimiters(body);
4514
4515 if !existing.contains("$$") {
4516 return "$$".to_string();
4517 }
4518
4519 for i in 0u64.. {
4521 let candidate = format!("$body_{}$", i);
4522 if !existing.contains(&candidate) {
4523 return candidate;
4524 }
4525 }
4526
4527 "$$".to_string()
4529}
4530
4531fn utf8_char_width(leading_byte: u8) -> usize {
4537 if leading_byte < 0x80 {
4538 1
4539 } else if leading_byte < 0xE0 {
4540 2
4541 } else if leading_byte < 0xF0 {
4542 3
4543 } else {
4544 4
4545 }
4546}
4547
4548impl crate::query::QueryBuilderTrait for PostgresQueryBuilder {
4549 fn placeholder(&self) -> (&str, bool) {
4550 ("$", true)
4551 }
4552
4553 fn quote_char(&self) -> char {
4554 '"'
4555 }
4556}
4557
4558#[cfg(test)]
4559mod tests {
4560 use super::*;
4561 use crate::{
4562 expr::{Expr, ExprTrait},
4563 query::Query,
4564 types::{Alias, IntoIden},
4565 };
4566 use rstest::rstest;
4567
4568 #[test]
4569 fn test_escape_identifier() {
4570 let builder = PostgresQueryBuilder::new();
4571 assert_eq!(builder.escape_identifier("user"), "\"user\"");
4572 assert_eq!(builder.escape_identifier("table_name"), "\"table_name\"");
4573 }
4574
4575 #[test]
4576 fn test_escape_identifier_with_quotes() {
4577 let builder = PostgresQueryBuilder::new();
4578 assert_eq!(builder.escape_identifier("user\"name"), "\"user\"\"name\"");
4579 }
4580
4581 #[test]
4582 fn test_format_placeholder() {
4583 let builder = PostgresQueryBuilder::new();
4584 assert_eq!(builder.format_placeholder(1), "$1");
4585 assert_eq!(builder.format_placeholder(2), "$2");
4586 assert_eq!(builder.format_placeholder(10), "$10");
4587 }
4588
4589 #[test]
4590 fn test_select_basic() {
4591 let builder = PostgresQueryBuilder::new();
4592 let mut stmt = Query::select();
4593 stmt.column("id").column("name").from("users");
4594
4595 let (sql, values) = builder.build_select(&stmt);
4596 assert_eq!(sql, "SELECT \"id\", \"name\" FROM \"users\"");
4597 assert_eq!(values.len(), 0);
4598 }
4599
4600 #[test]
4601 fn test_select_asterisk() {
4602 let builder = PostgresQueryBuilder::new();
4603 let mut stmt = Query::select();
4604 stmt.from("users");
4605
4606 let (sql, values) = builder.build_select(&stmt);
4607 assert_eq!(sql, "SELECT * FROM \"users\"");
4608 assert_eq!(values.len(), 0);
4609 }
4610
4611 #[test]
4612 fn test_select_with_where() {
4613 let builder = PostgresQueryBuilder::new();
4614 let mut stmt = Query::select();
4615 stmt.column("id")
4616 .from("users")
4617 .and_where(Expr::col("active").eq(true));
4618
4619 let (sql, _values) = builder.build_select(&stmt);
4620 assert!(sql.contains("SELECT"));
4622 assert!(sql.contains("FROM"));
4623 assert!(sql.contains("WHERE"));
4624 }
4625
4626 #[test]
4627 fn test_select_with_limit_offset() {
4628 let builder = PostgresQueryBuilder::new();
4629 let mut stmt = Query::select();
4630 stmt.column("id").from("users").limit(10).offset(20);
4631
4632 let (sql, values) = builder.build_select(&stmt);
4633 assert!(sql.contains("SELECT"));
4634 assert!(sql.contains("FROM"));
4635 assert!(sql.contains("LIMIT"));
4636 assert!(sql.contains("OFFSET"));
4637 assert_eq!(values.len(), 2);
4638 }
4639
4640 #[test]
4641 fn test_insert_basic() {
4642 let builder = PostgresQueryBuilder::new();
4643 let mut stmt = Query::insert();
4644 stmt.into_table("users")
4645 .columns(["name", "email"])
4646 .values_panic(["Alice", "alice@example.com"]);
4647
4648 let (sql, values) = builder.build_insert(&stmt);
4649 assert_eq!(
4650 sql,
4651 "INSERT INTO \"users\" (\"name\", \"email\") VALUES ($1, $2)"
4652 );
4653 assert_eq!(values.len(), 2);
4654 }
4655
4656 #[test]
4657 fn test_insert_multiple_rows() {
4658 let builder = PostgresQueryBuilder::new();
4659 let mut stmt = Query::insert();
4660 stmt.into_table("users")
4661 .columns(["name", "email"])
4662 .values_panic(["Alice", "alice@example.com"])
4663 .values_panic(["Bob", "bob@example.com"]);
4664
4665 let (sql, values) = builder.build_insert(&stmt);
4666 assert_eq!(
4667 sql,
4668 "INSERT INTO \"users\" (\"name\", \"email\") VALUES ($1, $2), ($3, $4)"
4669 );
4670 assert_eq!(values.len(), 4);
4671 }
4672
4673 #[test]
4674 fn test_insert_with_returning() {
4675 let builder = PostgresQueryBuilder::new();
4676 let mut stmt = Query::insert();
4677 stmt.into_table("users")
4678 .columns(["name"])
4679 .values_panic(["Alice"])
4680 .returning(["id", "created_at"]);
4681
4682 let (sql, values) = builder.build_insert(&stmt);
4683 assert!(sql.contains("INSERT INTO"));
4684 assert!(sql.contains("VALUES"));
4685 assert!(sql.contains("RETURNING"));
4686 assert!(sql.contains("\"id\""));
4687 assert!(sql.contains("\"created_at\""));
4688 assert_eq!(values.len(), 1);
4689 }
4690
4691 #[test]
4692 fn test_insert_with_returning_all() {
4693 let builder = PostgresQueryBuilder::new();
4694 let mut stmt = Query::insert();
4695 stmt.into_table("users")
4696 .columns(["name"])
4697 .values_panic(["Alice"])
4698 .returning_all();
4699
4700 let (sql, values) = builder.build_insert(&stmt);
4701 assert!(sql.contains("RETURNING *"));
4702 assert_eq!(values.len(), 1);
4703 }
4704
4705 #[test]
4706 fn test_insert_from_subquery() {
4707 let builder = PostgresQueryBuilder::new();
4708
4709 let select = Query::select()
4711 .column("name")
4712 .column("email")
4713 .from("temp_users")
4714 .to_owned();
4715
4716 let mut stmt = Query::insert();
4718 stmt.into_table("users")
4719 .columns(["name", "email"])
4720 .from_subquery(select);
4721
4722 let (sql, values) = builder.build_insert(&stmt);
4723 assert!(sql.contains("INSERT INTO \"users\""));
4724 assert!(sql.contains("\"name\", \"email\""));
4725 assert!(sql.contains("SELECT \"name\", \"email\" FROM \"temp_users\""));
4726 assert!(!sql.contains("VALUES"));
4727 assert_eq!(values.len(), 0);
4728 }
4729
4730 #[test]
4731 fn test_insert_from_subquery_with_where() {
4732 let builder = PostgresQueryBuilder::new();
4733
4734 let select = Query::select()
4736 .column("name")
4737 .column("email")
4738 .from("temp_users")
4739 .and_where(Expr::col("active").eq(true))
4740 .to_owned();
4741
4742 let mut stmt = Query::insert();
4744 stmt.into_table("users")
4745 .columns(["name", "email"])
4746 .from_subquery(select);
4747
4748 let (sql, values) = builder.build_insert(&stmt);
4749 assert!(sql.contains("INSERT INTO \"users\""));
4750 assert!(sql.contains("SELECT"));
4751 assert!(sql.contains("FROM \"temp_users\""));
4752 assert!(sql.contains("WHERE"));
4753 assert_eq!(values.len(), 1); }
4755
4756 #[test]
4757 fn test_update_basic() {
4758 let builder = PostgresQueryBuilder::new();
4759 let mut stmt = Query::update();
4760 stmt.table("users")
4761 .value("name", "Alice")
4762 .value("email", "alice@example.com");
4763
4764 let (sql, values) = builder.build_update(&stmt);
4765 assert_eq!(sql, "UPDATE \"users\" SET \"name\" = $1, \"email\" = $2");
4766 assert_eq!(values.len(), 2);
4767 }
4768
4769 #[test]
4770 fn test_update_with_where() {
4771 let builder = PostgresQueryBuilder::new();
4772 let mut stmt = Query::update();
4773 stmt.table("users")
4774 .value("active", false)
4775 .and_where(Expr::col("id").eq(1));
4776
4777 let (sql, values) = builder.build_update(&stmt);
4778 assert!(sql.contains("UPDATE"));
4779 assert!(sql.contains("SET"));
4780 assert!(sql.contains("WHERE"));
4781 assert_eq!(values.len(), 2); }
4783
4784 #[test]
4785 fn test_update_with_returning() {
4786 let builder = PostgresQueryBuilder::new();
4787 let mut stmt = Query::update();
4788 stmt.table("users")
4789 .value("active", false)
4790 .and_where(Expr::col("id").eq(1))
4791 .returning(["id", "updated_at"]);
4792
4793 let (sql, values) = builder.build_update(&stmt);
4794 assert!(sql.contains("UPDATE"));
4795 assert!(sql.contains("RETURNING"));
4796 assert!(sql.contains("\"id\""));
4797 assert!(sql.contains("\"updated_at\""));
4798 assert_eq!(values.len(), 2);
4799 }
4800
4801 #[test]
4802 fn test_delete_basic() {
4803 let builder = PostgresQueryBuilder::new();
4804 let mut stmt = Query::delete();
4805 stmt.from_table("users")
4806 .and_where(Expr::col("active").eq(false));
4807
4808 let (sql, values) = builder.build_delete(&stmt);
4809 assert!(sql.contains("DELETE FROM"));
4810 assert!(sql.contains("\"users\""));
4811 assert!(sql.contains("WHERE"));
4812 assert_eq!(values.len(), 1); }
4814
4815 #[test]
4816 fn test_delete_no_where() {
4817 let builder = PostgresQueryBuilder::new();
4818 let mut stmt = Query::delete();
4819 stmt.from_table("users");
4820
4821 let (sql, values) = builder.build_delete(&stmt);
4822 assert_eq!(sql, "DELETE FROM \"users\"");
4823 assert_eq!(values.len(), 0);
4824 }
4825
4826 #[test]
4827 fn test_delete_with_returning() {
4828 let builder = PostgresQueryBuilder::new();
4829 let mut stmt = Query::delete();
4830 stmt.from_table("users")
4831 .and_where(Expr::col("id").eq(1))
4832 .returning(["id", "name"]);
4833
4834 let (sql, values) = builder.build_delete(&stmt);
4835 assert!(sql.contains("DELETE FROM"));
4836 assert!(sql.contains("RETURNING"));
4837 assert!(sql.contains("\"id\""));
4838 assert!(sql.contains("\"name\""));
4839 assert_eq!(values.len(), 1);
4840 }
4841
4842 #[test]
4843 fn test_delete_with_returning_all() {
4844 let builder = PostgresQueryBuilder::new();
4845 let mut stmt = Query::delete();
4846 stmt.from_table("users")
4847 .and_where(Expr::col("id").eq(1))
4848 .returning_all();
4849
4850 let (sql, values) = builder.build_delete(&stmt);
4851 assert!(sql.contains("RETURNING *"));
4852 assert_eq!(values.len(), 1);
4853 }
4854
4855 #[test]
4858 fn test_inner_join_simple() {
4859 let builder = PostgresQueryBuilder::new();
4860 let mut stmt = Query::select();
4861 stmt.column("users.name")
4862 .column("orders.amount")
4863 .from("users")
4864 .inner_join(
4865 "orders",
4866 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4867 );
4868
4869 let (sql, _values) = builder.build_select(&stmt);
4870 assert!(sql.contains("FROM \"users\""));
4871 assert!(sql.contains("INNER JOIN \"orders\""));
4872 assert!(sql.contains("ON \"users\".\"id\" = \"orders\".\"user_id\""));
4873 }
4874
4875 #[test]
4876 fn test_left_join() {
4877 let builder = PostgresQueryBuilder::new();
4878 let mut stmt = Query::select();
4879 stmt.column("users.name")
4880 .column("profiles.bio")
4881 .from("users")
4882 .left_join(
4883 "profiles",
4884 Expr::col(("users", "id")).eq(Expr::col(("profiles", "user_id"))),
4885 );
4886
4887 let (sql, _values) = builder.build_select(&stmt);
4888 assert!(sql.contains("LEFT JOIN \"profiles\""));
4889 assert!(sql.contains("ON \"users\".\"id\" = \"profiles\".\"user_id\""));
4890 }
4891
4892 #[test]
4893 fn test_right_join() {
4894 let builder = PostgresQueryBuilder::new();
4895 let mut stmt = Query::select();
4896 stmt.column("users.name")
4897 .column("orders.amount")
4898 .from("users")
4899 .right_join(
4900 "orders",
4901 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4902 );
4903
4904 let (sql, _values) = builder.build_select(&stmt);
4905 assert!(sql.contains("RIGHT JOIN \"orders\""));
4906 assert!(sql.contains("ON \"users\".\"id\" = \"orders\".\"user_id\""));
4907 }
4908
4909 #[test]
4910 fn test_full_outer_join() {
4911 let builder = PostgresQueryBuilder::new();
4912 let mut stmt = Query::select();
4913 stmt.column("users.name")
4914 .column("orders.amount")
4915 .from("users")
4916 .full_outer_join(
4917 "orders",
4918 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4919 );
4920
4921 let (sql, _values) = builder.build_select(&stmt);
4922 assert!(sql.contains("FULL OUTER JOIN \"orders\""));
4923 assert!(sql.contains("ON \"users\".\"id\" = \"orders\".\"user_id\""));
4924 }
4925
4926 #[test]
4927 fn test_cross_join() {
4928 let builder = PostgresQueryBuilder::new();
4929 let mut stmt = Query::select();
4930 stmt.column("users.name")
4931 .column("roles.title")
4932 .from("users")
4933 .cross_join("roles");
4934
4935 let (sql, _values) = builder.build_select(&stmt);
4936 assert!(sql.contains("CROSS JOIN \"roles\""));
4937 assert!(!sql.contains("ON"));
4938 }
4939
4940 #[test]
4941 fn test_multiple_joins() {
4942 let builder = PostgresQueryBuilder::new();
4943 let mut stmt = Query::select();
4944 stmt.column("users.name")
4945 .column("orders.amount")
4946 .column("products.title")
4947 .from("users")
4948 .inner_join(
4949 "orders",
4950 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4951 )
4952 .inner_join(
4953 "products",
4954 Expr::col(("orders", "product_id")).eq(Expr::col(("products", "id"))),
4955 );
4956
4957 let (sql, _values) = builder.build_select(&stmt);
4958 assert!(sql.contains("INNER JOIN \"orders\""));
4959 assert!(sql.contains("INNER JOIN \"products\""));
4960 assert!(sql.contains("\"users\".\"id\" = \"orders\".\"user_id\""));
4961 assert!(sql.contains("\"orders\".\"product_id\" = \"products\".\"id\""));
4962 }
4963
4964 #[test]
4965 fn test_join_with_complex_condition() {
4966 let builder = PostgresQueryBuilder::new();
4967 let mut stmt = Query::select();
4968 stmt.column("users.name")
4969 .column("orders.amount")
4970 .from("users")
4971 .inner_join(
4972 "orders",
4973 Expr::col(("users", "id"))
4974 .eq(Expr::col(("orders", "user_id")))
4975 .and(Expr::col(("orders", "status")).eq("active")),
4976 );
4977
4978 let (sql, values) = builder.build_select(&stmt);
4979 assert!(sql.contains("INNER JOIN \"orders\""));
4980 assert!(sql.contains("ON"));
4981 assert!(sql.contains("\"users\".\"id\" = \"orders\".\"user_id\""));
4982 assert!(sql.contains("AND"));
4983 assert!(sql.contains("\"orders\".\"status\" = $"));
4984 assert_eq!(values.len(), 1);
4985 }
4986
4987 #[test]
4990 fn test_group_by_single_column() {
4991 let builder = PostgresQueryBuilder::new();
4992 let mut stmt = Query::select();
4993 stmt.column("category")
4994 .from("products")
4995 .group_by("category");
4996
4997 let (sql, _values) = builder.build_select(&stmt);
4998 assert!(sql.contains("GROUP BY \"category\""));
4999 }
5000
5001 #[test]
5002 fn test_group_by_multiple_columns() {
5003 let builder = PostgresQueryBuilder::new();
5004 let mut stmt = Query::select();
5005 stmt.column("category")
5006 .column("brand")
5007 .from("products")
5008 .group_by("category")
5009 .group_by("brand");
5010
5011 let (sql, _values) = builder.build_select(&stmt);
5012 assert!(sql.contains("GROUP BY \"category\", \"brand\""));
5013 }
5014
5015 #[test]
5016 fn test_group_by_with_count() {
5017 use crate::expr::SimpleExpr;
5018 use crate::types::{ColumnRef, IntoIden};
5019
5020 let builder = PostgresQueryBuilder::new();
5021 let mut stmt = Query::select();
5022 stmt.column("category")
5023 .expr(SimpleExpr::FunctionCall(
5024 "COUNT".into_iden(),
5025 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
5026 ))
5027 .from("products")
5028 .group_by("category");
5029
5030 let (sql, _values) = builder.build_select(&stmt);
5031 assert!(sql.contains("COUNT(*)"));
5032 assert!(sql.contains("GROUP BY \"category\""));
5033 }
5034
5035 #[test]
5036 fn test_having_simple() {
5037 use crate::expr::SimpleExpr;
5038 use crate::types::{BinOper, ColumnRef, IntoIden};
5039
5040 let builder = PostgresQueryBuilder::new();
5041 let mut stmt = Query::select();
5042 let count_expr = SimpleExpr::FunctionCall(
5043 "COUNT".into_iden(),
5044 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
5045 );
5046
5047 stmt.column("category")
5048 .expr(count_expr.clone())
5049 .from("products")
5050 .group_by("category")
5051 .and_having(SimpleExpr::Binary(
5052 Box::new(count_expr),
5053 BinOper::GreaterThan,
5054 Box::new(SimpleExpr::Value(5.into())),
5055 ));
5056
5057 let (sql, values) = builder.build_select(&stmt);
5058 assert!(sql.contains("GROUP BY \"category\""));
5059 assert!(sql.contains("HAVING"));
5060 assert!(sql.contains("COUNT(*)"));
5061 assert!(sql.contains(">"));
5062 assert_eq!(values.len(), 1);
5063 }
5064
5065 #[test]
5066 fn test_group_by_having_with_sum() {
5067 use crate::expr::SimpleExpr;
5068 use crate::types::{BinOper, ColumnRef, IntoIden};
5069
5070 let builder = PostgresQueryBuilder::new();
5071 let mut stmt = Query::select();
5072 let sum_expr = SimpleExpr::FunctionCall(
5073 "SUM".into_iden(),
5074 vec![SimpleExpr::Column(ColumnRef::column("amount"))],
5075 );
5076
5077 stmt.column("user_id")
5078 .expr(sum_expr.clone())
5079 .from("orders")
5080 .group_by("user_id")
5081 .and_having(SimpleExpr::Binary(
5082 Box::new(sum_expr),
5083 BinOper::GreaterThan,
5084 Box::new(SimpleExpr::Value(1000.into())),
5085 ));
5086
5087 let (sql, values) = builder.build_select(&stmt);
5088 assert!(sql.contains("SUM(\"amount\")"));
5089 assert!(sql.contains("GROUP BY \"user_id\""));
5090 assert!(sql.contains("HAVING"));
5091 assert_eq!(values.len(), 1);
5092 }
5093
5094 #[test]
5095 fn test_select_distinct() {
5096 let builder = PostgresQueryBuilder::new();
5097 let mut stmt = Query::select();
5098 stmt.column("category").from("products").distinct();
5099
5100 let (sql, _values) = builder.build_select(&stmt);
5101 assert!(sql.starts_with("SELECT DISTINCT"));
5102 assert!(sql.contains("\"category\""));
5103 assert!(sql.contains("FROM \"products\""));
5104 }
5105
5106 #[test]
5107 fn test_select_distinct_on() {
5108 let builder = PostgresQueryBuilder::new();
5109 let mut stmt = Query::select();
5110 stmt.column("id")
5111 .column("name")
5112 .from("users")
5113 .distinct_on(vec!["category"])
5114 .order_by("category", crate::types::Order::Asc);
5115
5116 let (sql, _values) = builder.build_select(&stmt);
5117 assert!(sql.contains("SELECT DISTINCT ON (\"category\")"));
5118 assert!(sql.contains("\"id\""));
5119 assert!(sql.contains("\"name\""));
5120 assert!(sql.contains("ORDER BY \"category\" ASC"));
5121 }
5122
5123 #[test]
5124 #[should_panic(expected = "PostgreSQL does not support DISTINCT ROW")]
5125 fn test_select_distinct_row_panics() {
5126 use crate::query::SelectDistinct;
5127
5128 let builder = PostgresQueryBuilder::new();
5129 let mut stmt = Query::select();
5130 stmt.column("name").from("products");
5131 stmt.distinct = Some(SelectDistinct::DistinctRow);
5132
5133 let _ = builder.build_select(&stmt);
5134 }
5135
5136 #[test]
5137 fn test_select_union() {
5138 let builder = PostgresQueryBuilder::new();
5139 let mut stmt1 = Query::select();
5140 stmt1.column("id").from("users");
5141
5142 let mut stmt2 = Query::select();
5143 stmt2.column("id").from("customers");
5144
5145 stmt1.union(stmt2);
5146
5147 let (sql, _values) = builder.build_select(&stmt1);
5148 assert!(sql.contains("SELECT \"id\" FROM \"users\""));
5149 assert!(sql.contains("UNION SELECT \"id\" FROM \"customers\""));
5150 }
5151
5152 #[test]
5153 fn test_select_union_all() {
5154 let builder = PostgresQueryBuilder::new();
5155 let mut stmt1 = Query::select();
5156 stmt1.column("name").from("products");
5157
5158 let mut stmt2 = Query::select();
5159 stmt2.column("name").from("archived_products");
5160
5161 stmt1.union_all(stmt2);
5162
5163 let (sql, _values) = builder.build_select(&stmt1);
5164 assert!(sql.contains("SELECT \"name\" FROM \"products\""));
5165 assert!(sql.contains("UNION ALL SELECT \"name\" FROM \"archived_products\""));
5166 }
5167
5168 #[test]
5169 fn test_select_intersect() {
5170 let builder = PostgresQueryBuilder::new();
5171 let mut stmt1 = Query::select();
5172 stmt1.column("email").from("subscribers");
5173
5174 let mut stmt2 = Query::select();
5175 stmt2.column("email").from("customers");
5176
5177 stmt1.intersect(stmt2);
5178
5179 let (sql, _values) = builder.build_select(&stmt1);
5180 assert!(sql.contains("SELECT \"email\" FROM \"subscribers\""));
5181 assert!(sql.contains("INTERSECT SELECT \"email\" FROM \"customers\""));
5182 }
5183
5184 #[test]
5185 fn test_select_except() {
5186 let builder = PostgresQueryBuilder::new();
5187 let mut stmt1 = Query::select();
5188 stmt1.column("id").from("all_users");
5189
5190 let mut stmt2 = Query::select();
5191 stmt2.column("id").from("banned_users");
5192
5193 stmt1.except(stmt2);
5194
5195 let (sql, _values) = builder.build_select(&stmt1);
5196 assert!(sql.contains("SELECT \"id\" FROM \"all_users\""));
5197 assert!(sql.contains("EXCEPT SELECT \"id\" FROM \"banned_users\""));
5198 }
5199
5200 #[test]
5201 fn test_select_multiple_unions() {
5202 let builder = PostgresQueryBuilder::new();
5203 let mut stmt1 = Query::select();
5204 stmt1.column("id").from("table1");
5205
5206 let mut stmt2 = Query::select();
5207 stmt2.column("id").from("table2");
5208
5209 let mut stmt3 = Query::select();
5210 stmt3.column("id").from("table3");
5211
5212 stmt1.union(stmt2);
5213 stmt1.union_all(stmt3);
5214
5215 let (sql, _values) = builder.build_select(&stmt1);
5216 assert!(sql.contains("SELECT \"id\" FROM \"table1\""));
5217 assert!(sql.contains("UNION SELECT \"id\" FROM \"table2\""));
5218 assert!(sql.contains("UNION ALL SELECT \"id\" FROM \"table3\""));
5219 }
5220
5221 #[test]
5222 fn test_select_exists_subquery() {
5223 use crate::expr::Expr;
5224
5225 let builder = PostgresQueryBuilder::new();
5226 let mut stmt = Query::select();
5227
5228 stmt.column("name").from("users");
5230
5231 let mut subquery = Query::select();
5233 subquery
5234 .column("id")
5235 .from("orders")
5236 .and_where(Expr::col(("orders", "user_id")).eq(Expr::col(("users", "id"))));
5237
5238 stmt.and_where(Expr::exists(subquery));
5240
5241 let (sql, _values) = builder.build_select(&stmt);
5242 assert!(sql.contains("SELECT \"name\" FROM \"users\""));
5243 assert!(sql.contains("WHERE"));
5244 assert!(sql.contains("EXISTS"));
5245 assert!(sql.contains("SELECT \"id\" FROM \"orders\""));
5246 }
5247
5248 #[test]
5249 fn test_select_in_subquery() {
5250 use crate::expr::Expr;
5251
5252 let builder = PostgresQueryBuilder::new();
5253 let mut stmt = Query::select();
5254
5255 stmt.column("name").from("users");
5257
5258 let mut subquery = Query::select();
5260 subquery.column("user_id").from("premium_users");
5261
5262 stmt.and_where(Expr::col("id").in_subquery(subquery));
5264
5265 let (sql, _values) = builder.build_select(&stmt);
5266 assert!(sql.contains("SELECT \"name\" FROM \"users\""));
5267 assert!(sql.contains("WHERE"));
5268 assert!(sql.contains("\"id\""));
5269 assert!(sql.contains("IN"));
5270 assert!(sql.contains("SELECT \"user_id\" FROM \"premium_users\""));
5271 }
5272
5273 #[test]
5274 fn test_select_not_exists_subquery() {
5275 use crate::expr::Expr;
5276
5277 let builder = PostgresQueryBuilder::new();
5278 let mut stmt = Query::select();
5279
5280 stmt.column("email").from("users");
5282
5283 let mut subquery = Query::select();
5285 subquery
5286 .column("id")
5287 .from("banned_users")
5288 .and_where(Expr::col(("banned_users", "user_id")).eq(Expr::col(("users", "id"))));
5289
5290 stmt.and_where(Expr::not_exists(subquery));
5292
5293 let (sql, _values) = builder.build_select(&stmt);
5294 assert!(sql.contains("SELECT \"email\" FROM \"users\""));
5295 assert!(sql.contains("WHERE"));
5296 assert!(sql.contains("NOT EXISTS"));
5297 assert!(sql.contains("SELECT \"id\" FROM \"banned_users\""));
5298 }
5299
5300 #[test]
5303 fn test_not_in_subquery() {
5304 let builder = PostgresQueryBuilder::new();
5305
5306 let mut subquery = Query::select();
5307 subquery
5308 .column("user_id")
5309 .from("blocked_users")
5310 .and_where(Expr::col("reason").eq("spam"));
5311
5312 let mut stmt = Query::select();
5313 stmt.column("name")
5314 .from("users")
5315 .and_where(Expr::col("id").not_in_subquery(subquery));
5316
5317 let (sql, values) = builder.build_select(&stmt);
5318 assert!(sql.contains("NOT IN"));
5319 assert!(sql.contains("SELECT \"user_id\" FROM \"blocked_users\""));
5320 assert!(sql.contains("\"reason\" = $"));
5321 assert_eq!(values.len(), 1);
5322 }
5323
5324 #[test]
5325 fn test_subquery_in_select_list() {
5326 let builder = PostgresQueryBuilder::new();
5327
5328 let mut subquery = Query::select();
5329 subquery
5330 .expr(Expr::col("count"))
5331 .from("order_counts")
5332 .and_where(Expr::col(("order_counts", "user_id")).eq(Expr::col(("users", "id"))));
5333
5334 let mut stmt = Query::select();
5335 stmt.column("name")
5336 .expr(Expr::subquery(subquery))
5337 .from("users");
5338
5339 let (sql, _values) = builder.build_select(&stmt);
5340 assert!(sql.contains("\"name\""));
5341 assert!(sql.contains("(SELECT \"count\" FROM \"order_counts\""));
5342 assert!(sql.contains("\"order_counts\".\"user_id\" = \"users\".\"id\""));
5343 }
5344
5345 #[test]
5346 fn test_multiple_exists_conditions() {
5347 let builder = PostgresQueryBuilder::new();
5348
5349 let mut sub1 = Query::select();
5350 sub1.column("id")
5351 .from("orders")
5352 .and_where(Expr::col(("orders", "user_id")).eq(Expr::col(("users", "id"))));
5353
5354 let mut sub2 = Query::select();
5355 sub2.column("id")
5356 .from("reviews")
5357 .and_where(Expr::col(("reviews", "user_id")).eq(Expr::col(("users", "id"))));
5358
5359 let mut stmt = Query::select();
5360 stmt.column("name")
5361 .from("users")
5362 .and_where(Expr::exists(sub1))
5363 .and_where(Expr::exists(sub2));
5364
5365 let (sql, _values) = builder.build_select(&stmt);
5366 assert!(sql.contains("EXISTS (SELECT \"id\" FROM \"orders\""));
5367 assert!(sql.contains("EXISTS (SELECT \"id\" FROM \"reviews\""));
5368 }
5369
5370 #[test]
5371 fn test_nested_subquery() {
5372 let builder = PostgresQueryBuilder::new();
5373
5374 let mut inner_subquery = Query::select();
5375 inner_subquery
5376 .column("department_id")
5377 .from("top_departments")
5378 .and_where(Expr::col("revenue").gt(1000000));
5379
5380 let mut outer_subquery = Query::select();
5381 outer_subquery
5382 .column("id")
5383 .from("employees")
5384 .and_where(Expr::col("department_id").in_subquery(inner_subquery));
5385
5386 let mut stmt = Query::select();
5387 stmt.column("name")
5388 .from("users")
5389 .and_where(Expr::col("employee_id").in_subquery(outer_subquery));
5390
5391 let (sql, values) = builder.build_select(&stmt);
5392 assert!(sql.contains("IN (SELECT \"id\" FROM \"employees\""));
5393 assert!(sql.contains("IN (SELECT \"department_id\" FROM \"top_departments\""));
5394 assert!(sql.contains("\"revenue\" > $"));
5395 assert_eq!(values.len(), 1);
5396 }
5397
5398 #[test]
5399 fn test_subquery_with_complex_where() {
5400 let builder = PostgresQueryBuilder::new();
5401
5402 let mut subquery = Query::select();
5403 subquery
5404 .column("product_id")
5405 .from("inventory")
5406 .and_where(Expr::col("quantity").gt(0))
5407 .and_where(Expr::col("warehouse").eq("main"))
5408 .and_where(Expr::col("status").eq("available"));
5409
5410 let mut stmt = Query::select();
5411 stmt.column("name")
5412 .column("price")
5413 .from("products")
5414 .and_where(Expr::col("id").in_subquery(subquery))
5415 .and_where(Expr::col("active").eq(true));
5416
5417 let (sql, values) = builder.build_select(&stmt);
5418 assert!(sql.contains("IN (SELECT \"product_id\" FROM \"inventory\""));
5419 assert!(sql.contains("\"quantity\" > $"));
5420 assert!(sql.contains("\"warehouse\" = $"));
5421 assert!(sql.contains("\"status\" = $"));
5422 assert!(sql.contains("\"active\" = $"));
5423 assert_eq!(values.len(), 4); }
5425
5426 #[test]
5427 fn test_from_subquery_preserves_parameter_values() {
5428 let builder = PostgresQueryBuilder::new();
5429
5430 let mut subquery = Query::select();
5432 subquery
5433 .column("id")
5434 .column("name")
5435 .from("users")
5436 .and_where(Expr::col("active").eq(true))
5437 .and_where(Expr::col("role").eq("admin"));
5438
5439 let mut stmt = Query::select();
5440 stmt.column("name")
5441 .from_subquery(subquery, Alias::new("active_admins"))
5442 .and_where(Expr::col("name").like("A%"));
5443
5444 let (sql, values) = builder.build_select(&stmt);
5446
5447 assert!(sql.contains("(SELECT"));
5449 assert!(sql.contains(") AS \"active_admins\""));
5450 assert_eq!(values.len(), 3);
5452 }
5453
5454 #[test]
5455 fn test_from_subquery_postgres_placeholder_renumbering() {
5456 let builder = PostgresQueryBuilder::new();
5457
5458 let mut subquery = Query::select();
5460 subquery
5461 .column("id")
5462 .from("users")
5463 .and_where(Expr::col("role").eq("admin"));
5464
5465 let mut stmt = Query::select();
5466 stmt.column("name")
5467 .from_subquery(subquery, Alias::new("sub"))
5468 .and_where(Expr::col("status").eq("active"));
5469
5470 let (sql, values) = builder.build_select(&stmt);
5472
5473 assert!(sql.contains("$1"));
5475 assert!(sql.contains("$2"));
5476 assert_eq!(values.len(), 2);
5477 }
5478
5479 #[test]
5482 fn test_where_is_null() {
5483 let builder = PostgresQueryBuilder::new();
5484 let mut stmt = Query::select();
5485 stmt.column("name")
5486 .from("users")
5487 .and_where(Expr::col("deleted_at").is_null());
5488
5489 let (sql, values) = builder.build_select(&stmt);
5490 assert!(sql.contains("\"deleted_at\" IS"));
5491 assert!(sql.to_uppercase().contains("NULL"));
5492 assert_eq!(values.len(), 0);
5493 }
5494
5495 #[test]
5496 fn test_where_is_not_null() {
5497 let builder = PostgresQueryBuilder::new();
5498 let mut stmt = Query::select();
5499 stmt.column("name")
5500 .from("users")
5501 .and_where(Expr::col("email").is_not_null());
5502
5503 let (sql, values) = builder.build_select(&stmt);
5504 assert!(sql.contains("\"email\" IS NOT"));
5505 assert!(sql.to_uppercase().contains("NULL"));
5506 assert_eq!(values.len(), 0);
5507 }
5508
5509 #[test]
5510 fn test_is_null_combined_with_other_conditions() {
5511 let builder = PostgresQueryBuilder::new();
5512 let mut stmt = Query::select();
5513 stmt.column("name")
5514 .from("users")
5515 .and_where(Expr::col("active").eq(true))
5516 .and_where(Expr::col("deleted_at").is_null())
5517 .and_where(Expr::col("email").is_not_null());
5518
5519 let (sql, values) = builder.build_select(&stmt);
5520 assert!(sql.contains("\"active\" = $"));
5521 assert!(sql.contains("\"deleted_at\" IS"));
5522 assert!(sql.contains("\"email\" IS NOT"));
5523 assert_eq!(values.len(), 1);
5524 }
5525
5526 #[test]
5527 fn test_is_null_with_join() {
5528 let builder = PostgresQueryBuilder::new();
5529 let mut stmt = Query::select();
5530 stmt.column(("users", "name"))
5531 .from("users")
5532 .left_join(
5533 "profiles",
5534 Expr::col(("users", "id")).eq(Expr::col(("profiles", "user_id"))),
5535 )
5536 .and_where(Expr::col(("profiles", "id")).is_null());
5537
5538 let (sql, values) = builder.build_select(&stmt);
5539 assert!(sql.contains("LEFT JOIN \"profiles\""));
5540 assert!(sql.contains("\"profiles\".\"id\" IS"));
5541 assert_eq!(values.len(), 0);
5542 }
5543
5544 #[test]
5546 fn test_where_or_condition() {
5547 use crate::expr::Condition;
5548
5549 let builder = PostgresQueryBuilder::new();
5550 let mut stmt = Query::select();
5551 stmt.column("name").from("users").cond_where(
5552 Condition::any()
5553 .add(Expr::col("status").eq("active"))
5554 .add(Expr::col("status").eq("pending")),
5555 );
5556
5557 let (sql, values) = builder.build_select(&stmt);
5558 assert!(sql.contains("\"status\" = $"));
5559 assert!(sql.contains(" OR "));
5560 assert_eq!(values.len(), 2);
5561 }
5562
5563 #[test]
5564 fn test_where_between() {
5565 let builder = PostgresQueryBuilder::new();
5566 let mut stmt = Query::select();
5567 stmt.column("name")
5568 .from("products")
5569 .and_where(Expr::col("price").between(100, 500));
5570
5571 let (sql, values) = builder.build_select(&stmt);
5572 assert!(sql.contains("\"price\" BETWEEN $"));
5573 assert!(sql.contains("AND $"));
5574 assert_eq!(values.len(), 2);
5575 }
5576
5577 #[test]
5578 fn test_where_not_between() {
5579 let builder = PostgresQueryBuilder::new();
5580 let mut stmt = Query::select();
5581 stmt.column("name")
5582 .from("products")
5583 .and_where(Expr::col("price").not_between(0, 10));
5584
5585 let (sql, values) = builder.build_select(&stmt);
5586 assert!(sql.contains("\"price\" NOT BETWEEN $"));
5587 assert!(sql.contains("AND $"));
5588 assert_eq!(values.len(), 2);
5589 }
5590
5591 #[test]
5592 fn test_where_like() {
5593 let builder = PostgresQueryBuilder::new();
5594 let mut stmt = Query::select();
5595 stmt.column("name")
5596 .from("users")
5597 .and_where(Expr::col("email").like("%@gmail.com"));
5598
5599 let (sql, values) = builder.build_select(&stmt);
5600 assert!(sql.contains("\"email\" LIKE $"));
5601 assert_eq!(values.len(), 1);
5602 }
5603
5604 #[test]
5605 fn test_where_in_values() {
5606 let builder = PostgresQueryBuilder::new();
5607 let mut stmt = Query::select();
5608 stmt.column("name")
5609 .from("users")
5610 .and_where(Expr::col("role").is_in(vec!["admin", "moderator", "editor"]));
5611
5612 let (sql, values) = builder.build_select(&stmt);
5613 assert!(sql.contains("\"role\" IN"));
5614 assert_eq!(values.len(), 3);
5615 }
5616
5617 #[test]
5618 fn test_insert_with_null_value() {
5619 use crate::value::Value;
5620
5621 let builder = PostgresQueryBuilder::new();
5622 let mut stmt = Query::insert();
5623 stmt.into_table("users")
5624 .columns(vec!["name", "email", "phone"])
5625 .values(vec![
5626 Value::String(Some(Box::new("John".to_string()))),
5627 Value::String(Some(Box::new("john@example.com".to_string()))),
5628 Value::String(None),
5629 ])
5630 .unwrap();
5631
5632 let (sql, values) = builder.build_insert(&stmt);
5633 assert!(sql.contains("INSERT INTO \"users\""));
5634 assert!(sql.contains("\"name\""));
5635 assert!(sql.contains("\"email\""));
5636 assert!(sql.contains("\"phone\""));
5637 assert!(sql.contains("NULL"));
5639 assert_eq!(values.len(), 2);
5640 }
5641
5642 #[test]
5643 fn test_select_with_single_cte() {
5644 let builder = PostgresQueryBuilder::new();
5645
5646 let mut cte_query = Query::select();
5648 cte_query
5649 .column("id")
5650 .column("name")
5651 .from("employees")
5652 .and_where(Expr::col("department").eq("Engineering"));
5653
5654 let mut stmt = Query::select();
5656 stmt.with_cte("eng_employees", cte_query)
5657 .column("name")
5658 .from("eng_employees");
5659
5660 let (sql, _values) = builder.build_select(&stmt);
5661 assert!(sql.contains("WITH"));
5662 assert!(sql.contains("\"eng_employees\""));
5663 assert!(sql.contains("AS"));
5664 assert!(sql.contains("SELECT \"id\", \"name\" FROM \"employees\""));
5665 assert!(sql.contains("SELECT \"name\" FROM \"eng_employees\""));
5666 }
5667
5668 #[test]
5669 fn test_select_with_multiple_ctes() {
5670 let builder = PostgresQueryBuilder::new();
5671
5672 let mut cte1 = Query::select();
5674 cte1.column("id")
5675 .column("name")
5676 .from("employees")
5677 .and_where(Expr::col("department").eq("Engineering"));
5678
5679 let mut cte2 = Query::select();
5681 cte2.column("id")
5682 .column("name")
5683 .from("employees")
5684 .and_where(Expr::col("department").eq("Sales"));
5685
5686 let mut stmt = Query::select();
5688 stmt.with_cte("eng_emp", cte1)
5689 .with_cte("sales_emp", cte2)
5690 .column("name")
5691 .from("eng_emp");
5692
5693 let (sql, _values) = builder.build_select(&stmt);
5694 assert!(sql.contains("WITH"));
5695 assert!(sql.contains("\"eng_emp\""));
5696 assert!(sql.contains("\"sales_emp\""));
5697 assert!(sql.contains("AS"));
5698 assert!(sql.contains("\"eng_emp\" AS"));
5700 assert!(sql.contains("\"sales_emp\" AS"));
5701 }
5702
5703 #[test]
5704 fn test_select_with_recursive_cte() {
5705 let builder = PostgresQueryBuilder::new();
5706
5707 let mut cte_query = Query::select();
5709 cte_query
5710 .column("id")
5711 .column("name")
5712 .column("manager_id")
5713 .from("employees");
5714
5715 let mut stmt = Query::select();
5717 stmt.with_recursive_cte("employee_hierarchy", cte_query)
5718 .column("name")
5719 .from("employee_hierarchy");
5720
5721 let (sql, _values) = builder.build_select(&stmt);
5722 assert!(sql.contains("WITH RECURSIVE"));
5723 assert!(sql.contains("\"employee_hierarchy\""));
5724 assert!(sql.contains("AS"));
5725 assert!(sql.contains("SELECT \"id\", \"name\", \"manager_id\" FROM \"employees\""));
5726 assert!(sql.contains("SELECT \"name\" FROM \"employee_hierarchy\""));
5727 }
5728
5729 #[test]
5732 fn test_window_row_number_with_partition_and_order() {
5733 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5734
5735 let builder = PostgresQueryBuilder::new();
5736 let mut stmt = Query::select();
5737
5738 let window = WindowStatement {
5739 partition_by: vec![Expr::col("department").into_simple_expr()],
5740 order_by: vec![OrderExpr {
5741 expr: OrderExprKind::Column("salary".into_iden()),
5742 order: Order::Desc,
5743 nulls: None,
5744 }],
5745 frame: None,
5746 };
5747
5748 stmt.expr(Expr::row_number().over(window))
5749 .column("name")
5750 .from("employees");
5751
5752 let (sql, _values) = builder.build_select(&stmt);
5753 assert_eq!(
5754 sql,
5755 r#"SELECT ROW_NUMBER() OVER ( PARTITION BY "department" ORDER BY "salary" DESC ), "name" FROM "employees""#
5756 );
5757 }
5758
5759 #[test]
5760 fn test_window_row_number_order_only() {
5761 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5762
5763 let builder = PostgresQueryBuilder::new();
5764 let mut stmt = Query::select();
5765
5766 let window = WindowStatement {
5767 partition_by: vec![],
5768 order_by: vec![OrderExpr {
5769 expr: OrderExprKind::Column("id".into_iden()),
5770 order: Order::Asc,
5771 nulls: None,
5772 }],
5773 frame: None,
5774 };
5775
5776 stmt.expr(Expr::row_number().over(window)).from("users");
5777
5778 let (sql, _values) = builder.build_select(&stmt);
5779 assert_eq!(
5780 sql,
5781 r#"SELECT ROW_NUMBER() OVER ( ORDER BY "id" ASC ) FROM "users""#
5782 );
5783 }
5784
5785 #[test]
5786 fn test_window_rank_basic() {
5787 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5788
5789 let builder = PostgresQueryBuilder::new();
5790 let mut stmt = Query::select();
5791
5792 let window = WindowStatement {
5793 partition_by: vec![],
5794 order_by: vec![OrderExpr {
5795 expr: OrderExprKind::Column("score".into_iden()),
5796 order: Order::Desc,
5797 nulls: None,
5798 }],
5799 frame: None,
5800 };
5801
5802 stmt.expr(Expr::rank().over(window))
5803 .column("name")
5804 .from("students");
5805
5806 let (sql, _values) = builder.build_select(&stmt);
5807 assert_eq!(
5808 sql,
5809 r#"SELECT RANK() OVER ( ORDER BY "score" DESC ), "name" FROM "students""#
5810 );
5811 }
5812
5813 #[test]
5814 fn test_window_rank_with_partition() {
5815 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5816
5817 let builder = PostgresQueryBuilder::new();
5818 let mut stmt = Query::select();
5819
5820 let window = WindowStatement {
5821 partition_by: vec![Expr::col("class").into_simple_expr()],
5822 order_by: vec![OrderExpr {
5823 expr: OrderExprKind::Column("score".into_iden()),
5824 order: Order::Desc,
5825 nulls: None,
5826 }],
5827 frame: None,
5828 };
5829
5830 stmt.expr(Expr::rank().over(window))
5831 .column("name")
5832 .from("students");
5833
5834 let (sql, _values) = builder.build_select(&stmt);
5835 assert_eq!(
5836 sql,
5837 r#"SELECT RANK() OVER ( PARTITION BY "class" ORDER BY "score" DESC ), "name" FROM "students""#
5838 );
5839 }
5840
5841 #[test]
5842 fn test_window_dense_rank_basic() {
5843 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5844
5845 let builder = PostgresQueryBuilder::new();
5846 let mut stmt = Query::select();
5847
5848 let window = WindowStatement {
5849 partition_by: vec![],
5850 order_by: vec![OrderExpr {
5851 expr: OrderExprKind::Column("points".into_iden()),
5852 order: Order::Desc,
5853 nulls: None,
5854 }],
5855 frame: None,
5856 };
5857
5858 stmt.expr(Expr::dense_rank().over(window))
5859 .column("player")
5860 .from("scores");
5861
5862 let (sql, _values) = builder.build_select(&stmt);
5863 assert_eq!(
5864 sql,
5865 r#"SELECT DENSE_RANK() OVER ( ORDER BY "points" DESC ), "player" FROM "scores""#
5866 );
5867 }
5868
5869 #[test]
5870 fn test_window_dense_rank_with_partition() {
5871 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5872
5873 let builder = PostgresQueryBuilder::new();
5874 let mut stmt = Query::select();
5875
5876 let window = WindowStatement {
5877 partition_by: vec![Expr::col("league").into_simple_expr()],
5878 order_by: vec![OrderExpr {
5879 expr: OrderExprKind::Column("points".into_iden()),
5880 order: Order::Desc,
5881 nulls: None,
5882 }],
5883 frame: None,
5884 };
5885
5886 stmt.expr(Expr::dense_rank().over(window))
5887 .column("player")
5888 .from("scores");
5889
5890 let (sql, _values) = builder.build_select(&stmt);
5891 assert_eq!(
5892 sql,
5893 r#"SELECT DENSE_RANK() OVER ( PARTITION BY "league" ORDER BY "points" DESC ), "player" FROM "scores""#
5894 );
5895 }
5896
5897 #[test]
5898 fn test_window_ntile_four_buckets() {
5899 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5900
5901 let builder = PostgresQueryBuilder::new();
5902 let mut stmt = Query::select();
5903
5904 let window = WindowStatement {
5905 partition_by: vec![],
5906 order_by: vec![OrderExpr {
5907 expr: OrderExprKind::Column("salary".into_iden()),
5908 order: Order::Asc,
5909 nulls: None,
5910 }],
5911 frame: None,
5912 };
5913
5914 stmt.expr(Expr::ntile(4).over(window))
5915 .column("name")
5916 .from("employees");
5917
5918 let (sql, _values) = builder.build_select(&stmt);
5919 assert_eq!(
5920 sql,
5921 r#"SELECT NTILE($1) OVER ( ORDER BY "salary" ASC ), "name" FROM "employees""#
5922 );
5923 }
5924
5925 #[test]
5926 fn test_window_ntile_custom_buckets() {
5927 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5928
5929 let builder = PostgresQueryBuilder::new();
5930 let mut stmt = Query::select();
5931
5932 let window = WindowStatement {
5933 partition_by: vec![Expr::col("department").into_simple_expr()],
5934 order_by: vec![OrderExpr {
5935 expr: OrderExprKind::Column("salary".into_iden()),
5936 order: Order::Desc,
5937 nulls: None,
5938 }],
5939 frame: None,
5940 };
5941
5942 stmt.expr(Expr::ntile(3).over(window))
5943 .column("name")
5944 .from("employees");
5945
5946 let (sql, _values) = builder.build_select(&stmt);
5947 assert_eq!(
5948 sql,
5949 r#"SELECT NTILE($1) OVER ( PARTITION BY "department" ORDER BY "salary" DESC ), "name" FROM "employees""#
5950 );
5951 }
5952
5953 #[test]
5954 fn test_window_lead_basic() {
5955 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5956
5957 let builder = PostgresQueryBuilder::new();
5958 let mut stmt = Query::select();
5959
5960 let window = WindowStatement {
5961 partition_by: vec![],
5962 order_by: vec![OrderExpr {
5963 expr: OrderExprKind::Column("date".into_iden()),
5964 order: Order::Asc,
5965 nulls: None,
5966 }],
5967 frame: None,
5968 };
5969
5970 stmt.expr(Expr::lead(Expr::col("price").into_simple_expr(), None, None).over(window))
5971 .column("date")
5972 .from("stocks");
5973
5974 let (sql, values) = builder.build_select(&stmt);
5975 assert_eq!(
5976 sql,
5977 r#"SELECT LEAD("price") OVER ( ORDER BY "date" ASC ), "date" FROM "stocks""#
5978 );
5979 assert_eq!(values.len(), 0);
5980 }
5981
5982 #[test]
5983 fn test_window_lead_with_offset_and_default() {
5984 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5985
5986 let builder = PostgresQueryBuilder::new();
5987 let mut stmt = Query::select();
5988
5989 let window = WindowStatement {
5990 partition_by: vec![Expr::col("ticker").into_simple_expr()],
5991 order_by: vec![OrderExpr {
5992 expr: OrderExprKind::Column("date".into_iden()),
5993 order: Order::Asc,
5994 nulls: None,
5995 }],
5996 frame: None,
5997 };
5998
5999 stmt.expr(
6000 Expr::lead(
6001 Expr::col("price").into_simple_expr(),
6002 Some(2),
6003 Some(0.0.into()),
6004 )
6005 .over(window),
6006 )
6007 .column("date")
6008 .from("stocks");
6009
6010 let (sql, values) = builder.build_select(&stmt);
6011 assert!(sql.contains("LEAD"));
6012 assert!(sql.contains("OVER"));
6013 assert!(sql.contains(r#"PARTITION BY "ticker""#));
6014 assert!(sql.contains(r#"ORDER BY "date" ASC"#));
6015 assert_eq!(values.len(), 2);
6016 }
6017
6018 #[test]
6019 fn test_window_lag_basic() {
6020 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6021
6022 let builder = PostgresQueryBuilder::new();
6023 let mut stmt = Query::select();
6024
6025 let window = WindowStatement {
6026 partition_by: vec![],
6027 order_by: vec![OrderExpr {
6028 expr: OrderExprKind::Column("month".into_iden()),
6029 order: Order::Asc,
6030 nulls: None,
6031 }],
6032 frame: None,
6033 };
6034
6035 stmt.expr(Expr::lag(Expr::col("revenue").into_simple_expr(), None, None).over(window))
6036 .column("month")
6037 .from("sales");
6038
6039 let (sql, values) = builder.build_select(&stmt);
6040 assert_eq!(
6041 sql,
6042 r#"SELECT LAG("revenue") OVER ( ORDER BY "month" ASC ), "month" FROM "sales""#
6043 );
6044 assert_eq!(values.len(), 0);
6045 }
6046
6047 #[test]
6048 fn test_window_lag_with_offset_and_default() {
6049 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6050
6051 let builder = PostgresQueryBuilder::new();
6052 let mut stmt = Query::select();
6053
6054 let window = WindowStatement {
6055 partition_by: vec![Expr::col("product").into_simple_expr()],
6056 order_by: vec![OrderExpr {
6057 expr: OrderExprKind::Column("month".into_iden()),
6058 order: Order::Asc,
6059 nulls: None,
6060 }],
6061 frame: None,
6062 };
6063
6064 stmt.expr(
6065 Expr::lag(
6066 Expr::col("revenue").into_simple_expr(),
6067 Some(3),
6068 Some(0.0.into()),
6069 )
6070 .over(window),
6071 )
6072 .column("month")
6073 .from("sales");
6074
6075 let (sql, values) = builder.build_select(&stmt);
6076 assert!(sql.contains("LAG"));
6077 assert!(sql.contains("OVER"));
6078 assert!(sql.contains(r#"PARTITION BY "product""#));
6079 assert!(sql.contains(r#"ORDER BY "month" ASC"#));
6080 assert_eq!(values.len(), 2);
6081 }
6082
6083 #[test]
6084 fn test_window_first_value() {
6085 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6086
6087 let builder = PostgresQueryBuilder::new();
6088 let mut stmt = Query::select();
6089
6090 let window = WindowStatement {
6091 partition_by: vec![Expr::col("category").into_simple_expr()],
6092 order_by: vec![OrderExpr {
6093 expr: OrderExprKind::Column("price".into_iden()),
6094 order: Order::Asc,
6095 nulls: None,
6096 }],
6097 frame: None,
6098 };
6099
6100 stmt.expr(Expr::first_value(Expr::col("name").into_simple_expr()).over(window))
6101 .column("name")
6102 .from("products");
6103
6104 let (sql, _values) = builder.build_select(&stmt);
6105 assert_eq!(
6106 sql,
6107 r#"SELECT FIRST_VALUE("name") OVER ( PARTITION BY "category" ORDER BY "price" ASC ), "name" FROM "products""#
6108 );
6109 }
6110
6111 #[test]
6112 fn test_window_last_value() {
6113 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6114
6115 let builder = PostgresQueryBuilder::new();
6116 let mut stmt = Query::select();
6117
6118 let window = WindowStatement {
6119 partition_by: vec![Expr::col("category").into_simple_expr()],
6120 order_by: vec![OrderExpr {
6121 expr: OrderExprKind::Column("price".into_iden()),
6122 order: Order::Desc,
6123 nulls: None,
6124 }],
6125 frame: None,
6126 };
6127
6128 stmt.expr(Expr::last_value(Expr::col("name").into_simple_expr()).over(window))
6129 .column("name")
6130 .from("products");
6131
6132 let (sql, _values) = builder.build_select(&stmt);
6133 assert_eq!(
6134 sql,
6135 r#"SELECT LAST_VALUE("name") OVER ( PARTITION BY "category" ORDER BY "price" DESC ), "name" FROM "products""#
6136 );
6137 }
6138
6139 #[test]
6140 fn test_window_nth_value() {
6141 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6142
6143 let builder = PostgresQueryBuilder::new();
6144 let mut stmt = Query::select();
6145
6146 let window = WindowStatement {
6147 partition_by: vec![Expr::col("department").into_simple_expr()],
6148 order_by: vec![OrderExpr {
6149 expr: OrderExprKind::Column("salary".into_iden()),
6150 order: Order::Desc,
6151 nulls: None,
6152 }],
6153 frame: None,
6154 };
6155
6156 stmt.expr(Expr::nth_value(Expr::col("name").into_simple_expr(), 2).over(window))
6157 .column("name")
6158 .from("employees");
6159
6160 let (sql, values) = builder.build_select(&stmt);
6161 assert!(sql.contains("NTH_VALUE"));
6162 assert!(sql.contains(r#"PARTITION BY "department""#));
6163 assert!(sql.contains(r#"ORDER BY "salary" DESC"#));
6164 assert_eq!(values.len(), 1); }
6166
6167 #[test]
6168 fn test_window_row_number_multiple_partition_columns() {
6169 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6170
6171 let builder = PostgresQueryBuilder::new();
6172 let mut stmt = Query::select();
6173
6174 let window = WindowStatement {
6175 partition_by: vec![
6176 Expr::col("country").into_simple_expr(),
6177 Expr::col("city").into_simple_expr(),
6178 ],
6179 order_by: vec![OrderExpr {
6180 expr: OrderExprKind::Column("population".into_iden()),
6181 order: Order::Desc,
6182 nulls: None,
6183 }],
6184 frame: None,
6185 };
6186
6187 stmt.expr(Expr::row_number().over(window))
6188 .column("name")
6189 .from("cities");
6190
6191 let (sql, _values) = builder.build_select(&stmt);
6192 assert_eq!(
6193 sql,
6194 r#"SELECT ROW_NUMBER() OVER ( PARTITION BY "country", "city" ORDER BY "population" DESC ), "name" FROM "cities""#
6195 );
6196 }
6197
6198 #[test]
6199 fn test_window_ntile_with_partition() {
6200 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6201
6202 let builder = PostgresQueryBuilder::new();
6203 let mut stmt = Query::select();
6204
6205 let window = WindowStatement {
6206 partition_by: vec![Expr::col("region").into_simple_expr()],
6207 order_by: vec![OrderExpr {
6208 expr: OrderExprKind::Column("revenue".into_iden()),
6209 order: Order::Desc,
6210 nulls: None,
6211 }],
6212 frame: None,
6213 };
6214
6215 stmt.expr(Expr::ntile(5).over(window))
6216 .column("store_name")
6217 .from("stores");
6218
6219 let (sql, _values) = builder.build_select(&stmt);
6220 assert_eq!(
6221 sql,
6222 r#"SELECT NTILE($1) OVER ( PARTITION BY "region" ORDER BY "revenue" DESC ), "store_name" FROM "stores""#
6223 );
6224 }
6225
6226 #[test]
6227 fn test_window_lead_with_offset_no_default() {
6228 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6229
6230 let builder = PostgresQueryBuilder::new();
6231 let mut stmt = Query::select();
6232
6233 let window = WindowStatement {
6234 partition_by: vec![],
6235 order_by: vec![OrderExpr {
6236 expr: OrderExprKind::Column("quarter".into_iden()),
6237 order: Order::Asc,
6238 nulls: None,
6239 }],
6240 frame: None,
6241 };
6242
6243 stmt.expr(Expr::lead(Expr::col("sales").into_simple_expr(), Some(2), None).over(window))
6244 .column("quarter")
6245 .from("quarterly_sales");
6246
6247 let (sql, _values) = builder.build_select(&stmt);
6248 assert_eq!(
6249 sql,
6250 r#"SELECT LEAD("sales", $1) OVER ( ORDER BY "quarter" ASC ), "quarter" FROM "quarterly_sales""#
6251 );
6252 }
6253
6254 #[test]
6255 fn test_window_lag_with_different_offset() {
6256 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6257
6258 let builder = PostgresQueryBuilder::new();
6259 let mut stmt = Query::select();
6260
6261 let window = WindowStatement {
6262 partition_by: vec![Expr::col("sensor_id").into_simple_expr()],
6263 order_by: vec![OrderExpr {
6264 expr: OrderExprKind::Column("timestamp".into_iden()),
6265 order: Order::Asc,
6266 nulls: None,
6267 }],
6268 frame: None,
6269 };
6270
6271 stmt.expr(Expr::lag(Expr::col("reading").into_simple_expr(), Some(5), None).over(window))
6272 .column("timestamp")
6273 .from("sensor_data");
6274
6275 let (sql, values) = builder.build_select(&stmt);
6276 assert_eq!(
6277 sql,
6278 r#"SELECT LAG("reading", $1) OVER ( PARTITION BY "sensor_id" ORDER BY "timestamp" ASC ), "timestamp" FROM "sensor_data""#
6279 );
6280 assert_eq!(values.len(), 1);
6281 }
6282
6283 #[test]
6284 fn test_window_multiple_functions_in_query() {
6285 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6286
6287 let builder = PostgresQueryBuilder::new();
6288 let mut stmt = Query::select();
6289
6290 let window1 = WindowStatement {
6291 partition_by: vec![Expr::col("department").into_simple_expr()],
6292 order_by: vec![OrderExpr {
6293 expr: OrderExprKind::Column("salary".into_iden()),
6294 order: Order::Desc,
6295 nulls: None,
6296 }],
6297 frame: None,
6298 };
6299
6300 let window2 = WindowStatement {
6301 partition_by: vec![],
6302 order_by: vec![OrderExpr {
6303 expr: OrderExprKind::Column("hire_date".into_iden()),
6304 order: Order::Asc,
6305 nulls: None,
6306 }],
6307 frame: None,
6308 };
6309
6310 stmt.expr(Expr::row_number().over(window1))
6311 .expr(Expr::rank().over(window2))
6312 .column("name")
6313 .from("employees");
6314
6315 let (sql, _values) = builder.build_select(&stmt);
6316 assert!(
6317 sql.contains(
6318 r#"ROW_NUMBER() OVER ( PARTITION BY "department" ORDER BY "salary" DESC )"#
6319 )
6320 );
6321 assert!(sql.contains(r#"RANK() OVER ( ORDER BY "hire_date" ASC )"#));
6322 assert!(sql.contains(r#""name""#));
6323 assert!(sql.contains(r#"FROM "employees""#));
6324 }
6325
6326 #[test]
6329 fn test_join_three_tables() {
6330 let builder = PostgresQueryBuilder::new();
6331 let mut stmt = Query::select();
6332 stmt.column(("users", "name"))
6333 .column(("orders", "order_date"))
6334 .column(("products", "product_name"))
6335 .from("users")
6336 .inner_join(
6337 "orders",
6338 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6339 )
6340 .inner_join(
6341 "products",
6342 Expr::col(("orders", "product_id")).eq(Expr::col(("products", "id"))),
6343 );
6344
6345 let (sql, _values) = builder.build_select(&stmt);
6346 assert_eq!(
6347 sql,
6348 r#"SELECT "users"."name", "orders"."order_date", "products"."product_name" FROM "users" INNER JOIN "orders" ON "users"."id" = "orders"."user_id" INNER JOIN "products" ON "orders"."product_id" = "products"."id""#
6349 );
6350 }
6351
6352 #[test]
6353 fn test_self_join() {
6354 use crate::types::TableRef;
6355
6356 let builder = PostgresQueryBuilder::new();
6357 let mut stmt = Query::select();
6358 stmt.column(("e1", "name"))
6359 .column(("e2", "name"))
6360 .from(TableRef::table_alias("employees", "e1"))
6361 .inner_join(
6362 TableRef::table_alias("employees", "e2"),
6363 Expr::col(("e1", "manager_id")).eq(Expr::col(("e2", "id"))),
6364 );
6365
6366 let (sql, _values) = builder.build_select(&stmt);
6367 assert!(sql.contains(r#"FROM "employees" AS "e1""#));
6368 assert!(sql.contains(r#"INNER JOIN "employees" AS "e2""#));
6369 assert!(sql.contains(r#"ON "e1"."manager_id" = "e2"."id""#));
6370 }
6371
6372 #[test]
6373 fn test_join_complex_conditions() {
6374 let builder = PostgresQueryBuilder::new();
6375 let mut stmt = Query::select();
6376 stmt.from("orders").left_join(
6377 "customers",
6378 Expr::col(("orders", "customer_id"))
6379 .eq(Expr::col(("customers", "id")))
6380 .and(Expr::col(("customers", "active")).eq(true))
6381 .and(
6382 Expr::col(("orders", "created_at"))
6383 .gt(Expr::col(("customers", "registered_at"))),
6384 ),
6385 );
6386
6387 let (sql, values) = builder.build_select(&stmt);
6388 assert!(sql.contains("LEFT JOIN \"customers\""));
6389 assert!(sql.contains("\"orders\".\"customer_id\" = \"customers\".\"id\""));
6390 assert!(sql.contains("AND \"customers\".\"active\" = $"));
6391 assert!(sql.contains("AND \"orders\".\"created_at\" > \"customers\".\"registered_at\""));
6392 assert_eq!(values.len(), 1); }
6394
6395 #[test]
6396 fn test_join_with_subquery_in_condition() {
6397 let builder = PostgresQueryBuilder::new();
6398
6399 let mut subquery = Query::select();
6400 subquery.expr(Expr::col("max_id")).from("user_stats");
6401
6402 let mut stmt = Query::select();
6403 stmt.from("users").inner_join(
6404 "profiles",
6405 Expr::col(("users", "id"))
6406 .eq(Expr::col(("profiles", "user_id")))
6407 .and(Expr::col(("users", "id")).in_subquery(subquery)),
6408 );
6409
6410 let (sql, _values) = builder.build_select(&stmt);
6411 assert!(sql.contains("INNER JOIN \"profiles\""));
6412 assert!(sql.contains("\"users\".\"id\" = \"profiles\".\"user_id\""));
6413 assert!(sql.contains("IN"));
6414 assert!(sql.contains("SELECT \"max_id\" FROM \"user_stats\""));
6415 }
6416
6417 #[test]
6418 fn test_multiple_left_joins() {
6419 let builder = PostgresQueryBuilder::new();
6420 let mut stmt = Query::select();
6421 stmt.column(("users", "name"))
6422 .column(("profiles", "bio"))
6423 .column(("addresses", "city"))
6424 .column(("phone_numbers", "number"))
6425 .from("users")
6426 .left_join(
6427 "profiles",
6428 Expr::col(("users", "id")).eq(Expr::col(("profiles", "user_id"))),
6429 )
6430 .left_join(
6431 "addresses",
6432 Expr::col(("users", "id")).eq(Expr::col(("addresses", "user_id"))),
6433 )
6434 .left_join(
6435 "phone_numbers",
6436 Expr::col(("users", "id")).eq(Expr::col(("phone_numbers", "user_id"))),
6437 );
6438
6439 let (sql, _values) = builder.build_select(&stmt);
6440 assert!(sql.contains("LEFT JOIN \"profiles\""));
6441 assert!(sql.contains("LEFT JOIN \"addresses\""));
6442 assert!(sql.contains("LEFT JOIN \"phone_numbers\""));
6443 }
6444
6445 #[test]
6446 fn test_mixed_join_types() {
6447 let builder = PostgresQueryBuilder::new();
6448 let mut stmt = Query::select();
6449 stmt.column(("users", "name"))
6450 .from("users")
6451 .inner_join(
6452 "orders",
6453 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6454 )
6455 .left_join(
6456 "reviews",
6457 Expr::col(("orders", "id")).eq(Expr::col(("reviews", "order_id"))),
6458 )
6459 .right_join(
6460 "refunds",
6461 Expr::col(("orders", "id")).eq(Expr::col(("refunds", "order_id"))),
6462 );
6463
6464 let (sql, _values) = builder.build_select(&stmt);
6465 assert!(sql.contains("INNER JOIN \"orders\""));
6466 assert!(sql.contains("LEFT JOIN \"reviews\""));
6467 assert!(sql.contains("RIGHT JOIN \"refunds\""));
6468 }
6469
6470 #[test]
6471 fn test_join_with_group_by() {
6472 use crate::expr::SimpleExpr;
6473 use crate::types::{BinOper, ColumnRef, IntoIden};
6474
6475 let builder = PostgresQueryBuilder::new();
6476 let mut stmt = Query::select();
6477 let count_expr = SimpleExpr::FunctionCall(
6478 "COUNT".into_iden(),
6479 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
6480 );
6481
6482 stmt.column(("users", "name"))
6483 .expr(count_expr.clone())
6484 .from("users")
6485 .inner_join(
6486 "orders",
6487 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6488 )
6489 .group_by(("users", "name"))
6490 .and_having(SimpleExpr::Binary(
6491 Box::new(count_expr),
6492 BinOper::GreaterThan,
6493 Box::new(SimpleExpr::Value(5.into())),
6494 ));
6495
6496 let (sql, values) = builder.build_select(&stmt);
6497 assert!(sql.contains("INNER JOIN \"orders\""));
6498 assert!(sql.contains("GROUP BY \"users\".\"name\""));
6499 assert!(sql.contains("HAVING"));
6500 assert!(sql.contains("COUNT(*) > $"));
6501 assert_eq!(values.len(), 1);
6502 }
6503
6504 #[test]
6505 fn test_join_with_window_function() {
6506 use crate::types::{IntoIden, Order, OrderExpr, OrderExprKind, WindowStatement};
6507
6508 let builder = PostgresQueryBuilder::new();
6509 let mut stmt = Query::select();
6510
6511 let window = WindowStatement {
6512 partition_by: vec![Expr::col(("departments", "name")).into_simple_expr()],
6513 order_by: vec![OrderExpr {
6514 expr: OrderExprKind::TableColumn("employees".into_iden(), "salary".into_iden()),
6515 order: Order::Desc,
6516 nulls: None,
6517 }],
6518 frame: None,
6519 };
6520
6521 stmt.column(("employees", "name"))
6522 .expr(Expr::row_number().over(window))
6523 .from("employees")
6524 .inner_join(
6525 "departments",
6526 Expr::col(("employees", "department_id")).eq(Expr::col(("departments", "id"))),
6527 );
6528
6529 let (sql, _values) = builder.build_select(&stmt);
6530 assert!(sql.contains("INNER JOIN \"departments\""));
6531 assert!(sql.contains("ROW_NUMBER() OVER"));
6532 assert!(sql.contains(r#"PARTITION BY "departments"."name""#));
6533 }
6534
6535 #[test]
6536 fn test_four_table_join() {
6537 let builder = PostgresQueryBuilder::new();
6538 let mut stmt = Query::select();
6539 stmt.column(("users", "name"))
6540 .column(("orders", "order_date"))
6541 .column(("products", "product_name"))
6542 .column(("categories", "category_name"))
6543 .from("users")
6544 .inner_join(
6545 "orders",
6546 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6547 )
6548 .inner_join(
6549 "products",
6550 Expr::col(("orders", "product_id")).eq(Expr::col(("products", "id"))),
6551 )
6552 .inner_join(
6553 "categories",
6554 Expr::col(("products", "category_id")).eq(Expr::col(("categories", "id"))),
6555 );
6556
6557 let (sql, _values) = builder.build_select(&stmt);
6558 assert!(sql.contains("FROM \"users\""));
6559 assert!(sql.contains("INNER JOIN \"orders\""));
6560 assert!(sql.contains("INNER JOIN \"products\""));
6561 assert!(sql.contains("INNER JOIN \"categories\""));
6562 }
6563
6564 #[test]
6565 fn test_join_with_cte() {
6566 use crate::types::TableRef;
6567
6568 let builder = PostgresQueryBuilder::new();
6569
6570 let mut cte = Query::select();
6571 cte.column("user_id")
6572 .expr(Expr::col("total"))
6573 .from("order_totals")
6574 .and_where(Expr::col("total").gt(1000));
6575
6576 let mut stmt = Query::select();
6577 stmt.with_cte("high_value_customers", cte)
6578 .column(("users", "name"))
6579 .column(("hvc", "total"))
6580 .from("users")
6581 .inner_join(
6582 TableRef::table_alias("high_value_customers", "hvc"),
6583 Expr::col(("users", "id")).eq(Expr::col(("hvc", "user_id"))),
6584 );
6585
6586 let (sql, values) = builder.build_select(&stmt);
6587 assert!(sql.contains("WITH \"high_value_customers\" AS"));
6588 assert!(sql.contains("INNER JOIN \"high_value_customers\" AS \"hvc\""));
6589 assert_eq!(values.len(), 1); }
6591
6592 #[test]
6593 fn test_cte_with_where_and_params() {
6594 let builder = PostgresQueryBuilder::new();
6595
6596 let mut cte_query = Query::select();
6597 cte_query
6598 .column("id")
6599 .column("total")
6600 .from("orders")
6601 .and_where(Expr::col("status").eq("completed"))
6602 .and_where(Expr::col("amount").gt(1000));
6603
6604 let mut stmt = Query::select();
6605 stmt.with_cte("large_orders", cte_query)
6606 .column("id")
6607 .column("total")
6608 .from("large_orders");
6609
6610 let (sql, values) = builder.build_select(&stmt);
6611 assert!(sql.contains("WITH"));
6612 assert!(sql.contains(r#""large_orders" AS"#));
6613 assert!(sql.contains(r#""status" = $"#));
6614 assert!(sql.contains(r#""amount" > $"#));
6615 assert_eq!(values.len(), 2);
6616 }
6617
6618 #[test]
6619 fn test_cte_used_in_join() {
6620 use crate::types::TableRef;
6621
6622 let builder = PostgresQueryBuilder::new();
6623
6624 let mut cte_query = Query::select();
6625 cte_query
6626 .column("user_id")
6627 .column("order_count")
6628 .from("orders")
6629 .group_by("user_id");
6630
6631 let mut stmt = Query::select();
6632 stmt.with_cte("user_orders", cte_query)
6633 .column(("users", "name"))
6634 .column(("uo", "order_count"))
6635 .from("users")
6636 .inner_join(
6637 TableRef::table_alias("user_orders", "uo"),
6638 Expr::col(("users", "id")).eq(Expr::col(("uo", "user_id"))),
6639 );
6640
6641 let (sql, values) = builder.build_select(&stmt);
6642 assert!(sql.contains("WITH"));
6643 assert!(sql.contains(r#""user_orders" AS"#));
6644 assert!(sql.contains(r#"INNER JOIN "user_orders" AS "uo""#));
6645 assert!(sql.contains(r#""users"."id" = "uo"."user_id""#));
6646 assert_eq!(values.len(), 0);
6647 }
6648
6649 #[test]
6650 fn test_cte_with_aggregation() {
6651 use crate::expr::SimpleExpr;
6652 use crate::types::{ColumnRef, IntoIden};
6653
6654 let builder = PostgresQueryBuilder::new();
6655
6656 let mut cte_query = Query::select();
6657 cte_query
6658 .column("category")
6659 .expr(SimpleExpr::FunctionCall(
6660 "COUNT".into_iden(),
6661 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
6662 ))
6663 .expr(SimpleExpr::FunctionCall(
6664 "SUM".into_iden(),
6665 vec![SimpleExpr::Column(ColumnRef::column("price"))],
6666 ))
6667 .from("products")
6668 .group_by("category");
6669
6670 let mut stmt = Query::select();
6671 stmt.with_cte("category_stats", cte_query)
6672 .column("category")
6673 .from("category_stats");
6674
6675 let (sql, values) = builder.build_select(&stmt);
6676 assert!(sql.contains("WITH"));
6677 assert!(sql.contains(r#""category_stats" AS"#));
6678 assert!(sql.contains("COUNT(*)"));
6679 assert!(sql.contains(r#"SUM("price")"#));
6680 assert!(sql.contains(r#"GROUP BY "category""#));
6681 assert_eq!(values.len(), 0);
6682 }
6683
6684 #[test]
6685 fn test_cte_with_subquery() {
6686 let builder = PostgresQueryBuilder::new();
6687
6688 let mut sub = Query::select();
6689 sub.column("user_id").from("vip_users");
6690
6691 let mut cte_query = Query::select();
6692 cte_query
6693 .column("id")
6694 .column("total")
6695 .from("orders")
6696 .and_where(Expr::col("user_id").in_subquery(sub))
6697 .and_where(Expr::col("status").eq("shipped"));
6698
6699 let mut stmt = Query::select();
6700 stmt.with_cte("vip_orders", cte_query)
6701 .column("id")
6702 .column("total")
6703 .from("vip_orders");
6704
6705 let (sql, values) = builder.build_select(&stmt);
6706 assert!(sql.contains("WITH"));
6707 assert!(sql.contains(r#""vip_orders" AS"#));
6708 assert!(sql.contains("IN"));
6709 assert!(sql.contains(r#"SELECT "user_id" FROM "vip_users""#));
6710 assert!(sql.contains(r#""status" = $"#));
6711 assert_eq!(values.len(), 1);
6712 }
6713
6714 #[test]
6715 fn test_multiple_recursive_and_regular_ctes() {
6716 let builder = PostgresQueryBuilder::new();
6717
6718 let mut regular_cte = Query::select();
6720 regular_cte
6721 .column("id")
6722 .column("name")
6723 .from("departments")
6724 .and_where(Expr::col("active").eq(true));
6725
6726 let mut recursive_cte = Query::select();
6728 recursive_cte
6729 .column("id")
6730 .column("name")
6731 .column("parent_id")
6732 .from("categories");
6733
6734 let mut stmt = Query::select();
6736 stmt.with_cte("active_depts", regular_cte)
6737 .with_recursive_cte("category_tree", recursive_cte)
6738 .column("name")
6739 .from("category_tree");
6740
6741 let (sql, values) = builder.build_select(&stmt);
6742 assert!(sql.contains("WITH RECURSIVE"));
6743 assert!(sql.contains(r#""active_depts" AS"#));
6744 assert!(sql.contains(r#""category_tree" AS"#));
6745 assert!(sql.contains(r#""active" = $"#));
6746 assert!(sql.contains(r#"FROM "category_tree""#));
6747 assert_eq!(values.len(), 1);
6748 }
6749
6750 #[test]
6753 fn test_case_simple_when_else() {
6754 let builder = PostgresQueryBuilder::new();
6755
6756 let case_expr = Expr::case()
6757 .when(Expr::col("status").eq("active"), "Active")
6758 .else_result("Inactive");
6759
6760 let mut stmt = Query::select();
6761 stmt.expr_as(case_expr, "status_label").from("users");
6762
6763 let (sql, values) = builder.build_select(&stmt);
6764 assert!(sql.contains("CASE"));
6765 assert!(sql.contains("WHEN"));
6766 assert!(sql.contains(r#""status" = $"#));
6767 assert!(sql.contains("THEN"));
6768 assert!(sql.contains("ELSE"));
6769 assert!(sql.contains("END"));
6770 assert!(sql.contains(r#"AS "status_label""#));
6771 assert_eq!(values.len(), 3);
6772 }
6773
6774 #[test]
6775 fn test_case_multiple_when_clauses() {
6776 let builder = PostgresQueryBuilder::new();
6777
6778 let case_expr = Expr::case()
6779 .when(Expr::col("score").gte(90), "A")
6780 .when(Expr::col("score").gte(80), "B")
6781 .when(Expr::col("score").gte(70), "C")
6782 .else_result("F");
6783
6784 let mut stmt = Query::select();
6785 stmt.expr_as(case_expr, "grade").from("students");
6786
6787 let (sql, values) = builder.build_select(&stmt);
6788 assert!(sql.contains("CASE"));
6789 let when_count = sql.matches("WHEN").count();
6791 assert_eq!(when_count, 3);
6792 let then_count = sql.matches("THEN").count();
6793 assert_eq!(then_count, 3);
6794 assert!(sql.contains("ELSE"));
6795 assert!(sql.contains("END"));
6796 assert_eq!(values.len(), 7);
6798 }
6799
6800 #[test]
6801 fn test_case_without_else() {
6802 let builder = PostgresQueryBuilder::new();
6803
6804 let case_expr = Expr::case()
6805 .when(Expr::col("type").eq("admin"), "Administrator")
6806 .when(Expr::col("type").eq("user"), "Regular User")
6807 .build();
6808
6809 let mut stmt = Query::select();
6810 stmt.expr_as(case_expr, "type_label").from("accounts");
6811
6812 let (sql, values) = builder.build_select(&stmt);
6813 assert!(sql.contains("CASE"));
6814 assert!(sql.contains("WHEN"));
6815 assert!(sql.contains("THEN"));
6816 assert!(!sql.contains("ELSE"));
6817 assert!(sql.contains("END"));
6818 assert_eq!(values.len(), 4);
6819 }
6820
6821 #[test]
6822 fn test_case_in_where_clause() {
6823 let builder = PostgresQueryBuilder::new();
6824
6825 let case_expr = Expr::case()
6826 .when(Expr::col("role").eq("admin"), 1)
6827 .else_result(0);
6828
6829 let mut stmt = Query::select();
6830 stmt.column("name").from("users").and_where(case_expr.eq(1));
6831
6832 let (sql, values) = builder.build_select(&stmt);
6833 assert!(sql.contains("WHERE"));
6834 assert!(sql.contains("CASE"));
6835 assert!(sql.contains("WHEN"));
6836 assert!(sql.contains("END"));
6837 assert!(values.len() >= 3);
6838 }
6839
6840 #[test]
6841 fn test_case_in_order_by() {
6842 let builder = PostgresQueryBuilder::new();
6843
6844 let case_expr = Expr::case()
6845 .when(Expr::col("priority").eq("high"), 1)
6846 .when(Expr::col("priority").eq("medium"), 2)
6847 .else_result(3);
6848
6849 let mut stmt = Query::select();
6850 stmt.column("name")
6851 .column("priority")
6852 .from("tasks")
6853 .order_by_expr(case_expr, crate::types::Order::Asc);
6854
6855 let (sql, values) = builder.build_select(&stmt);
6856 assert!(sql.contains("ORDER BY"));
6857 assert!(sql.contains("CASE"));
6858 assert!(sql.contains("WHEN"));
6859 assert!(sql.contains("END"));
6860 assert!(sql.contains("ASC"));
6861 assert_eq!(values.len(), 5);
6862 }
6863
6864 #[test]
6867 fn test_order_by_multiple_columns_mixed() {
6868 let builder = PostgresQueryBuilder::new();
6869
6870 let mut stmt = Query::select();
6871 stmt.column("name")
6872 .column("age")
6873 .column("score")
6874 .from("students")
6875 .order_by("name", crate::types::Order::Asc)
6876 .order_by("age", crate::types::Order::Desc)
6877 .order_by("score", crate::types::Order::Asc);
6878
6879 let (sql, _values) = builder.build_select(&stmt);
6880 assert!(sql.contains("ORDER BY"));
6881 assert!(sql.contains(r#""name" ASC"#));
6882 assert!(sql.contains(r#""age" DESC"#));
6883 assert!(sql.contains(r#""score" ASC"#));
6884 }
6885
6886 #[test]
6887 fn test_order_by_nulls_first() {
6888 use crate::types::{IntoColumnRef, NullOrdering, OrderExpr, OrderExprKind};
6889
6890 let builder = PostgresQueryBuilder::new();
6891
6892 let mut stmt = Query::select();
6893 stmt.column("name").column("created_at").from("events");
6894 stmt.orders.push(OrderExpr {
6895 expr: OrderExprKind::Expr(Box::new(SimpleExpr::Column("created_at".into_column_ref()))),
6896 order: crate::types::Order::Desc,
6897 nulls: Some(NullOrdering::First),
6898 });
6899
6900 let (sql, _values) = builder.build_select(&stmt);
6901 assert!(sql.contains("ORDER BY"));
6902 assert!(sql.contains("DESC"));
6903 assert!(sql.contains("NULLS FIRST"));
6904 }
6905
6906 #[test]
6907 fn test_order_by_nulls_last() {
6908 use crate::types::{IntoColumnRef, NullOrdering, OrderExpr, OrderExprKind};
6909
6910 let builder = PostgresQueryBuilder::new();
6911
6912 let mut stmt = Query::select();
6913 stmt.column("name").column("updated_at").from("posts");
6914 stmt.orders.push(OrderExpr {
6915 expr: OrderExprKind::Expr(Box::new(SimpleExpr::Column("updated_at".into_column_ref()))),
6916 order: crate::types::Order::Asc,
6917 nulls: Some(NullOrdering::Last),
6918 });
6919
6920 let (sql, _values) = builder.build_select(&stmt);
6921 assert!(sql.contains("ORDER BY"));
6922 assert!(sql.contains("ASC"));
6923 assert!(sql.contains("NULLS LAST"));
6924 }
6925
6926 #[test]
6927 fn test_limit_without_offset() {
6928 let builder = PostgresQueryBuilder::new();
6929
6930 let mut stmt = Query::select();
6931 stmt.column("id").from("items").limit(5);
6932
6933 let (sql, values) = builder.build_select(&stmt);
6934 assert!(sql.contains("LIMIT"));
6935 assert!(!sql.contains("OFFSET"));
6936 assert_eq!(values.len(), 1);
6937 }
6938
6939 #[test]
6942 fn test_arithmetic_add_sub() {
6943 let builder = PostgresQueryBuilder::new();
6944 let mut stmt = Query::select();
6945 stmt.column("name").from("products");
6946 stmt.and_where(Expr::col("price").add(10i32).gt(100i32));
6947
6948 let (sql, values) = builder.build_select(&stmt);
6949 assert!(sql.contains(r#""price" + $1"#));
6950 assert!(sql.contains("> $2"));
6951 assert_eq!(values.len(), 2);
6952 }
6953
6954 #[test]
6955 fn test_arithmetic_mul_div_mod() {
6956 let builder = PostgresQueryBuilder::new();
6957 let mut stmt = Query::select();
6958 stmt.column("name").from("items");
6959 stmt.and_where(
6960 Expr::col("quantity")
6961 .mul(Expr::col("unit_price"))
6962 .gt(1000i32),
6963 );
6964
6965 let (sql, values) = builder.build_select(&stmt);
6966 assert!(sql.contains(r#""quantity" * "unit_price""#));
6967 assert!(sql.contains("> $1"));
6968 assert_eq!(values.len(), 1);
6969 }
6970
6971 #[test]
6972 fn test_like_ilike_pattern() {
6973 let builder = PostgresQueryBuilder::new();
6974 let mut stmt = Query::select();
6975 stmt.column("name").from("users");
6976 stmt.and_where(Expr::col("email").like("%@example.com"));
6977
6978 let (sql, values) = builder.build_select(&stmt);
6979 assert!(sql.contains(r#""email" LIKE $1"#));
6980 assert_eq!(values.len(), 1);
6981 }
6982
6983 #[test]
6984 fn test_pg_concat_operator() {
6985 use crate::types::{BinOper, IntoColumnRef, PgBinOper};
6986 let builder = PostgresQueryBuilder::new();
6987 let mut stmt = Query::select();
6988 stmt.expr(SimpleExpr::Binary(
6989 Box::new(SimpleExpr::Column("first_name".into_column_ref())),
6990 BinOper::PgOperator(PgBinOper::Concatenate),
6991 Box::new(SimpleExpr::Column("last_name".into_column_ref())),
6992 ));
6993 stmt.from("users");
6994
6995 let (sql, _values) = builder.build_select(&stmt);
6996 assert!(sql.contains(r#""first_name" || "last_name""#));
6997 }
6998
6999 #[test]
7002 fn test_drop_table_basic() {
7003 let builder = PostgresQueryBuilder::new();
7004 let mut stmt = Query::drop_table();
7005 stmt.table("users");
7006
7007 let (sql, values) = builder.build_drop_table(&stmt);
7008 assert_eq!(sql, "DROP TABLE \"users\"");
7009 assert_eq!(values.len(), 0);
7010 }
7011
7012 #[test]
7013 fn test_drop_table_if_exists() {
7014 let builder = PostgresQueryBuilder::new();
7015 let mut stmt = Query::drop_table();
7016 stmt.table("users").if_exists();
7017
7018 let (sql, values) = builder.build_drop_table(&stmt);
7019 assert_eq!(sql, "DROP TABLE IF EXISTS \"users\"");
7020 assert_eq!(values.len(), 0);
7021 }
7022
7023 #[test]
7024 fn test_drop_table_cascade() {
7025 let builder = PostgresQueryBuilder::new();
7026 let mut stmt = Query::drop_table();
7027 stmt.table("users").cascade();
7028
7029 let (sql, values) = builder.build_drop_table(&stmt);
7030 assert_eq!(sql, "DROP TABLE \"users\" CASCADE");
7031 assert_eq!(values.len(), 0);
7032 }
7033
7034 #[test]
7035 fn test_drop_table_restrict() {
7036 let builder = PostgresQueryBuilder::new();
7037 let mut stmt = Query::drop_table();
7038 stmt.table("users").restrict();
7039
7040 let (sql, values) = builder.build_drop_table(&stmt);
7041 assert_eq!(sql, "DROP TABLE \"users\" RESTRICT");
7042 assert_eq!(values.len(), 0);
7043 }
7044
7045 #[test]
7046 fn test_drop_table_multiple() {
7047 let builder = PostgresQueryBuilder::new();
7048 let mut stmt = Query::drop_table();
7049 stmt.table("users").table("posts");
7050
7051 let (sql, values) = builder.build_drop_table(&stmt);
7052 assert_eq!(sql, "DROP TABLE \"users\", \"posts\"");
7053 assert_eq!(values.len(), 0);
7054 }
7055
7056 #[test]
7057 fn test_drop_index_basic() {
7058 let builder = PostgresQueryBuilder::new();
7059 let mut stmt = Query::drop_index();
7060 stmt.name("idx_email");
7061
7062 let (sql, values) = builder.build_drop_index(&stmt);
7063 assert_eq!(sql, "DROP INDEX \"idx_email\"");
7064 assert_eq!(values.len(), 0);
7065 }
7066
7067 #[test]
7068 fn test_drop_index_if_exists() {
7069 let builder = PostgresQueryBuilder::new();
7070 let mut stmt = Query::drop_index();
7071 stmt.name("idx_email").if_exists();
7072
7073 let (sql, values) = builder.build_drop_index(&stmt);
7074 assert_eq!(sql, "DROP INDEX IF EXISTS \"idx_email\"");
7075 assert_eq!(values.len(), 0);
7076 }
7077
7078 #[test]
7079 fn test_drop_index_cascade() {
7080 let builder = PostgresQueryBuilder::new();
7081 let mut stmt = Query::drop_index();
7082 stmt.name("idx_email").cascade();
7083
7084 let (sql, values) = builder.build_drop_index(&stmt);
7085 assert_eq!(sql, "DROP INDEX \"idx_email\" CASCADE");
7086 assert_eq!(values.len(), 0);
7087 }
7088
7089 #[test]
7090 fn test_drop_index_restrict() {
7091 let builder = PostgresQueryBuilder::new();
7092 let mut stmt = Query::drop_index();
7093 stmt.name("idx_email").restrict();
7094
7095 let (sql, values) = builder.build_drop_index(&stmt);
7096 assert_eq!(sql, "DROP INDEX \"idx_email\" RESTRICT");
7097 assert_eq!(values.len(), 0);
7098 }
7099
7100 #[test]
7103 fn test_create_table_basic() {
7104 use crate::types::{ColumnDef, ColumnType};
7105
7106 let builder = PostgresQueryBuilder::new();
7107 let mut stmt = Query::create_table();
7108 stmt.table("users");
7109 stmt.columns.push(ColumnDef {
7110 name: "id".into_iden(),
7111 column_type: Some(ColumnType::Integer),
7112 not_null: false,
7113 unique: false,
7114 primary_key: false,
7115 auto_increment: false,
7116 default: None,
7117 check: None,
7118 comment: None,
7119 });
7120 stmt.columns.push(ColumnDef {
7121 name: "name".into_iden(),
7122 column_type: Some(ColumnType::String(Some(255))),
7123 not_null: false,
7124 unique: false,
7125 primary_key: false,
7126 auto_increment: false,
7127 default: None,
7128 check: None,
7129 comment: None,
7130 });
7131
7132 let (sql, values) = builder.build_create_table(&stmt);
7133 assert!(sql.contains("CREATE TABLE \"users\""));
7134 assert!(sql.contains("\"id\" INTEGER"));
7135 assert!(sql.contains("\"name\" VARCHAR(255)"));
7136 assert_eq!(values.len(), 0);
7137 }
7138
7139 #[test]
7140 fn test_create_table_if_not_exists() {
7141 use crate::types::{ColumnDef, ColumnType};
7142
7143 let builder = PostgresQueryBuilder::new();
7144 let mut stmt = Query::create_table();
7145 stmt.table("users").if_not_exists();
7146 stmt.columns.push(ColumnDef {
7147 name: "id".into_iden(),
7148 column_type: Some(ColumnType::Integer),
7149 not_null: false,
7150 unique: false,
7151 primary_key: false,
7152 auto_increment: false,
7153 default: None,
7154 check: None,
7155 comment: None,
7156 });
7157
7158 let (sql, values) = builder.build_create_table(&stmt);
7159 assert!(sql.contains("CREATE TABLE IF NOT EXISTS \"users\""));
7160 assert!(sql.contains("\"id\" INTEGER"));
7161 assert_eq!(values.len(), 0);
7162 }
7163
7164 #[test]
7165 fn test_create_table_with_primary_key() {
7166 use crate::types::{ColumnDef, ColumnType};
7167
7168 let builder = PostgresQueryBuilder::new();
7169 let mut stmt = Query::create_table();
7170 stmt.table("users");
7171 stmt.columns.push(ColumnDef {
7172 name: "id".into_iden(),
7173 column_type: Some(ColumnType::Integer),
7174 not_null: false,
7175 unique: false,
7176 primary_key: true,
7177 auto_increment: false,
7178 default: None,
7179 check: None,
7180 comment: None,
7181 });
7182
7183 let (sql, values) = builder.build_create_table(&stmt);
7184 assert!(sql.contains("CREATE TABLE \"users\""));
7185 assert!(sql.contains("\"id\" INTEGER PRIMARY KEY"));
7186 assert_eq!(values.len(), 0);
7187 }
7188
7189 #[test]
7190 fn test_create_table_with_not_null() {
7191 use crate::types::{ColumnDef, ColumnType};
7192
7193 let builder = PostgresQueryBuilder::new();
7194 let mut stmt = Query::create_table();
7195 stmt.table("users");
7196 stmt.columns.push(ColumnDef {
7197 name: "email".into_iden(),
7198 column_type: Some(ColumnType::String(Some(255))),
7199 not_null: true,
7200 unique: false,
7201 primary_key: false,
7202 auto_increment: false,
7203 default: None,
7204 check: None,
7205 comment: None,
7206 });
7207
7208 let (sql, values) = builder.build_create_table(&stmt);
7209 assert!(sql.contains("\"email\" VARCHAR(255) NOT NULL"));
7210 assert_eq!(values.len(), 0);
7211 }
7212
7213 #[test]
7214 fn test_create_table_with_unique() {
7215 use crate::types::{ColumnDef, ColumnType};
7216
7217 let builder = PostgresQueryBuilder::new();
7218 let mut stmt = Query::create_table();
7219 stmt.table("users");
7220 stmt.columns.push(ColumnDef {
7221 name: "username".into_iden(),
7222 column_type: Some(ColumnType::String(Some(50))),
7223 not_null: false,
7224 unique: true,
7225 primary_key: false,
7226 auto_increment: false,
7227 default: None,
7228 check: None,
7229 comment: None,
7230 });
7231
7232 let (sql, values) = builder.build_create_table(&stmt);
7233 assert!(sql.contains("\"username\" VARCHAR(50) UNIQUE"));
7234 assert_eq!(values.len(), 0);
7235 }
7236
7237 #[test]
7238 fn test_create_table_with_default() {
7239 use crate::types::{ColumnDef, ColumnType};
7240
7241 let builder = PostgresQueryBuilder::new();
7242 let mut stmt = Query::create_table();
7243 stmt.table("users");
7244 stmt.columns.push(ColumnDef {
7245 name: "active".into_iden(),
7246 column_type: Some(ColumnType::Boolean),
7247 not_null: false,
7248 unique: false,
7249 primary_key: false,
7250 auto_increment: false,
7251 default: Some(Expr::value(true).into_simple_expr()),
7252 check: None,
7253 comment: None,
7254 });
7255
7256 let (sql, values) = builder.build_create_table(&stmt);
7257 assert!(sql.contains("\"active\" BOOLEAN DEFAULT"));
7258 assert_eq!(values.len(), 1);
7259 }
7260
7261 #[test]
7262 fn test_create_table_with_check() {
7263 use crate::types::{ColumnDef, ColumnType};
7264
7265 let builder = PostgresQueryBuilder::new();
7266 let mut stmt = Query::create_table();
7267 stmt.table("users");
7268 stmt.columns.push(ColumnDef {
7269 name: "age".into_iden(),
7270 column_type: Some(ColumnType::Integer),
7271 not_null: false,
7272 unique: false,
7273 primary_key: false,
7274 auto_increment: false,
7275 default: None,
7276 check: Some(Expr::col("age").gte(0).into_simple_expr()),
7277 comment: None,
7278 });
7279
7280 let (sql, values) = builder.build_create_table(&stmt);
7281 assert!(sql.contains("\"age\" INTEGER CHECK"));
7283 assert!(sql.contains(">= 0"));
7284 assert_eq!(values.len(), 0);
7285 }
7286
7287 #[test]
7288 fn test_create_table_with_table_constraint() {
7289 use crate::types::{ColumnDef, ColumnType, TableConstraint};
7290
7291 let builder = PostgresQueryBuilder::new();
7292 let mut stmt = Query::create_table();
7293 stmt.table("users");
7294 stmt.columns.push(ColumnDef {
7295 name: "id".into_iden(),
7296 column_type: Some(ColumnType::Integer),
7297 not_null: false,
7298 unique: false,
7299 primary_key: false,
7300 auto_increment: false,
7301 default: None,
7302 check: None,
7303 comment: None,
7304 });
7305 stmt.columns.push(ColumnDef {
7306 name: "email".into_iden(),
7307 column_type: Some(ColumnType::String(Some(255))),
7308 not_null: false,
7309 unique: false,
7310 primary_key: false,
7311 auto_increment: false,
7312 default: None,
7313 check: None,
7314 comment: None,
7315 });
7316 stmt.constraints.push(TableConstraint::PrimaryKey {
7317 name: Some("pk_users".into_iden()),
7318 columns: vec!["id".into_iden()],
7319 });
7320
7321 let (sql, values) = builder.build_create_table(&stmt);
7322 assert!(sql.contains("CONSTRAINT \"pk_users\" PRIMARY KEY (\"id\")"));
7323 assert_eq!(values.len(), 0);
7324 }
7325
7326 #[test]
7327 fn test_create_table_with_foreign_key() {
7328 use crate::types::{
7329 ColumnDef, ColumnType, ForeignKeyAction, IntoTableRef, TableConstraint,
7330 };
7331
7332 let builder = PostgresQueryBuilder::new();
7333 let mut stmt = Query::create_table();
7334 stmt.table("posts");
7335 stmt.columns.push(ColumnDef {
7336 name: "id".into_iden(),
7337 column_type: Some(ColumnType::Integer),
7338 not_null: false,
7339 unique: false,
7340 primary_key: true,
7341 auto_increment: false,
7342 default: None,
7343 check: None,
7344 comment: None,
7345 });
7346 stmt.columns.push(ColumnDef {
7347 name: "user_id".into_iden(),
7348 column_type: Some(ColumnType::Integer),
7349 not_null: false,
7350 unique: false,
7351 primary_key: false,
7352 auto_increment: false,
7353 default: None,
7354 check: None,
7355 comment: None,
7356 });
7357 stmt.constraints.push(TableConstraint::ForeignKey {
7358 name: Some("fk_user".into_iden()),
7359 columns: vec!["user_id".into_iden()],
7360 ref_table: Box::new("users".into_table_ref()),
7361 ref_columns: vec!["id".into_iden()],
7362 on_delete: Some(ForeignKeyAction::Cascade),
7363 on_update: Some(ForeignKeyAction::Restrict),
7364 });
7365
7366 let (sql, values) = builder.build_create_table(&stmt);
7367 assert!(sql.contains("CONSTRAINT \"fk_user\" FOREIGN KEY (\"user_id\")"));
7368 assert!(sql.contains("REFERENCES \"users\" (\"id\")"));
7369 assert!(sql.contains("ON DELETE CASCADE"));
7370 assert!(sql.contains("ON UPDATE RESTRICT"));
7371 assert_eq!(values.len(), 0);
7372 }
7373
7374 #[test]
7375 fn test_create_index_basic() {
7376 use crate::query::IndexColumn;
7377
7378 let builder = PostgresQueryBuilder::new();
7379 let mut stmt = Query::create_index();
7380 stmt.name("idx_users_email");
7381 stmt.table("users");
7382 stmt.columns.push(IndexColumn {
7383 name: "email".into_iden(),
7384 order: None,
7385 });
7386
7387 let (sql, values) = builder.build_create_index(&stmt);
7388 assert_eq!(
7389 sql,
7390 r#"CREATE INDEX "idx_users_email" ON "users" ("email")"#
7391 );
7392 assert_eq!(values.len(), 0);
7393 }
7394
7395 #[test]
7396 fn test_create_index_unique() {
7397 use crate::query::IndexColumn;
7398
7399 let builder = PostgresQueryBuilder::new();
7400 let mut stmt = Query::create_index();
7401 stmt.name("idx_users_username");
7402 stmt.table("users");
7403 stmt.unique = true;
7404 stmt.columns.push(IndexColumn {
7405 name: "username".into_iden(),
7406 order: None,
7407 });
7408
7409 let (sql, values) = builder.build_create_index(&stmt);
7410 assert_eq!(
7411 sql,
7412 r#"CREATE UNIQUE INDEX "idx_users_username" ON "users" ("username")"#
7413 );
7414 assert_eq!(values.len(), 0);
7415 }
7416
7417 #[test]
7418 fn test_create_index_if_not_exists() {
7419 use crate::query::IndexColumn;
7420
7421 let builder = PostgresQueryBuilder::new();
7422 let mut stmt = Query::create_index();
7423 stmt.name("idx_users_email");
7424 stmt.table("users");
7425 stmt.if_not_exists = true;
7426 stmt.columns.push(IndexColumn {
7427 name: "email".into_iden(),
7428 order: None,
7429 });
7430
7431 let (sql, values) = builder.build_create_index(&stmt);
7432 assert_eq!(
7433 sql,
7434 r#"CREATE INDEX IF NOT EXISTS "idx_users_email" ON "users" ("email")"#
7435 );
7436 assert_eq!(values.len(), 0);
7437 }
7438
7439 #[test]
7440 fn test_create_index_with_order() {
7441 use crate::query::IndexColumn;
7442 use crate::types::Order;
7443
7444 let builder = PostgresQueryBuilder::new();
7445 let mut stmt = Query::create_index();
7446 stmt.name("idx_users_created");
7447 stmt.table("users");
7448 stmt.columns.push(IndexColumn {
7449 name: "created_at".into_iden(),
7450 order: Some(Order::Desc),
7451 });
7452
7453 let (sql, values) = builder.build_create_index(&stmt);
7454 assert_eq!(
7455 sql,
7456 r#"CREATE INDEX "idx_users_created" ON "users" ("created_at" DESC)"#
7457 );
7458 assert_eq!(values.len(), 0);
7459 }
7460
7461 #[test]
7462 fn test_create_index_multiple_columns() {
7463 use crate::query::IndexColumn;
7464 use crate::types::Order;
7465
7466 let builder = PostgresQueryBuilder::new();
7467 let mut stmt = Query::create_index();
7468 stmt.name("idx_users_name");
7469 stmt.table("users");
7470 stmt.columns.push(IndexColumn {
7471 name: "last_name".into_iden(),
7472 order: Some(Order::Asc),
7473 });
7474 stmt.columns.push(IndexColumn {
7475 name: "first_name".into_iden(),
7476 order: Some(Order::Asc),
7477 });
7478
7479 let (sql, values) = builder.build_create_index(&stmt);
7480 assert_eq!(
7481 sql,
7482 r#"CREATE INDEX "idx_users_name" ON "users" ("last_name" ASC, "first_name" ASC)"#
7483 );
7484 assert_eq!(values.len(), 0);
7485 }
7486
7487 #[test]
7488 fn test_create_index_with_using_btree() {
7489 use crate::query::{IndexColumn, IndexMethod};
7490
7491 let builder = PostgresQueryBuilder::new();
7492 let mut stmt = Query::create_index();
7493 stmt.name("idx_users_id");
7494 stmt.table("users");
7495 stmt.using = Some(IndexMethod::BTree);
7496 stmt.columns.push(IndexColumn {
7497 name: "id".into_iden(),
7498 order: None,
7499 });
7500
7501 let (sql, values) = builder.build_create_index(&stmt);
7502 assert_eq!(
7503 sql,
7504 r#"CREATE INDEX "idx_users_id" ON "users" USING BTREE ("id")"#
7505 );
7506 assert_eq!(values.len(), 0);
7507 }
7508
7509 #[test]
7510 fn test_create_index_with_using_gin() {
7511 use crate::query::{IndexColumn, IndexMethod};
7512
7513 let builder = PostgresQueryBuilder::new();
7514 let mut stmt = Query::create_index();
7515 stmt.name("idx_posts_tags");
7516 stmt.table("posts");
7517 stmt.using = Some(IndexMethod::Gin);
7518 stmt.columns.push(IndexColumn {
7519 name: "tags".into_iden(),
7520 order: None,
7521 });
7522
7523 let (sql, values) = builder.build_create_index(&stmt);
7524 assert_eq!(
7525 sql,
7526 r#"CREATE INDEX "idx_posts_tags" ON "posts" USING GIN ("tags")"#
7527 );
7528 assert_eq!(values.len(), 0);
7529 }
7530
7531 #[test]
7532 fn test_create_index_partial_with_where() {
7533 use crate::query::IndexColumn;
7534
7535 let builder = PostgresQueryBuilder::new();
7536 let mut stmt = Query::create_index();
7537 stmt.name("idx_users_active_email");
7538 stmt.table("users");
7539 stmt.columns.push(IndexColumn {
7540 name: "email".into_iden(),
7541 order: None,
7542 });
7543 stmt.r#where = Some(Expr::col("active").eq(true).into_simple_expr());
7544
7545 let (sql, values) = builder.build_create_index(&stmt);
7546 assert_eq!(
7547 sql,
7548 r#"CREATE INDEX "idx_users_active_email" ON "users" ("email") WHERE "active" = $1"#
7549 );
7550 assert_eq!(values.len(), 1);
7551 }
7552
7553 #[test]
7554 fn test_alter_table_add_column() {
7555 use crate::query::AlterTableOperation;
7556 use crate::types::{ColumnDef, ColumnType};
7557
7558 let builder = PostgresQueryBuilder::new();
7559 let mut stmt = Query::alter_table();
7560 stmt.table("users");
7561 stmt.operations
7562 .push(AlterTableOperation::AddColumn(ColumnDef {
7563 name: "age".into_iden(),
7564 column_type: Some(ColumnType::Integer),
7565 not_null: false,
7566 unique: false,
7567 primary_key: false,
7568 auto_increment: false,
7569 default: None,
7570 check: None,
7571 comment: None,
7572 }));
7573
7574 let (sql, values) = builder.build_alter_table(&stmt);
7575 assert_eq!(sql, r#"ALTER TABLE "users" ADD COLUMN "age" INTEGER"#);
7576 assert_eq!(values.len(), 0);
7577 }
7578
7579 #[test]
7580 fn test_alter_table_drop_column() {
7581 use crate::query::AlterTableOperation;
7582
7583 let builder = PostgresQueryBuilder::new();
7584 let mut stmt = Query::alter_table();
7585 stmt.table("users");
7586 stmt.operations.push(AlterTableOperation::DropColumn {
7587 name: "age".into_iden(),
7588 if_exists: false,
7589 });
7590
7591 let (sql, values) = builder.build_alter_table(&stmt);
7592 assert_eq!(sql, r#"ALTER TABLE "users" DROP COLUMN "age""#);
7593 assert_eq!(values.len(), 0);
7594 }
7595
7596 #[test]
7597 fn test_alter_table_drop_column_if_exists() {
7598 use crate::query::AlterTableOperation;
7599
7600 let builder = PostgresQueryBuilder::new();
7601 let mut stmt = Query::alter_table();
7602 stmt.table("users");
7603 stmt.operations.push(AlterTableOperation::DropColumn {
7604 name: "age".into_iden(),
7605 if_exists: true,
7606 });
7607
7608 let (sql, values) = builder.build_alter_table(&stmt);
7609 assert_eq!(sql, r#"ALTER TABLE "users" DROP COLUMN IF EXISTS "age""#);
7610 assert_eq!(values.len(), 0);
7611 }
7612
7613 #[test]
7614 fn test_alter_table_rename_column() {
7615 use crate::query::AlterTableOperation;
7616
7617 let builder = PostgresQueryBuilder::new();
7618 let mut stmt = Query::alter_table();
7619 stmt.table("users");
7620 stmt.operations.push(AlterTableOperation::RenameColumn {
7621 old: "email".into_iden(),
7622 new: "email_address".into_iden(),
7623 });
7624
7625 let (sql, values) = builder.build_alter_table(&stmt);
7626 assert_eq!(
7627 sql,
7628 r#"ALTER TABLE "users" RENAME COLUMN "email" TO "email_address""#
7629 );
7630 assert_eq!(values.len(), 0);
7631 }
7632
7633 #[test]
7634 fn test_alter_table_modify_column_type() {
7635 use crate::query::AlterTableOperation;
7636 use crate::types::{ColumnDef, ColumnType};
7637
7638 let builder = PostgresQueryBuilder::new();
7639 let mut stmt = Query::alter_table();
7640 stmt.table("users");
7641 stmt.operations
7642 .push(AlterTableOperation::ModifyColumn(ColumnDef {
7643 name: "age".into_iden(),
7644 column_type: Some(ColumnType::BigInteger),
7645 not_null: false,
7646 unique: false,
7647 primary_key: false,
7648 auto_increment: false,
7649 default: None,
7650 check: None,
7651 comment: None,
7652 }));
7653
7654 let (sql, values) = builder.build_alter_table(&stmt);
7655 assert_eq!(sql, r#"ALTER TABLE "users" ALTER COLUMN "age" TYPE BIGINT"#);
7656 assert_eq!(values.len(), 0);
7657 }
7658
7659 #[test]
7660 fn test_alter_table_add_constraint() {
7661 use crate::query::AlterTableOperation;
7662 use crate::types::TableConstraint;
7663
7664 let builder = PostgresQueryBuilder::new();
7665 let mut stmt = Query::alter_table();
7666 stmt.table("users");
7667 stmt.operations.push(AlterTableOperation::AddConstraint(
7668 TableConstraint::Unique {
7669 name: Some("unique_email".into_iden()),
7670 columns: vec!["email".into_iden()],
7671 },
7672 ));
7673
7674 let (sql, values) = builder.build_alter_table(&stmt);
7675 assert_eq!(
7676 sql,
7677 r#"ALTER TABLE "users" ADD CONSTRAINT "unique_email" UNIQUE ("email")"#
7678 );
7679 assert_eq!(values.len(), 0);
7680 }
7681
7682 #[test]
7683 fn test_alter_table_drop_constraint() {
7684 use crate::query::AlterTableOperation;
7685
7686 let builder = PostgresQueryBuilder::new();
7687 let mut stmt = Query::alter_table();
7688 stmt.table("users");
7689 stmt.operations.push(AlterTableOperation::DropConstraint {
7690 name: "unique_email".into_iden(),
7691 if_exists: false,
7692 });
7693
7694 let (sql, values) = builder.build_alter_table(&stmt);
7695 assert_eq!(sql, r#"ALTER TABLE "users" DROP CONSTRAINT "unique_email""#);
7696 assert_eq!(values.len(), 0);
7697 }
7698
7699 #[test]
7700 fn test_alter_table_rename_table() {
7701 use crate::query::AlterTableOperation;
7702
7703 let builder = PostgresQueryBuilder::new();
7704 let mut stmt = Query::alter_table();
7705 stmt.table("users");
7706 stmt.operations
7707 .push(AlterTableOperation::RenameTable("accounts".into_iden()));
7708
7709 let (sql, values) = builder.build_alter_table(&stmt);
7710 assert_eq!(sql, r#"ALTER TABLE "users" RENAME TO "accounts""#);
7711 assert_eq!(values.len(), 0);
7712 }
7713
7714 #[test]
7717 fn test_truncate_table_basic() {
7718 let builder = PostgresQueryBuilder::new();
7719 let mut stmt = Query::truncate_table();
7720 stmt.table("users");
7721
7722 let (sql, values) = builder.build_truncate_table(&stmt);
7723 assert_eq!(sql, r#"TRUNCATE TABLE "users""#);
7724 assert_eq!(values.len(), 0);
7725 }
7726
7727 #[test]
7728 fn test_truncate_table_multiple() {
7729 let builder = PostgresQueryBuilder::new();
7730 let mut stmt = Query::truncate_table();
7731 stmt.table("users").table("posts").table("comments");
7732
7733 let (sql, values) = builder.build_truncate_table(&stmt);
7734 assert_eq!(sql, r#"TRUNCATE TABLE "users", "posts", "comments""#);
7735 assert_eq!(values.len(), 0);
7736 }
7737
7738 #[test]
7739 fn test_truncate_table_restart_identity() {
7740 let builder = PostgresQueryBuilder::new();
7741 let mut stmt = Query::truncate_table();
7742 stmt.table("users").restart_identity();
7743
7744 let (sql, values) = builder.build_truncate_table(&stmt);
7745 assert_eq!(sql, r#"TRUNCATE TABLE "users" RESTART IDENTITY"#);
7746 assert_eq!(values.len(), 0);
7747 }
7748
7749 #[test]
7750 fn test_truncate_table_cascade() {
7751 let builder = PostgresQueryBuilder::new();
7752 let mut stmt = Query::truncate_table();
7753 stmt.table("users").cascade();
7754
7755 let (sql, values) = builder.build_truncate_table(&stmt);
7756 assert_eq!(sql, r#"TRUNCATE TABLE "users" CASCADE"#);
7757 assert_eq!(values.len(), 0);
7758 }
7759
7760 #[test]
7761 fn test_truncate_table_restrict() {
7762 let builder = PostgresQueryBuilder::new();
7763 let mut stmt = Query::truncate_table();
7764 stmt.table("users").restrict();
7765
7766 let (sql, values) = builder.build_truncate_table(&stmt);
7767 assert_eq!(sql, r#"TRUNCATE TABLE "users" RESTRICT"#);
7768 assert_eq!(values.len(), 0);
7769 }
7770
7771 #[test]
7772 fn test_truncate_table_restart_identity_cascade() {
7773 let builder = PostgresQueryBuilder::new();
7774 let mut stmt = Query::truncate_table();
7775 stmt.table("users").restart_identity().cascade();
7776
7777 let (sql, values) = builder.build_truncate_table(&stmt);
7778 assert_eq!(sql, r#"TRUNCATE TABLE "users" RESTART IDENTITY CASCADE"#);
7779 assert_eq!(values.len(), 0);
7780 }
7781
7782 #[test]
7783 fn test_create_trigger_basic() {
7784 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
7785
7786 let builder = PostgresQueryBuilder::new();
7787 let mut stmt = Query::create_trigger();
7788 stmt.name("audit_log")
7789 .timing(TriggerTiming::After)
7790 .event(TriggerEvent::Insert)
7791 .on_table("users")
7792 .for_each(TriggerScope::Row)
7793 .execute_function("log_user_insert");
7794
7795 let (sql, values) = builder.build_create_trigger(&stmt);
7796 assert_eq!(
7797 sql,
7798 r#"CREATE TRIGGER "audit_log" AFTER INSERT ON "users" FOR EACH ROW EXECUTE FUNCTION "log_user_insert"()"#
7799 );
7800 assert_eq!(values.len(), 0);
7801 }
7802
7803 #[test]
7804 fn test_create_trigger_before_update() {
7805 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
7806
7807 let builder = PostgresQueryBuilder::new();
7808 let mut stmt = Query::create_trigger();
7809 stmt.name("update_timestamp")
7810 .timing(TriggerTiming::Before)
7811 .event(TriggerEvent::Update { columns: None })
7812 .on_table("users")
7813 .for_each(TriggerScope::Row)
7814 .execute_function("update_modified_at");
7815
7816 let (sql, values) = builder.build_create_trigger(&stmt);
7817 assert_eq!(
7818 sql,
7819 r#"CREATE TRIGGER "update_timestamp" BEFORE UPDATE ON "users" FOR EACH ROW EXECUTE FUNCTION "update_modified_at"()"#
7820 );
7821 assert_eq!(values.len(), 0);
7822 }
7823
7824 #[test]
7825 fn test_create_trigger_delete_for_statement() {
7826 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
7827
7828 let builder = PostgresQueryBuilder::new();
7829 let mut stmt = Query::create_trigger();
7830 stmt.name("audit_delete")
7831 .timing(TriggerTiming::After)
7832 .event(TriggerEvent::Delete)
7833 .on_table("users")
7834 .for_each(TriggerScope::Statement)
7835 .execute_function("log_bulk_delete");
7836
7837 let (sql, values) = builder.build_create_trigger(&stmt);
7838 assert_eq!(
7839 sql,
7840 r#"CREATE TRIGGER "audit_delete" AFTER DELETE ON "users" FOR EACH STATEMENT EXECUTE FUNCTION "log_bulk_delete"()"#
7841 );
7842 assert_eq!(values.len(), 0);
7843 }
7844
7845 #[test]
7846 fn test_drop_trigger_basic() {
7847 let builder = PostgresQueryBuilder::new();
7848 let mut stmt = Query::drop_trigger();
7849 stmt.name("audit_log").on_table("users");
7850
7851 let (sql, values) = builder.build_drop_trigger(&stmt);
7852 assert_eq!(sql, r#"DROP TRIGGER "audit_log" ON "users""#);
7853 assert_eq!(values.len(), 0);
7854 }
7855
7856 #[test]
7857 fn test_drop_trigger_if_exists() {
7858 let builder = PostgresQueryBuilder::new();
7859 let mut stmt = Query::drop_trigger();
7860 stmt.name("audit_log").on_table("users").if_exists();
7861
7862 let (sql, values) = builder.build_drop_trigger(&stmt);
7863 assert_eq!(sql, r#"DROP TRIGGER IF EXISTS "audit_log" ON "users""#);
7864 assert_eq!(values.len(), 0);
7865 }
7866
7867 #[test]
7868 fn test_drop_trigger_cascade() {
7869 let builder = PostgresQueryBuilder::new();
7870 let mut stmt = Query::drop_trigger();
7871 stmt.name("audit_log").on_table("users").cascade();
7872
7873 let (sql, values) = builder.build_drop_trigger(&stmt);
7874 assert_eq!(sql, r#"DROP TRIGGER "audit_log" ON "users" CASCADE"#);
7875 assert_eq!(values.len(), 0);
7876 }
7877
7878 #[test]
7880 fn test_create_function_basic() {
7881 use crate::types::function::FunctionLanguage;
7882
7883 let builder = PostgresQueryBuilder::new();
7884 let mut stmt = Query::create_function();
7885 stmt.name("my_func")
7886 .returns("integer")
7887 .language(FunctionLanguage::Sql)
7888 .body("SELECT 1");
7889
7890 let (sql, values) = builder.build_create_function(&stmt);
7891 assert_eq!(
7892 sql,
7893 r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL AS $$SELECT 1$$"#
7894 );
7895 assert_eq!(values.len(), 0);
7896 }
7897
7898 #[test]
7899 fn test_create_function_or_replace() {
7900 use crate::types::function::FunctionLanguage;
7901
7902 let builder = PostgresQueryBuilder::new();
7903 let mut stmt = Query::create_function();
7904 stmt.name("my_func")
7905 .or_replace()
7906 .returns("integer")
7907 .language(FunctionLanguage::Sql)
7908 .body("SELECT 1");
7909
7910 let (sql, values) = builder.build_create_function(&stmt);
7911 assert_eq!(
7912 sql,
7913 r#"CREATE OR REPLACE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL AS $$SELECT 1$$"#
7914 );
7915 assert_eq!(values.len(), 0);
7916 }
7917
7918 #[test]
7919 fn test_create_function_with_parameters() {
7920 use crate::types::function::FunctionLanguage;
7921
7922 let builder = PostgresQueryBuilder::new();
7923 let mut stmt = Query::create_function();
7924 stmt.name("add_numbers")
7925 .add_parameter("a", "integer")
7926 .add_parameter("b", "integer")
7927 .returns("integer")
7928 .language(FunctionLanguage::Sql)
7929 .body("SELECT $1 + $2");
7930
7931 let (sql, values) = builder.build_create_function(&stmt);
7932 assert_eq!(
7933 sql,
7934 r#"CREATE FUNCTION "add_numbers"("a" integer, "b" integer) RETURNS integer LANGUAGE SQL AS $$SELECT $1 + $2$$"#
7935 );
7936 assert_eq!(values.len(), 0);
7937 }
7938
7939 #[test]
7940 fn test_create_function_with_behavior() {
7941 use crate::types::function::{FunctionBehavior, FunctionLanguage};
7942
7943 let builder = PostgresQueryBuilder::new();
7944 let mut stmt = Query::create_function();
7945 stmt.name("my_func")
7946 .returns("integer")
7947 .language(FunctionLanguage::Sql)
7948 .behavior(FunctionBehavior::Immutable)
7949 .body("SELECT 1");
7950
7951 let (sql, values) = builder.build_create_function(&stmt);
7952 assert_eq!(
7953 sql,
7954 r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL IMMUTABLE AS $$SELECT 1$$"#
7955 );
7956 assert_eq!(values.len(), 0);
7957 }
7958
7959 #[test]
7960 fn test_create_function_with_security() {
7961 use crate::types::function::{FunctionLanguage, FunctionSecurity};
7962
7963 let builder = PostgresQueryBuilder::new();
7964 let mut stmt = Query::create_function();
7965 stmt.name("my_func")
7966 .returns("integer")
7967 .language(FunctionLanguage::Sql)
7968 .security(FunctionSecurity::Definer)
7969 .body("SELECT 1");
7970
7971 let (sql, values) = builder.build_create_function(&stmt);
7972 assert_eq!(
7973 sql,
7974 r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL SECURITY DEFINER AS $$SELECT 1$$"#
7975 );
7976 assert_eq!(values.len(), 0);
7977 }
7978
7979 #[test]
7980 fn test_create_function_plpgsql() {
7981 use crate::types::function::FunctionLanguage;
7982
7983 let builder = PostgresQueryBuilder::new();
7984 let mut stmt = Query::create_function();
7985 stmt.name("increment")
7986 .add_parameter("val", "integer")
7987 .returns("integer")
7988 .language(FunctionLanguage::PlPgSql)
7989 .body("BEGIN RETURN val + 1; END;");
7990
7991 let (sql, values) = builder.build_create_function(&stmt);
7992 assert_eq!(
7993 sql,
7994 r#"CREATE FUNCTION "increment"("val" integer) RETURNS integer LANGUAGE PLPGSQL AS $$BEGIN RETURN val + 1; END;$$"#
7995 );
7996 assert_eq!(values.len(), 0);
7997 }
7998
7999 #[test]
8000 fn test_create_function_all_options() {
8001 use crate::types::function::{FunctionBehavior, FunctionLanguage, FunctionSecurity};
8002
8003 let builder = PostgresQueryBuilder::new();
8004 let mut stmt = Query::create_function();
8005 stmt.name("complex_func")
8006 .or_replace()
8007 .add_parameter("a", "integer")
8008 .add_parameter("b", "text")
8009 .returns("integer")
8010 .language(FunctionLanguage::PlPgSql)
8011 .behavior(FunctionBehavior::Stable)
8012 .security(FunctionSecurity::Definer)
8013 .body("BEGIN RETURN a + LENGTH(b); END;");
8014
8015 let (sql, values) = builder.build_create_function(&stmt);
8016 assert_eq!(
8017 sql,
8018 r#"CREATE OR REPLACE FUNCTION "complex_func"("a" integer, "b" text) RETURNS integer LANGUAGE PLPGSQL STABLE SECURITY DEFINER AS $$BEGIN RETURN a + LENGTH(b); END;$$"#
8019 );
8020 assert_eq!(values.len(), 0);
8021 }
8022
8023 #[test]
8025 fn test_alter_function_rename_to() {
8026 let builder = PostgresQueryBuilder::new();
8027 let mut stmt = Query::alter_function();
8028 stmt.name("my_func").rename_to("new_func");
8029
8030 let (sql, values) = builder.build_alter_function(&stmt);
8031 assert_eq!(sql, r#"ALTER FUNCTION "my_func" RENAME TO "new_func""#);
8032 assert_eq!(values.len(), 0);
8033 }
8034
8035 #[test]
8036 fn test_alter_function_owner_to() {
8037 let builder = PostgresQueryBuilder::new();
8038 let mut stmt = Query::alter_function();
8039 stmt.name("my_func").owner_to("new_owner");
8040
8041 let (sql, values) = builder.build_alter_function(&stmt);
8042 assert_eq!(sql, r#"ALTER FUNCTION "my_func" OWNER TO "new_owner""#);
8043 assert_eq!(values.len(), 0);
8044 }
8045
8046 #[test]
8047 fn test_alter_function_set_schema() {
8048 let builder = PostgresQueryBuilder::new();
8049 let mut stmt = Query::alter_function();
8050 stmt.name("my_func").set_schema("new_schema");
8051
8052 let (sql, values) = builder.build_alter_function(&stmt);
8053 assert_eq!(sql, r#"ALTER FUNCTION "my_func" SET SCHEMA "new_schema""#);
8054 assert_eq!(values.len(), 0);
8055 }
8056
8057 #[test]
8058 fn test_alter_function_set_behavior_immutable() {
8059 use crate::types::function::FunctionBehavior;
8060
8061 let builder = PostgresQueryBuilder::new();
8062 let mut stmt = Query::alter_function();
8063 stmt.name("my_func")
8064 .set_behavior(FunctionBehavior::Immutable);
8065
8066 let (sql, values) = builder.build_alter_function(&stmt);
8067 assert_eq!(sql, r#"ALTER FUNCTION "my_func" IMMUTABLE"#);
8068 assert_eq!(values.len(), 0);
8069 }
8070
8071 #[test]
8072 fn test_alter_function_set_security_definer() {
8073 use crate::types::function::FunctionSecurity;
8074
8075 let builder = PostgresQueryBuilder::new();
8076 let mut stmt = Query::alter_function();
8077 stmt.name("my_func").set_security(FunctionSecurity::Definer);
8078
8079 let (sql, values) = builder.build_alter_function(&stmt);
8080 assert_eq!(sql, r#"ALTER FUNCTION "my_func" SECURITY DEFINER"#);
8081 assert_eq!(values.len(), 0);
8082 }
8083
8084 #[test]
8085 fn test_alter_function_with_parameters() {
8086 let builder = PostgresQueryBuilder::new();
8087 let mut stmt = Query::alter_function();
8088 stmt.name("my_func")
8089 .add_parameter("a", "integer")
8090 .add_parameter("b", "text")
8091 .rename_to("new_func");
8092
8093 let (sql, values) = builder.build_alter_function(&stmt);
8094 assert_eq!(
8095 sql,
8096 r#"ALTER FUNCTION "my_func"("a" integer, "b" text) RENAME TO "new_func""#
8097 );
8098 assert_eq!(values.len(), 0);
8099 }
8100
8101 #[test]
8103 fn test_drop_function_basic() {
8104 let builder = PostgresQueryBuilder::new();
8105 let mut stmt = Query::drop_function();
8106 stmt.name("my_func");
8107
8108 let (sql, values) = builder.build_drop_function(&stmt);
8109 assert_eq!(sql, r#"DROP FUNCTION "my_func""#);
8110 assert_eq!(values.len(), 0);
8111 }
8112
8113 #[test]
8114 fn test_drop_function_if_exists() {
8115 let builder = PostgresQueryBuilder::new();
8116 let mut stmt = Query::drop_function();
8117 stmt.name("my_func").if_exists();
8118
8119 let (sql, values) = builder.build_drop_function(&stmt);
8120 assert_eq!(sql, r#"DROP FUNCTION IF EXISTS "my_func""#);
8121 assert_eq!(values.len(), 0);
8122 }
8123
8124 #[test]
8125 fn test_drop_function_cascade() {
8126 let builder = PostgresQueryBuilder::new();
8127 let mut stmt = Query::drop_function();
8128 stmt.name("my_func").cascade();
8129
8130 let (sql, values) = builder.build_drop_function(&stmt);
8131 assert_eq!(sql, r#"DROP FUNCTION "my_func" CASCADE"#);
8132 assert_eq!(values.len(), 0);
8133 }
8134
8135 #[test]
8136 fn test_drop_function_with_parameters() {
8137 let builder = PostgresQueryBuilder::new();
8138 let mut stmt = Query::drop_function();
8139 stmt.name("my_func")
8140 .add_parameter("", "integer")
8141 .add_parameter("", "text");
8142
8143 let (sql, values) = builder.build_drop_function(&stmt);
8144 assert_eq!(sql, r#"DROP FUNCTION "my_func"(integer, text)"#);
8145 assert_eq!(values.len(), 0);
8146 }
8147
8148 #[test]
8149 fn test_drop_function_all_options() {
8150 let builder = PostgresQueryBuilder::new();
8151 let mut stmt = Query::drop_function();
8152 stmt.name("my_func")
8153 .if_exists()
8154 .add_parameter("", "integer")
8155 .cascade();
8156
8157 let (sql, values) = builder.build_drop_function(&stmt);
8158 assert_eq!(sql, r#"DROP FUNCTION IF EXISTS "my_func"(integer) CASCADE"#);
8159 assert_eq!(values.len(), 0);
8160 }
8161
8162 #[test]
8164 fn test_create_procedure_basic() {
8165 use crate::types::function::FunctionLanguage;
8166
8167 let builder = PostgresQueryBuilder::new();
8168 let mut stmt = Query::create_procedure();
8169 stmt.name("my_proc")
8170 .language(FunctionLanguage::Sql)
8171 .body("SELECT 1");
8172
8173 let (sql, values) = builder.build_create_procedure(&stmt);
8174 assert_eq!(
8175 sql,
8176 r#"CREATE PROCEDURE "my_proc"() LANGUAGE SQL AS $$SELECT 1$$"#
8177 );
8178 assert_eq!(values.len(), 0);
8179 }
8180
8181 #[test]
8182 fn test_create_procedure_or_replace() {
8183 use crate::types::function::FunctionLanguage;
8184
8185 let builder = PostgresQueryBuilder::new();
8186 let mut stmt = Query::create_procedure();
8187 stmt.name("my_proc")
8188 .or_replace()
8189 .language(FunctionLanguage::PlPgSql)
8190 .body("BEGIN SELECT 1; END;");
8191
8192 let (sql, values) = builder.build_create_procedure(&stmt);
8193 assert_eq!(
8194 sql,
8195 r#"CREATE OR REPLACE PROCEDURE "my_proc"() LANGUAGE PLPGSQL AS $$BEGIN SELECT 1; END;$$"#
8196 );
8197 assert_eq!(values.len(), 0);
8198 }
8199
8200 #[test]
8201 fn test_create_procedure_with_parameters() {
8202 use crate::types::function::FunctionLanguage;
8203
8204 let builder = PostgresQueryBuilder::new();
8205 let mut stmt = Query::create_procedure();
8206 stmt.name("my_proc")
8207 .add_parameter("a", "integer")
8208 .add_parameter("b", "text")
8209 .language(FunctionLanguage::PlPgSql)
8210 .body("BEGIN INSERT INTO log VALUES (a, b); END;");
8211
8212 let (sql, values) = builder.build_create_procedure(&stmt);
8213 assert_eq!(
8214 sql,
8215 r#"CREATE PROCEDURE "my_proc"("a" integer, "b" text) LANGUAGE PLPGSQL AS $$BEGIN INSERT INTO log VALUES (a, b); END;$$"#
8216 );
8217 assert_eq!(values.len(), 0);
8218 }
8219
8220 #[test]
8221 fn test_create_procedure_with_behavior() {
8222 use crate::types::function::{FunctionBehavior, FunctionLanguage};
8223
8224 let builder = PostgresQueryBuilder::new();
8225 let mut stmt = Query::create_procedure();
8226 stmt.name("my_proc")
8227 .language(FunctionLanguage::Sql)
8228 .behavior(FunctionBehavior::Immutable)
8229 .body("SELECT 1");
8230
8231 let (sql, values) = builder.build_create_procedure(&stmt);
8232 assert_eq!(
8233 sql,
8234 r#"CREATE PROCEDURE "my_proc"() LANGUAGE SQL IMMUTABLE AS $$SELECT 1$$"#
8235 );
8236 assert_eq!(values.len(), 0);
8237 }
8238
8239 #[test]
8240 fn test_create_procedure_with_security() {
8241 use crate::types::function::{FunctionLanguage, FunctionSecurity};
8242
8243 let builder = PostgresQueryBuilder::new();
8244 let mut stmt = Query::create_procedure();
8245 stmt.name("my_proc")
8246 .language(FunctionLanguage::Sql)
8247 .security(FunctionSecurity::Definer)
8248 .body("SELECT 1");
8249
8250 let (sql, values) = builder.build_create_procedure(&stmt);
8251 assert_eq!(
8252 sql,
8253 r#"CREATE PROCEDURE "my_proc"() LANGUAGE SQL SECURITY DEFINER AS $$SELECT 1$$"#
8254 );
8255 assert_eq!(values.len(), 0);
8256 }
8257
8258 #[test]
8259 fn test_create_procedure_all_options() {
8260 use crate::types::function::{FunctionBehavior, FunctionLanguage, FunctionSecurity};
8261
8262 let builder = PostgresQueryBuilder::new();
8263 let mut stmt = Query::create_procedure();
8264 stmt.name("my_proc")
8265 .or_replace()
8266 .add_parameter("a", "integer")
8267 .add_parameter("b", "text")
8268 .language(FunctionLanguage::PlPgSql)
8269 .behavior(FunctionBehavior::Immutable)
8270 .security(FunctionSecurity::Definer)
8271 .body("BEGIN INSERT INTO log VALUES (a, b); END;");
8272
8273 let (sql, values) = builder.build_create_procedure(&stmt);
8274 assert_eq!(
8275 sql,
8276 r#"CREATE OR REPLACE PROCEDURE "my_proc"("a" integer, "b" text) LANGUAGE PLPGSQL IMMUTABLE SECURITY DEFINER AS $$BEGIN INSERT INTO log VALUES (a, b); END;$$"#
8277 );
8278 assert_eq!(values.len(), 0);
8279 }
8280
8281 #[test]
8282 fn test_alter_procedure_rename_to() {
8283 let builder = PostgresQueryBuilder::new();
8284 let mut stmt = Query::alter_procedure();
8285 stmt.name("my_proc").rename_to("new_proc");
8286
8287 let (sql, values) = builder.build_alter_procedure(&stmt);
8288 assert_eq!(sql, r#"ALTER PROCEDURE "my_proc" RENAME TO "new_proc""#);
8289 assert_eq!(values.len(), 0);
8290 }
8291
8292 #[test]
8293 fn test_alter_procedure_owner_to() {
8294 let builder = PostgresQueryBuilder::new();
8295 let mut stmt = Query::alter_procedure();
8296 stmt.name("my_proc").owner_to("new_owner");
8297
8298 let (sql, values) = builder.build_alter_procedure(&stmt);
8299 assert_eq!(sql, r#"ALTER PROCEDURE "my_proc" OWNER TO "new_owner""#);
8300 assert_eq!(values.len(), 0);
8301 }
8302
8303 #[test]
8304 fn test_alter_procedure_set_schema() {
8305 let builder = PostgresQueryBuilder::new();
8306 let mut stmt = Query::alter_procedure();
8307 stmt.name("my_proc").set_schema("new_schema");
8308
8309 let (sql, values) = builder.build_alter_procedure(&stmt);
8310 assert_eq!(sql, r#"ALTER PROCEDURE "my_proc" SET SCHEMA "new_schema""#);
8311 assert_eq!(values.len(), 0);
8312 }
8313
8314 #[test]
8315 fn test_alter_procedure_with_signature() {
8316 let builder = PostgresQueryBuilder::new();
8317 let mut stmt = Query::alter_procedure();
8318 stmt.name("my_proc")
8319 .add_parameter("a", "integer")
8320 .rename_to("new_proc");
8321
8322 let (sql, values) = builder.build_alter_procedure(&stmt);
8323 assert_eq!(
8324 sql,
8325 r#"ALTER PROCEDURE "my_proc"("a" integer) RENAME TO "new_proc""#
8326 );
8327 assert_eq!(values.len(), 0);
8328 }
8329
8330 #[test]
8331 fn test_drop_procedure_basic() {
8332 let builder = PostgresQueryBuilder::new();
8333 let mut stmt = Query::drop_procedure();
8334 stmt.name("my_proc");
8335
8336 let (sql, values) = builder.build_drop_procedure(&stmt);
8337 assert_eq!(sql, r#"DROP PROCEDURE "my_proc""#);
8338 assert_eq!(values.len(), 0);
8339 }
8340
8341 #[test]
8342 fn test_drop_procedure_if_exists() {
8343 let builder = PostgresQueryBuilder::new();
8344 let mut stmt = Query::drop_procedure();
8345 stmt.name("my_proc").if_exists();
8346
8347 let (sql, values) = builder.build_drop_procedure(&stmt);
8348 assert_eq!(sql, r#"DROP PROCEDURE IF EXISTS "my_proc""#);
8349 assert_eq!(values.len(), 0);
8350 }
8351
8352 #[test]
8353 fn test_drop_procedure_cascade() {
8354 let builder = PostgresQueryBuilder::new();
8355 let mut stmt = Query::drop_procedure();
8356 stmt.name("my_proc").cascade();
8357
8358 let (sql, values) = builder.build_drop_procedure(&stmt);
8359 assert_eq!(sql, r#"DROP PROCEDURE "my_proc" CASCADE"#);
8360 assert_eq!(values.len(), 0);
8361 }
8362
8363 #[test]
8364 fn test_drop_procedure_with_signature() {
8365 let builder = PostgresQueryBuilder::new();
8366 let mut stmt = Query::drop_procedure();
8367 stmt.name("my_proc").add_parameter("", "integer");
8368
8369 let (sql, values) = builder.build_drop_procedure(&stmt);
8370 assert_eq!(sql, r#"DROP PROCEDURE "my_proc"(integer)"#);
8371 assert_eq!(values.len(), 0);
8372 }
8373
8374 #[test]
8375 fn test_drop_procedure_all_options() {
8376 let builder = PostgresQueryBuilder::new();
8377 let mut stmt = Query::drop_procedure();
8378 stmt.name("my_proc")
8379 .if_exists()
8380 .add_parameter("", "integer")
8381 .cascade();
8382
8383 let (sql, values) = builder.build_drop_procedure(&stmt);
8384 assert_eq!(
8385 sql,
8386 r#"DROP PROCEDURE IF EXISTS "my_proc"(integer) CASCADE"#
8387 );
8388 assert_eq!(values.len(), 0);
8389 }
8390
8391 #[test]
8393 fn test_create_type_enum() {
8394 let builder = PostgresQueryBuilder::new();
8395 let mut stmt = Query::create_type();
8396 stmt.name("mood")
8397 .as_enum(vec!["happy".to_string(), "sad".to_string()]);
8398
8399 let (sql, values) = builder.build_create_type(&stmt);
8400 assert_eq!(sql, r#"CREATE TYPE "mood" AS ENUM ('happy', 'sad')"#);
8401 assert_eq!(values.len(), 0);
8402 }
8403
8404 #[test]
8405 fn test_create_type_enum_with_single_quote() {
8406 let builder = PostgresQueryBuilder::new();
8407 let mut stmt = Query::create_type();
8408 stmt.name("test").as_enum(vec!["it's".to_string()]);
8409
8410 let (sql, values) = builder.build_create_type(&stmt);
8411 assert_eq!(sql, r#"CREATE TYPE "test" AS ENUM ('it''s')"#);
8412 assert_eq!(values.len(), 0);
8413 }
8414
8415 #[test]
8416 fn test_create_type_composite() {
8417 let builder = PostgresQueryBuilder::new();
8418 let mut stmt = Query::create_type();
8419 stmt.name("address").as_composite(vec![
8420 ("street".to_string(), "text".to_string()),
8421 ("city".to_string(), "text".to_string()),
8422 ]);
8423
8424 let (sql, values) = builder.build_create_type(&stmt);
8425 assert_eq!(
8426 sql,
8427 r#"CREATE TYPE "address" AS ("street" text, "city" text)"#
8428 );
8429 assert_eq!(values.len(), 0);
8430 }
8431
8432 #[test]
8433 fn test_create_type_domain_minimal() {
8434 let builder = PostgresQueryBuilder::new();
8435 let mut stmt = Query::create_type();
8436 stmt.name("positive_int").as_domain("integer".to_string());
8437
8438 let (sql, values) = builder.build_create_type(&stmt);
8439 assert_eq!(sql, r#"CREATE TYPE "positive_int" AS integer"#);
8440 assert_eq!(values.len(), 0);
8441 }
8442
8443 #[test]
8444 fn test_create_type_domain_with_constraint() {
8445 let builder = PostgresQueryBuilder::new();
8446 let mut stmt = Query::create_type();
8447 stmt.name("positive_int")
8448 .as_domain("integer".to_string())
8449 .constraint(
8450 "check_positive".to_string(),
8451 "CHECK (VALUE > 0)".to_string(),
8452 );
8453
8454 let (sql, values) = builder.build_create_type(&stmt);
8455 assert_eq!(
8456 sql,
8457 r#"CREATE TYPE "positive_int" AS integer CHECK (VALUE > 0)"#
8458 );
8459 assert_eq!(values.len(), 0);
8460 }
8461
8462 #[test]
8463 fn test_create_type_domain_with_default() {
8464 let builder = PostgresQueryBuilder::new();
8465 let mut stmt = Query::create_type();
8466 stmt.name("my_domain")
8467 .as_domain("integer".to_string())
8468 .default_value("0".to_string());
8469
8470 let (sql, values) = builder.build_create_type(&stmt);
8471 assert_eq!(sql, r#"CREATE TYPE "my_domain" AS integer DEFAULT 0"#);
8472 assert_eq!(values.len(), 0);
8473 }
8474
8475 #[test]
8476 fn test_create_type_domain_not_null() {
8477 let builder = PostgresQueryBuilder::new();
8478 let mut stmt = Query::create_type();
8479 stmt.name("my_domain")
8480 .as_domain("integer".to_string())
8481 .not_null();
8482
8483 let (sql, values) = builder.build_create_type(&stmt);
8484 assert_eq!(sql, r#"CREATE TYPE "my_domain" AS integer NOT NULL"#);
8485 assert_eq!(values.len(), 0);
8486 }
8487
8488 #[test]
8489 fn test_create_type_domain_full() {
8490 let builder = PostgresQueryBuilder::new();
8491 let mut stmt = Query::create_type();
8492 stmt.name("positive_int")
8493 .as_domain("integer".to_string())
8494 .default_value("1".to_string())
8495 .constraint(
8496 "check_positive".to_string(),
8497 "CHECK (VALUE > 0)".to_string(),
8498 )
8499 .not_null();
8500
8501 let (sql, values) = builder.build_create_type(&stmt);
8502 assert_eq!(
8503 sql,
8504 r#"CREATE TYPE "positive_int" AS integer DEFAULT 1 CHECK (VALUE > 0) NOT NULL"#
8505 );
8506 assert_eq!(values.len(), 0);
8507 }
8508
8509 #[test]
8510 fn test_create_type_range_minimal() {
8511 let builder = PostgresQueryBuilder::new();
8512 let mut stmt = Query::create_type();
8513 stmt.name("int_range").as_range("integer".to_string());
8514
8515 let (sql, values) = builder.build_create_type(&stmt);
8516 assert_eq!(
8517 sql,
8518 r#"CREATE TYPE "int_range" AS RANGE (SUBTYPE = integer)"#
8519 );
8520 assert_eq!(values.len(), 0);
8521 }
8522
8523 #[test]
8524 fn test_create_type_range_with_subtype_diff() {
8525 let builder = PostgresQueryBuilder::new();
8526 let mut stmt = Query::create_type();
8527 stmt.name("int_range")
8528 .as_range("integer".to_string())
8529 .subtype_diff("int4range_subdiff".to_string());
8530
8531 let (sql, values) = builder.build_create_type(&stmt);
8532 assert_eq!(
8533 sql,
8534 r#"CREATE TYPE "int_range" AS RANGE (SUBTYPE = integer, SUBTYPE_DIFF = int4range_subdiff)"#
8535 );
8536 assert_eq!(values.len(), 0);
8537 }
8538
8539 #[test]
8540 fn test_create_type_range_full() {
8541 let builder = PostgresQueryBuilder::new();
8542 let mut stmt = Query::create_type();
8543 stmt.name("int_range")
8544 .as_range("integer".to_string())
8545 .subtype_diff("int4range_subdiff".to_string())
8546 .canonical("int4range_canonical".to_string());
8547
8548 let (sql, values) = builder.build_create_type(&stmt);
8549 assert_eq!(
8550 sql,
8551 r#"CREATE TYPE "int_range" AS RANGE (SUBTYPE = integer, SUBTYPE_DIFF = int4range_subdiff, CANONICAL = int4range_canonical)"#
8552 );
8553 assert_eq!(values.len(), 0);
8554 }
8555
8556 #[test]
8558 fn test_alter_type_rename_to() {
8559 let builder = PostgresQueryBuilder::new();
8560 let mut stmt = Query::alter_type();
8561 stmt.name("old_name").rename_to("new_name");
8562
8563 let (sql, values) = builder.build_alter_type(&stmt);
8564 assert_eq!(sql, r#"ALTER TYPE "old_name" RENAME TO "new_name""#);
8565 assert_eq!(values.len(), 0);
8566 }
8567
8568 #[test]
8569 fn test_alter_type_owner_to() {
8570 let builder = PostgresQueryBuilder::new();
8571 let mut stmt = Query::alter_type();
8572 stmt.name("my_type").owner_to("new_owner");
8573
8574 let (sql, values) = builder.build_alter_type(&stmt);
8575 assert_eq!(sql, r#"ALTER TYPE "my_type" OWNER TO "new_owner""#);
8576 assert_eq!(values.len(), 0);
8577 }
8578
8579 #[test]
8580 fn test_alter_type_set_schema() {
8581 let builder = PostgresQueryBuilder::new();
8582 let mut stmt = Query::alter_type();
8583 stmt.name("my_type").set_schema("new_schema");
8584
8585 let (sql, values) = builder.build_alter_type(&stmt);
8586 assert_eq!(sql, r#"ALTER TYPE "my_type" SET SCHEMA "new_schema""#);
8587 assert_eq!(values.len(), 0);
8588 }
8589
8590 #[test]
8591 fn test_alter_type_add_value() {
8592 let builder = PostgresQueryBuilder::new();
8593 let mut stmt = Query::alter_type();
8594 stmt.name("mood").add_value("excited", None);
8595
8596 let (sql, values) = builder.build_alter_type(&stmt);
8597 assert_eq!(sql, r#"ALTER TYPE "mood" ADD VALUE 'excited'"#);
8598 assert_eq!(values.len(), 0);
8599 }
8600
8601 #[test]
8602 fn test_alter_type_add_value_before() {
8603 let builder = PostgresQueryBuilder::new();
8604 let mut stmt = Query::alter_type();
8605 stmt.name("mood").add_value("excited", Some("happy"));
8606
8607 let (sql, values) = builder.build_alter_type(&stmt);
8608 assert_eq!(
8609 sql,
8610 r#"ALTER TYPE "mood" ADD VALUE 'excited' BEFORE 'happy'"#
8611 );
8612 assert_eq!(values.len(), 0);
8613 }
8614
8615 #[test]
8616 fn test_alter_type_rename_value() {
8617 let builder = PostgresQueryBuilder::new();
8618 let mut stmt = Query::alter_type();
8619 stmt.name("mood").rename_value("happy", "joyful");
8620
8621 let (sql, values) = builder.build_alter_type(&stmt);
8622 assert_eq!(sql, r#"ALTER TYPE "mood" RENAME VALUE 'happy' TO 'joyful'"#);
8623 assert_eq!(values.len(), 0);
8624 }
8625
8626 #[test]
8627 fn test_alter_type_add_constraint() {
8628 let builder = PostgresQueryBuilder::new();
8629 let mut stmt = Query::alter_type();
8630 stmt.name("my_domain")
8631 .add_constraint("positive_check", "CHECK (VALUE > 0)");
8632
8633 let (sql, values) = builder.build_alter_type(&stmt);
8634 assert_eq!(
8635 sql,
8636 r#"ALTER TYPE "my_domain" ADD CONSTRAINT "positive_check" CHECK (VALUE > 0)"#
8637 );
8638 assert_eq!(values.len(), 0);
8639 }
8640
8641 #[test]
8642 fn test_alter_type_drop_constraint() {
8643 let builder = PostgresQueryBuilder::new();
8644 let mut stmt = Query::alter_type();
8645 stmt.name("my_domain")
8646 .drop_constraint("my_constraint", false);
8647
8648 let (sql, values) = builder.build_alter_type(&stmt);
8649 assert_eq!(
8650 sql,
8651 r#"ALTER TYPE "my_domain" DROP CONSTRAINT "my_constraint""#
8652 );
8653 assert_eq!(values.len(), 0);
8654 }
8655
8656 #[test]
8657 fn test_alter_type_drop_constraint_if_exists() {
8658 let builder = PostgresQueryBuilder::new();
8659 let mut stmt = Query::alter_type();
8660 stmt.name("my_domain")
8661 .drop_constraint("my_constraint", true);
8662
8663 let (sql, values) = builder.build_alter_type(&stmt);
8664 assert_eq!(
8665 sql,
8666 r#"ALTER TYPE "my_domain" DROP CONSTRAINT IF EXISTS "my_constraint""#
8667 );
8668 assert_eq!(values.len(), 0);
8669 }
8670
8671 #[test]
8672 fn test_alter_type_set_default() {
8673 let builder = PostgresQueryBuilder::new();
8674 let mut stmt = Query::alter_type();
8675 stmt.name("my_domain").set_default("0");
8676
8677 let (sql, values) = builder.build_alter_type(&stmt);
8678 assert_eq!(sql, r#"ALTER TYPE "my_domain" SET DEFAULT 0"#);
8679 assert_eq!(values.len(), 0);
8680 }
8681
8682 #[test]
8683 fn test_alter_type_drop_default() {
8684 let builder = PostgresQueryBuilder::new();
8685 let mut stmt = Query::alter_type();
8686 stmt.name("my_domain").drop_default();
8687
8688 let (sql, values) = builder.build_alter_type(&stmt);
8689 assert_eq!(sql, r#"ALTER TYPE "my_domain" DROP DEFAULT"#);
8690 assert_eq!(values.len(), 0);
8691 }
8692
8693 #[test]
8694 fn test_alter_type_set_not_null() {
8695 let builder = PostgresQueryBuilder::new();
8696 let mut stmt = Query::alter_type();
8697 stmt.name("my_domain").set_not_null();
8698
8699 let (sql, values) = builder.build_alter_type(&stmt);
8700 assert_eq!(sql, r#"ALTER TYPE "my_domain" SET NOT NULL"#);
8701 assert_eq!(values.len(), 0);
8702 }
8703
8704 #[test]
8705 fn test_alter_type_drop_not_null() {
8706 let builder = PostgresQueryBuilder::new();
8707 let mut stmt = Query::alter_type();
8708 stmt.name("my_domain").drop_not_null();
8709
8710 let (sql, values) = builder.build_alter_type(&stmt);
8711 assert_eq!(sql, r#"ALTER TYPE "my_domain" DROP NOT NULL"#);
8712 assert_eq!(values.len(), 0);
8713 }
8714
8715 #[test]
8717 fn test_drop_type_basic() {
8718 let builder = PostgresQueryBuilder::new();
8719 let mut stmt = Query::drop_type();
8720 stmt.name("my_type");
8721
8722 let (sql, values) = builder.build_drop_type(&stmt);
8723 assert_eq!(sql, r#"DROP TYPE "my_type""#);
8724 assert_eq!(values.len(), 0);
8725 }
8726
8727 #[test]
8728 fn test_drop_type_if_exists() {
8729 let builder = PostgresQueryBuilder::new();
8730 let mut stmt = Query::drop_type();
8731 stmt.name("my_type").if_exists();
8732
8733 let (sql, values) = builder.build_drop_type(&stmt);
8734 assert_eq!(sql, r#"DROP TYPE IF EXISTS "my_type""#);
8735 assert_eq!(values.len(), 0);
8736 }
8737
8738 #[test]
8739 fn test_drop_type_cascade() {
8740 let builder = PostgresQueryBuilder::new();
8741 let mut stmt = Query::drop_type();
8742 stmt.name("my_type").cascade();
8743
8744 let (sql, values) = builder.build_drop_type(&stmt);
8745 assert_eq!(sql, r#"DROP TYPE "my_type" CASCADE"#);
8746 assert_eq!(values.len(), 0);
8747 }
8748
8749 #[test]
8750 fn test_drop_type_restrict() {
8751 let builder = PostgresQueryBuilder::new();
8752 let mut stmt = Query::drop_type();
8753 stmt.name("my_type").restrict();
8754
8755 let (sql, values) = builder.build_drop_type(&stmt);
8756 assert_eq!(sql, r#"DROP TYPE "my_type" RESTRICT"#);
8757 assert_eq!(values.len(), 0);
8758 }
8759
8760 #[test]
8761 fn test_drop_type_all_options() {
8762 let builder = PostgresQueryBuilder::new();
8763 let mut stmt = Query::drop_type();
8764 stmt.name("my_type").if_exists().cascade();
8765
8766 let (sql, values) = builder.build_drop_type(&stmt);
8767 assert_eq!(sql, r#"DROP TYPE IF EXISTS "my_type" CASCADE"#);
8768 assert_eq!(values.len(), 0);
8769 }
8770
8771 #[test]
8773 #[should_panic(expected = "PostgreSQL users should use VACUUM ANALYZE")]
8774 fn test_optimize_table_panics() {
8775 let builder = PostgresQueryBuilder::new();
8776 let mut stmt = Query::optimize_table();
8777 stmt.table("users");
8778
8779 let _ = builder.build_optimize_table(&stmt);
8780 }
8781
8782 #[test]
8783 #[should_panic(expected = "not supported in PostgreSQL")]
8784 fn test_repair_table_panics() {
8785 let builder = PostgresQueryBuilder::new();
8786 let mut stmt = Query::repair_table();
8787 stmt.table("users");
8788
8789 let _ = builder.build_repair_table(&stmt);
8790 }
8791
8792 #[test]
8793 #[should_panic(expected = "not supported in PostgreSQL")]
8794 fn test_check_table_panics() {
8795 let builder = PostgresQueryBuilder::new();
8796 let mut stmt = Query::check_table();
8797 stmt.table("users");
8798
8799 let _ = builder.build_check_table(&stmt);
8800 }
8801
8802 #[test]
8805 fn test_grant_single_privilege_on_table() {
8806 use crate::dcl::{GrantStatement, Privilege};
8807
8808 let builder = PostgresQueryBuilder::new();
8809 let stmt = GrantStatement::new()
8810 .privilege(Privilege::Select)
8811 .on_table("users")
8812 .to("app_user");
8813
8814 let (sql, values) = builder.build_grant(&stmt);
8815 assert_eq!(sql, r#"GRANT SELECT ON TABLE "users" TO "app_user""#);
8816 assert!(values.is_empty());
8817 }
8818
8819 #[test]
8820 fn test_grant_multiple_privileges() {
8821 use crate::dcl::{GrantStatement, Privilege};
8822
8823 let builder = PostgresQueryBuilder::new();
8824 let stmt = GrantStatement::new()
8825 .privileges(vec![
8826 Privilege::Select,
8827 Privilege::Insert,
8828 Privilege::Update,
8829 ])
8830 .on_table("users")
8831 .to("app_user");
8832
8833 let (sql, values) = builder.build_grant(&stmt);
8834 assert_eq!(
8835 sql,
8836 r#"GRANT SELECT, INSERT, UPDATE ON TABLE "users" TO "app_user""#
8837 );
8838 assert!(values.is_empty());
8839 }
8840
8841 #[test]
8842 fn test_grant_multiple_objects() {
8843 use crate::dcl::{GrantStatement, ObjectType, Privilege};
8844
8845 let builder = PostgresQueryBuilder::new();
8846 let stmt = GrantStatement::new()
8847 .privilege(Privilege::Select)
8848 .object_type(ObjectType::Table)
8849 .object("users")
8850 .object("posts")
8851 .to("app_user");
8852
8853 let (sql, values) = builder.build_grant(&stmt);
8854 assert_eq!(
8855 sql,
8856 r#"GRANT SELECT ON TABLE "users", "posts" TO "app_user""#
8857 );
8858 assert!(values.is_empty());
8859 }
8860
8861 #[test]
8862 fn test_grant_multiple_grantees() {
8863 use crate::dcl::{GrantStatement, Grantee, Privilege};
8864
8865 let builder = PostgresQueryBuilder::new();
8866 let stmt = GrantStatement::new()
8867 .privilege(Privilege::Select)
8868 .on_table("users")
8869 .grantee(Grantee::role("app_user"))
8870 .grantee(Grantee::role("readonly_user"));
8871
8872 let (sql, values) = builder.build_grant(&stmt);
8873 assert_eq!(
8874 sql,
8875 r#"GRANT SELECT ON TABLE "users" TO "app_user", "readonly_user""#
8876 );
8877 assert!(values.is_empty());
8878 }
8879
8880 #[test]
8881 fn test_grant_with_grant_option() {
8882 use crate::dcl::{GrantStatement, Privilege};
8883
8884 let builder = PostgresQueryBuilder::new();
8885 let stmt = GrantStatement::new()
8886 .privilege(Privilege::Select)
8887 .on_table("users")
8888 .to("app_user")
8889 .with_grant_option(true);
8890
8891 let (sql, values) = builder.build_grant(&stmt);
8892 assert_eq!(
8893 sql,
8894 r#"GRANT SELECT ON TABLE "users" TO "app_user" WITH GRANT OPTION"#
8895 );
8896 assert!(values.is_empty());
8897 }
8898
8899 #[test]
8900 fn test_grant_with_granted_by() {
8901 use crate::dcl::{GrantStatement, Grantee, Privilege};
8902
8903 let builder = PostgresQueryBuilder::new();
8904 let stmt = GrantStatement::new()
8905 .privilege(Privilege::Select)
8906 .on_table("users")
8907 .to("app_user")
8908 .granted_by(Grantee::role("admin"));
8909
8910 let (sql, values) = builder.build_grant(&stmt);
8911 assert_eq!(
8912 sql,
8913 r#"GRANT SELECT ON TABLE "users" TO "app_user" GRANTED BY "admin""#
8914 );
8915 assert!(values.is_empty());
8916 }
8917
8918 #[test]
8919 fn test_grant_on_database() {
8920 use crate::dcl::{GrantStatement, Privilege};
8921
8922 let builder = PostgresQueryBuilder::new();
8923 let stmt = GrantStatement::new()
8924 .privilege(Privilege::Create)
8925 .on_database("mydb")
8926 .to("app_user");
8927
8928 let (sql, values) = builder.build_grant(&stmt);
8929 assert_eq!(sql, r#"GRANT CREATE ON DATABASE "mydb" TO "app_user""#);
8930 assert!(values.is_empty());
8931 }
8932
8933 #[test]
8934 fn test_grant_on_schema() {
8935 use crate::dcl::{GrantStatement, Privilege};
8936
8937 let builder = PostgresQueryBuilder::new();
8938 let stmt = GrantStatement::new()
8939 .privilege(Privilege::Usage)
8940 .on_schema("public")
8941 .to("app_user");
8942
8943 let (sql, values) = builder.build_grant(&stmt);
8944 assert_eq!(sql, r#"GRANT USAGE ON SCHEMA "public" TO "app_user""#);
8945 assert!(values.is_empty());
8946 }
8947
8948 #[test]
8949 fn test_grant_on_sequence() {
8950 use crate::dcl::{GrantStatement, Privilege};
8951
8952 let builder = PostgresQueryBuilder::new();
8953 let stmt = GrantStatement::new()
8954 .privilege(Privilege::Usage)
8955 .on_sequence("user_id_seq")
8956 .to("app_user");
8957
8958 let (sql, values) = builder.build_grant(&stmt);
8959 assert_eq!(
8960 sql,
8961 r#"GRANT USAGE ON SEQUENCE "user_id_seq" TO "app_user""#
8962 );
8963 assert!(values.is_empty());
8964 }
8965
8966 #[test]
8967 fn test_grant_all_privileges() {
8968 use crate::dcl::{GrantStatement, Privilege};
8969
8970 let builder = PostgresQueryBuilder::new();
8971 let stmt = GrantStatement::new()
8972 .privilege(Privilege::All)
8973 .on_table("users")
8974 .to("admin");
8975
8976 let (sql, values) = builder.build_grant(&stmt);
8977 assert_eq!(sql, r#"GRANT ALL PRIVILEGES ON TABLE "users" TO "admin""#);
8978 assert!(values.is_empty());
8979 }
8980
8981 #[test]
8982 fn test_grant_to_public() {
8983 use crate::dcl::{GrantStatement, Grantee, Privilege};
8984
8985 let builder = PostgresQueryBuilder::new();
8986 let stmt = GrantStatement::new()
8987 .privilege(Privilege::Select)
8988 .on_table("public_data")
8989 .grantee(Grantee::Public);
8990
8991 let (sql, values) = builder.build_grant(&stmt);
8992 assert_eq!(sql, r#"GRANT SELECT ON TABLE "public_data" TO PUBLIC"#);
8993 assert!(values.is_empty());
8994 }
8995
8996 #[test]
8997 fn test_grant_to_current_user() {
8998 use crate::dcl::{GrantStatement, Grantee, Privilege};
8999
9000 let builder = PostgresQueryBuilder::new();
9001 let stmt = GrantStatement::new()
9002 .privilege(Privilege::Select)
9003 .on_table("users")
9004 .grantee(Grantee::CurrentUser);
9005
9006 let (sql, values) = builder.build_grant(&stmt);
9007 assert_eq!(sql, r#"GRANT SELECT ON TABLE "users" TO CURRENT_USER"#);
9008 assert!(values.is_empty());
9009 }
9010
9011 #[test]
9012 fn test_grant_complex() {
9013 use crate::dcl::{GrantStatement, Grantee, Privilege};
9014
9015 let builder = PostgresQueryBuilder::new();
9016 let stmt = GrantStatement::new()
9017 .privileges(vec![
9018 Privilege::Select,
9019 Privilege::Insert,
9020 Privilege::Update,
9021 ])
9022 .on_table("users")
9023 .on_table("posts")
9024 .grantee(Grantee::role("app_user"))
9025 .grantee(Grantee::role("readonly_user"))
9026 .with_grant_option(true)
9027 .granted_by(Grantee::role("admin"));
9028
9029 let (sql, values) = builder.build_grant(&stmt);
9030 assert!(sql.starts_with("GRANT SELECT, INSERT, UPDATE ON TABLE"));
9031 assert!(sql.contains(r#""users", "posts""#));
9032 assert!(sql.contains(r#"TO "app_user", "readonly_user""#));
9033 assert!(sql.contains("WITH GRANT OPTION"));
9034 assert!(sql.contains(r#"GRANTED BY "admin""#));
9035 assert!(values.is_empty());
9036 }
9037
9038 #[test]
9041 fn test_revoke_single_privilege() {
9042 use crate::dcl::{Privilege, RevokeStatement};
9043
9044 let builder = PostgresQueryBuilder::new();
9045 let stmt = RevokeStatement::new()
9046 .privilege(Privilege::Insert)
9047 .from_table("users")
9048 .from("app_user");
9049
9050 let (sql, values) = builder.build_revoke(&stmt);
9051 assert_eq!(sql, r#"REVOKE INSERT ON TABLE "users" FROM "app_user""#);
9052 assert!(values.is_empty());
9053 }
9054
9055 #[test]
9056 fn test_revoke_multiple_privileges() {
9057 use crate::dcl::{Privilege, RevokeStatement};
9058
9059 let builder = PostgresQueryBuilder::new();
9060 let stmt = RevokeStatement::new()
9061 .privileges(vec![
9062 Privilege::Select,
9063 Privilege::Insert,
9064 Privilege::Update,
9065 ])
9066 .from_table("users")
9067 .from("app_user");
9068
9069 let (sql, values) = builder.build_revoke(&stmt);
9070 assert_eq!(
9071 sql,
9072 r#"REVOKE SELECT, INSERT, UPDATE ON TABLE "users" FROM "app_user""#
9073 );
9074 assert!(values.is_empty());
9075 }
9076
9077 #[test]
9078 fn test_revoke_with_cascade() {
9079 use crate::dcl::{Privilege, RevokeStatement};
9080
9081 let builder = PostgresQueryBuilder::new();
9082 let stmt = RevokeStatement::new()
9083 .privilege(Privilege::All)
9084 .from_table("users")
9085 .from("app_user")
9086 .cascade(true);
9087
9088 let (sql, values) = builder.build_revoke(&stmt);
9089 assert_eq!(
9090 sql,
9091 r#"REVOKE ALL PRIVILEGES ON TABLE "users" FROM "app_user" CASCADE"#
9092 );
9093 assert!(values.is_empty());
9094 }
9095
9096 #[test]
9097 fn test_revoke_grant_option_for() {
9098 use crate::dcl::{Privilege, RevokeStatement};
9099
9100 let builder = PostgresQueryBuilder::new();
9101 let stmt = RevokeStatement::new()
9102 .privilege(Privilege::Select)
9103 .from_table("users")
9104 .from("app_user")
9105 .grant_option_for(true);
9106
9107 let (sql, values) = builder.build_revoke(&stmt);
9108 assert_eq!(
9109 sql,
9110 r#"REVOKE GRANT OPTION FOR SELECT ON TABLE "users" FROM "app_user""#
9111 );
9112 assert!(values.is_empty());
9113 }
9114
9115 #[test]
9116 fn test_revoke_from_database() {
9117 use crate::dcl::{Privilege, RevokeStatement};
9118
9119 let builder = PostgresQueryBuilder::new();
9120 let stmt = RevokeStatement::new()
9121 .privilege(Privilege::Create)
9122 .from_database("mydb")
9123 .from("app_user");
9124
9125 let (sql, values) = builder.build_revoke(&stmt);
9126 assert_eq!(sql, r#"REVOKE CREATE ON DATABASE "mydb" FROM "app_user""#);
9127 assert!(values.is_empty());
9128 }
9129
9130 #[test]
9131 fn test_revoke_from_schema() {
9132 use crate::dcl::{Privilege, RevokeStatement};
9133
9134 let builder = PostgresQueryBuilder::new();
9135 let stmt = RevokeStatement::new()
9136 .privilege(Privilege::Usage)
9137 .from_schema("public")
9138 .from("app_user");
9139
9140 let (sql, values) = builder.build_revoke(&stmt);
9141 assert_eq!(sql, r#"REVOKE USAGE ON SCHEMA "public" FROM "app_user""#);
9142 assert!(values.is_empty());
9143 }
9144
9145 #[test]
9146 fn test_revoke_from_sequence() {
9147 use crate::dcl::{Privilege, RevokeStatement};
9148
9149 let builder = PostgresQueryBuilder::new();
9150 let stmt = RevokeStatement::new()
9151 .privilege(Privilege::Usage)
9152 .from_sequence("user_id_seq")
9153 .from("app_user");
9154
9155 let (sql, values) = builder.build_revoke(&stmt);
9156 assert_eq!(
9157 sql,
9158 r#"REVOKE USAGE ON SEQUENCE "user_id_seq" FROM "app_user""#
9159 );
9160 assert!(values.is_empty());
9161 }
9162
9163 #[test]
9164 fn test_revoke_from_public() {
9165 use crate::dcl::{Grantee, Privilege, RevokeStatement};
9166
9167 let builder = PostgresQueryBuilder::new();
9168 let stmt = RevokeStatement::new()
9169 .privilege(Privilege::Select)
9170 .from_table("public_data")
9171 .grantee(Grantee::Public);
9172
9173 let (sql, values) = builder.build_revoke(&stmt);
9174 assert_eq!(sql, r#"REVOKE SELECT ON TABLE "public_data" FROM PUBLIC"#);
9175 assert!(values.is_empty());
9176 }
9177
9178 #[test]
9179 fn test_revoke_from_current_user() {
9180 use crate::dcl::{Grantee, Privilege, RevokeStatement};
9181
9182 let builder = PostgresQueryBuilder::new();
9183 let stmt = RevokeStatement::new()
9184 .privilege(Privilege::Select)
9185 .from_table("users")
9186 .grantee(Grantee::CurrentUser);
9187
9188 let (sql, values) = builder.build_revoke(&stmt);
9189 assert_eq!(sql, r#"REVOKE SELECT ON TABLE "users" FROM CURRENT_USER"#);
9190 assert!(values.is_empty());
9191 }
9192
9193 #[test]
9194 fn test_revoke_complex() {
9195 use crate::dcl::{Grantee, Privilege, RevokeStatement};
9196
9197 let builder = PostgresQueryBuilder::new();
9198 let stmt = RevokeStatement::new()
9199 .privileges(vec![Privilege::Select, Privilege::Insert])
9200 .from_table("users")
9201 .from_table("posts")
9202 .grantee(Grantee::role("app_user"))
9203 .grantee(Grantee::role("readonly_user"))
9204 .cascade(true);
9205
9206 let (sql, values) = builder.build_revoke(&stmt);
9207 assert!(sql.starts_with("REVOKE SELECT, INSERT ON TABLE"));
9208 assert!(sql.contains(r#""users", "posts""#));
9209 assert!(sql.contains(r#"FROM "app_user", "readonly_user""#));
9210 assert!(sql.contains("CASCADE"));
9211 assert!(values.is_empty());
9212 }
9213
9214 #[test]
9215 fn test_create_role_simple() {
9216 use crate::dcl::CreateRoleStatement;
9217
9218 let builder = PostgresQueryBuilder::new();
9219 let stmt = CreateRoleStatement::new().role("developer");
9220
9221 let (sql, values) = builder.build_create_role(&stmt);
9222 assert_eq!(sql, r#"CREATE ROLE "developer""#);
9223 assert!(values.is_empty());
9224 }
9225
9226 #[test]
9227 fn test_create_role_with_login() {
9228 use crate::dcl::{CreateRoleStatement, RoleAttribute};
9229 use crate::value::Value;
9230
9231 let builder = PostgresQueryBuilder::new();
9232 let stmt = CreateRoleStatement::new()
9233 .role("app_user")
9234 .attribute(RoleAttribute::Login)
9235 .attribute(RoleAttribute::Password("secret".to_string()));
9236
9237 let (sql, values) = builder.build_create_role(&stmt);
9238 assert_eq!(sql, r#"CREATE ROLE "app_user" WITH LOGIN PASSWORD $1"#);
9239 assert_eq!(values.len(), 1);
9240 assert_eq!(
9241 values[0],
9242 Value::String(Some(Box::new("secret".to_string())))
9243 );
9244 }
9245
9246 #[test]
9247 fn test_create_role_with_multiple_attributes() {
9248 use crate::dcl::{CreateRoleStatement, RoleAttribute};
9249
9250 let builder = PostgresQueryBuilder::new();
9251 let stmt = CreateRoleStatement::new()
9252 .role("superuser")
9253 .attribute(RoleAttribute::SuperUser)
9254 .attribute(RoleAttribute::CreateDb)
9255 .attribute(RoleAttribute::CreateRole)
9256 .attribute(RoleAttribute::ConnectionLimit(10));
9257
9258 let (sql, values) = builder.build_create_role(&stmt);
9259 assert_eq!(
9260 sql,
9261 r#"CREATE ROLE "superuser" WITH SUPERUSER CREATEDB CREATEROLE CONNECTION LIMIT 10"#
9262 );
9263 assert!(values.is_empty());
9264 }
9265
9266 #[test]
9267 fn test_drop_role_simple() {
9268 use crate::dcl::DropRoleStatement;
9269
9270 let builder = PostgresQueryBuilder::new();
9271 let stmt = DropRoleStatement::new().role("old_role");
9272
9273 let (sql, values) = builder.build_drop_role(&stmt);
9274 assert_eq!(sql, r#"DROP ROLE "old_role""#);
9275 assert!(values.is_empty());
9276 }
9277
9278 #[test]
9279 fn test_drop_role_if_exists() {
9280 use crate::dcl::DropRoleStatement;
9281
9282 let builder = PostgresQueryBuilder::new();
9283 let stmt = DropRoleStatement::new().role("old_role").if_exists(true);
9284
9285 let (sql, values) = builder.build_drop_role(&stmt);
9286 assert_eq!(sql, r#"DROP ROLE IF EXISTS "old_role""#);
9287 assert!(values.is_empty());
9288 }
9289
9290 #[test]
9291 fn test_drop_role_multiple() {
9292 use crate::dcl::DropRoleStatement;
9293
9294 let builder = PostgresQueryBuilder::new();
9295 let stmt = DropRoleStatement::new()
9296 .role("role1")
9297 .role("role2")
9298 .role("role3");
9299
9300 let (sql, values) = builder.build_drop_role(&stmt);
9301 assert_eq!(sql, r#"DROP ROLE "role1", "role2", "role3""#);
9302 assert!(values.is_empty());
9303 }
9304
9305 #[test]
9306 fn test_alter_role_with_attributes() {
9307 use crate::dcl::{AlterRoleStatement, RoleAttribute};
9308
9309 let builder = PostgresQueryBuilder::new();
9310 let stmt = AlterRoleStatement::new()
9311 .role("developer")
9312 .attribute(RoleAttribute::NoLogin)
9313 .attribute(RoleAttribute::ConnectionLimit(5));
9314
9315 let (sql, values) = builder.build_alter_role(&stmt);
9316 assert_eq!(
9317 sql,
9318 r#"ALTER ROLE "developer" WITH NOLOGIN CONNECTION LIMIT 5"#
9319 );
9320 assert!(values.is_empty());
9321 }
9322
9323 #[test]
9324 fn test_alter_role_rename_to() {
9325 use crate::dcl::AlterRoleStatement;
9326
9327 let builder = PostgresQueryBuilder::new();
9328 let stmt = AlterRoleStatement::new()
9329 .role("old_name")
9330 .rename_to("new_name");
9331
9332 let (sql, values) = builder.build_alter_role(&stmt);
9333 assert_eq!(sql, r#"ALTER ROLE "old_name" RENAME TO "new_name""#);
9334 assert!(values.is_empty());
9335 }
9336
9337 #[test]
9339 fn test_create_user_basic() {
9340 use crate::dcl::CreateUserStatement;
9341
9342 let builder = PostgresQueryBuilder::new();
9343 let stmt = CreateUserStatement::new().user("app_user");
9344
9345 let (sql, values) = builder.build_create_user(&stmt);
9346 assert_eq!(sql, r#"CREATE ROLE "app_user" WITH LOGIN"#);
9347 assert!(values.is_empty());
9348 }
9349
9350 #[test]
9351 fn test_create_user_with_password() {
9352 use crate::dcl::{CreateUserStatement, RoleAttribute};
9353 use crate::value::Value;
9354
9355 let builder = PostgresQueryBuilder::new();
9356 let stmt = CreateUserStatement::new()
9357 .user("app_user")
9358 .attribute(RoleAttribute::Password("secret".to_string()));
9359
9360 let (sql, values) = builder.build_create_user(&stmt);
9361 assert_eq!(sql, r#"CREATE ROLE "app_user" WITH LOGIN PASSWORD $1"#);
9362 assert_eq!(values.len(), 1);
9363 assert_eq!(
9364 values[0],
9365 Value::String(Some(Box::new("secret".to_string())))
9366 );
9367 }
9368
9369 #[test]
9371 fn test_drop_user_basic() {
9372 use crate::dcl::DropUserStatement;
9373
9374 let builder = PostgresQueryBuilder::new();
9375 let stmt = DropUserStatement::new().user("app_user");
9376
9377 let (sql, values) = builder.build_drop_user(&stmt);
9378 assert_eq!(sql, r#"DROP ROLE "app_user""#);
9379 assert!(values.is_empty());
9380 }
9381
9382 #[test]
9383 fn test_drop_user_if_exists() {
9384 use crate::dcl::DropUserStatement;
9385
9386 let builder = PostgresQueryBuilder::new();
9387 let stmt = DropUserStatement::new().user("app_user").if_exists(true);
9388
9389 let (sql, values) = builder.build_drop_user(&stmt);
9390 assert_eq!(sql, r#"DROP ROLE IF EXISTS "app_user""#);
9391 assert!(values.is_empty());
9392 }
9393
9394 #[test]
9396 fn test_alter_user_basic() {
9397 use crate::dcl::{AlterUserStatement, RoleAttribute};
9398 use crate::value::Value;
9399
9400 let builder = PostgresQueryBuilder::new();
9401 let stmt = AlterUserStatement::new()
9402 .user("app_user")
9403 .attribute(RoleAttribute::Password("new_secret".to_string()));
9404
9405 let (sql, values) = builder.build_alter_user(&stmt);
9406 assert_eq!(sql, r#"ALTER ROLE "app_user" WITH PASSWORD $1"#);
9407 assert_eq!(values.len(), 1);
9408 assert_eq!(
9409 values[0],
9410 Value::String(Some(Box::new("new_secret".to_string())))
9411 );
9412 }
9413
9414 #[test]
9416 #[should_panic(expected = "RENAME USER is not supported by PostgreSQL")]
9417 fn test_rename_user_panics() {
9418 use crate::dcl::RenameUserStatement;
9419
9420 let builder = PostgresQueryBuilder::new();
9421 let stmt = RenameUserStatement::new().rename("old", "new");
9422
9423 builder.build_rename_user(&stmt);
9424 }
9425
9426 #[test]
9428 fn test_set_role_named() {
9429 use crate::dcl::{RoleTarget, SetRoleStatement};
9430
9431 let builder = PostgresQueryBuilder::new();
9432 let stmt = SetRoleStatement::new().role(RoleTarget::Named("admin".to_string()));
9433
9434 let (sql, values) = builder.build_set_role(&stmt);
9435 assert_eq!(sql, r#"SET ROLE "admin""#);
9436 assert!(values.is_empty());
9437 }
9438
9439 #[test]
9440 fn test_set_role_none() {
9441 use crate::dcl::{RoleTarget, SetRoleStatement};
9442
9443 let builder = PostgresQueryBuilder::new();
9444 let stmt = SetRoleStatement::new().role(RoleTarget::None);
9445
9446 let (sql, values) = builder.build_set_role(&stmt);
9447 assert_eq!(sql, "SET ROLE NONE");
9448 assert!(values.is_empty());
9449 }
9450
9451 #[test]
9452 #[should_panic(expected = "SET ROLE ALL is not supported by PostgreSQL")]
9453 fn test_set_role_all_panics() {
9454 use crate::dcl::{RoleTarget, SetRoleStatement};
9455
9456 let builder = PostgresQueryBuilder::new();
9457 let stmt = SetRoleStatement::new().role(RoleTarget::All);
9458
9459 builder.build_set_role(&stmt);
9460 }
9461
9462 #[test]
9464 fn test_reset_role() {
9465 use crate::dcl::ResetRoleStatement;
9466
9467 let builder = PostgresQueryBuilder::new();
9468 let stmt = ResetRoleStatement::new();
9469
9470 let (sql, values) = builder.build_reset_role(&stmt);
9471 assert_eq!(sql, "RESET ROLE");
9472 assert!(values.is_empty());
9473 }
9474
9475 #[test]
9477 #[should_panic(expected = "SET DEFAULT ROLE is not supported by PostgreSQL")]
9478 fn test_set_default_role_panics() {
9479 use crate::dcl::{DefaultRoleSpec, SetDefaultRoleStatement};
9480
9481 let builder = PostgresQueryBuilder::new();
9482 let stmt = SetDefaultRoleStatement::new()
9483 .roles(DefaultRoleSpec::All)
9484 .user("app_user");
9485
9486 builder.build_set_default_role(&stmt);
9487 }
9488
9489 #[rstest]
9492 fn test_as_enum_escapes_type_name_with_special_characters() {
9493 let builder = PostgresQueryBuilder::new();
9495 let mut stmt = Query::select();
9496 stmt.expr(Expr::val("active").as_enum(Alias::new("user\"status")))
9497 .from("users");
9498
9499 let (sql, _) = builder.build_select(&stmt);
9501
9502 assert!(sql.contains("::\"user\"\"status\""));
9504 }
9505
9506 #[rstest]
9507 fn test_cast_escapes_type_name_with_special_characters() {
9508 let builder = PostgresQueryBuilder::new();
9510 let mut stmt = Query::select();
9511 stmt.expr(Expr::col("age").cast_as(Alias::new("my\"type")))
9512 .from("users");
9513
9514 let (sql, _) = builder.build_select(&stmt);
9516
9517 assert!(sql.contains("CAST(\"age\" AS \"my\"\"type\")"));
9519 }
9520
9521 #[rstest]
9522 fn test_trigger_function_name_is_escaped() {
9523 let builder = PostgresQueryBuilder::new();
9525 let mut stmt = Query::create_trigger();
9526 stmt.name("test_trigger")
9527 .timing(crate::types::TriggerTiming::After)
9528 .event(crate::types::TriggerEvent::Insert)
9529 .on_table("users")
9530 .for_each(crate::types::TriggerScope::Row)
9531 .execute_function("my\"func");
9532
9533 let (sql, _) = builder.build_create_trigger(&stmt);
9535
9536 assert!(sql.contains("EXECUTE FUNCTION \"my\"\"func\"()"));
9538 }
9539
9540 #[rstest]
9541 fn test_as_enum_normal_type_name_is_quoted() {
9542 let builder = PostgresQueryBuilder::new();
9544 let mut stmt = Query::select();
9545 stmt.expr(Expr::val("active").as_enum(Alias::new("status")))
9546 .from("users");
9547
9548 let (sql, _) = builder.build_select(&stmt);
9550
9551 assert!(sql.contains("::\"status\""));
9553 }
9554
9555 #[rstest]
9556 fn test_cast_normal_type_name_is_quoted() {
9557 let builder = PostgresQueryBuilder::new();
9559 let mut stmt = Query::select();
9560 stmt.expr(Expr::col("age").cast_as(Alias::new("INTEGER")))
9561 .from("users");
9562
9563 let (sql, _) = builder.build_select(&stmt);
9565
9566 assert!(sql.contains("CAST(\"age\" AS \"INTEGER\")"));
9568 }
9569
9570 #[rstest]
9573 fn test_safe_delimiter_default_when_body_has_no_dollar_quotes() {
9574 let body = "BEGIN RETURN 1; END;";
9576
9577 let delimiter = generate_safe_dollar_quote_delimiter(body);
9579
9580 assert_eq!(delimiter, "$$");
9582 }
9583
9584 #[rstest]
9585 fn test_safe_delimiter_avoids_collision_with_dollar_dollar() {
9586 let body = "BEGIN $$ nested $$ END;";
9588
9589 let delimiter = generate_safe_dollar_quote_delimiter(body);
9591
9592 assert_ne!(
9594 delimiter, "$$",
9595 "Delimiter must not be $$ when body contains $$"
9596 );
9597 assert_eq!(delimiter, "$body_0$");
9598 }
9599
9600 #[rstest]
9601 fn test_safe_delimiter_injection_attempt_with_dollar_quotes() {
9602 let body = "$$ ; DROP TABLE users; --";
9604
9605 let delimiter = generate_safe_dollar_quote_delimiter(body);
9607
9608 assert_ne!(delimiter, "$$");
9610 let delimiters = collect_dollar_quote_delimiters(body);
9611 assert!(
9612 !delimiters.contains(&delimiter),
9613 "Generated delimiter must not conflict with any delimiter in body"
9614 );
9615 }
9616
9617 #[rstest]
9618 fn test_safe_delimiter_skips_collision_with_body_0() {
9619 let body = "BEGIN $$ test $body_0$ END;";
9621
9622 let delimiter = generate_safe_dollar_quote_delimiter(body);
9624
9625 assert_eq!(delimiter, "$body_1$");
9627 }
9628
9629 #[rstest]
9630 fn test_safe_delimiter_multiple_collisions() {
9631 let body = "$$ $body_0$ $body_1$";
9633
9634 let delimiter = generate_safe_dollar_quote_delimiter(body);
9636
9637 assert_eq!(delimiter, "$body_2$");
9639 }
9640
9641 #[rstest]
9642 fn test_safe_delimiter_ignores_dollar_amount_not_delimiter() {
9643 let body = "SELECT $100 + $200";
9645
9646 let delimiter = generate_safe_dollar_quote_delimiter(body);
9648
9649 assert_eq!(delimiter, "$$");
9651 }
9652
9653 #[rstest]
9654 fn test_safe_delimiter_empty_body() {
9655 let body = "";
9657
9658 let delimiter = generate_safe_dollar_quote_delimiter(body);
9660
9661 assert_eq!(delimiter, "$$");
9663 }
9664
9665 #[rstest]
9666 fn test_safe_delimiter_whitespace_only_body() {
9667 let body = " \t\n ";
9669
9670 let delimiter = generate_safe_dollar_quote_delimiter(body);
9672
9673 assert_eq!(delimiter, "$$");
9675 }
9676
9677 #[rstest]
9678 fn test_safe_delimiter_nested_dollar_quotes() {
9679 let body = "$inner$ SELECT 1 $inner$ $$ nested $$";
9681
9682 let delimiter = generate_safe_dollar_quote_delimiter(body);
9684
9685 assert_ne!(delimiter, "$$");
9687 assert_ne!(delimiter, "$inner$");
9688 assert_eq!(delimiter, "$body_0$");
9689 }
9690
9691 #[rstest]
9692 fn test_safe_delimiter_tag_style_delimiters() {
9693 let body = "$func$ BEGIN RETURN 1; END; $func$";
9695
9696 let delimiter = generate_safe_dollar_quote_delimiter(body);
9698
9699 assert_eq!(delimiter, "$$");
9701 }
9702
9703 #[rstest]
9706 fn test_collect_delimiters_empty_body() {
9707 let body = "";
9709
9710 let delimiters = collect_dollar_quote_delimiters(body);
9712
9713 assert!(delimiters.is_empty());
9715 }
9716
9717 #[rstest]
9718 fn test_collect_delimiters_no_dollar_signs() {
9719 let body = "SELECT 1 + 2";
9721
9722 let delimiters = collect_dollar_quote_delimiters(body);
9724
9725 assert!(delimiters.is_empty());
9727 }
9728
9729 #[rstest]
9730 fn test_collect_delimiters_dollar_amounts_are_not_delimiters() {
9731 let body = "SELECT $1 + $2";
9733
9734 let delimiters = collect_dollar_quote_delimiters(body);
9736
9737 assert!(delimiters.is_empty());
9739 }
9740
9741 #[rstest]
9742 fn test_collect_delimiters_finds_empty_tag() {
9743 let body = "$$ body content $$";
9745
9746 let delimiters = collect_dollar_quote_delimiters(body);
9748
9749 assert_eq!(delimiters.len(), 1);
9751 assert!(delimiters.contains("$$"));
9752 }
9753
9754 #[rstest]
9755 fn test_collect_delimiters_finds_named_tag() {
9756 let body = "$func$ body $func$";
9758
9759 let delimiters = collect_dollar_quote_delimiters(body);
9761
9762 assert_eq!(delimiters.len(), 1);
9764 assert!(delimiters.contains("$func$"));
9765 }
9766
9767 #[rstest]
9768 fn test_collect_delimiters_finds_multiple_tags() {
9769 let body = "$$ outer $inner$ nested $inner$ outer $$";
9771
9772 let delimiters = collect_dollar_quote_delimiters(body);
9774
9775 assert_eq!(delimiters.len(), 2);
9777 assert!(delimiters.contains("$$"));
9778 assert!(delimiters.contains("$inner$"));
9779 }
9780
9781 #[rstest]
9782 fn test_collect_delimiters_underscore_in_tag() {
9783 let body = "$my_tag$ content $my_tag$";
9785
9786 let delimiters = collect_dollar_quote_delimiters(body);
9788
9789 assert!(delimiters.contains("$my_tag$"));
9791 }
9792
9793 #[rstest]
9794 fn test_collect_delimiters_rejects_digit_start_tag() {
9795 let body = "$1tag$ content";
9797
9798 let delimiters = collect_dollar_quote_delimiters(body);
9800
9801 assert!(!delimiters.contains("$1tag$"));
9803 }
9804
9805 #[rstest]
9810 fn test_adjust_placeholder_offsets_basic() {
9811 let sql = "SELECT * FROM t WHERE a = $1 AND b = $2";
9813
9814 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 2, 3);
9816
9817 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $4 AND b = $5");
9819 }
9820
9821 #[rstest]
9822 fn test_adjust_placeholder_offsets_zero_offset() {
9823 let sql = "SELECT * FROM t WHERE a = $1";
9825
9826 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 1, 0);
9828
9829 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $1");
9831 }
9832
9833 #[rstest]
9834 fn test_adjust_placeholder_offsets_skips_quoted_identifiers() {
9835 let sql = r#"SELECT "col$1" FROM t WHERE a = $1"#;
9837
9838 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 1, 5);
9840
9841 assert_eq!(adjusted, r#"SELECT "col$1" FROM t WHERE a = $6"#);
9843 }
9844
9845 #[rstest]
9846 fn test_adjust_placeholder_offsets_skips_string_literals() {
9847 let sql = "SELECT * FROM t WHERE a = $1 AND b = 'price$1'";
9849
9850 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 1, 2);
9852
9853 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $3 AND b = 'price$1'");
9855 }
9856
9857 #[rstest]
9858 fn test_adjust_placeholder_offsets_no_collision() {
9859 let sql = "SELECT * FROM t WHERE a = $1 AND b = $2";
9861
9862 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 2, 1);
9864
9865 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $2 AND b = $3");
9867 }
9868
9869 #[rstest]
9874 fn test_expr_as_renders_as_alias() {
9875 let builder = PostgresQueryBuilder::new();
9877 let mut stmt = Query::select();
9878 stmt.expr(Expr::col("name").expr_as("display_name"))
9879 .from("users");
9880
9881 let (sql, _) = builder.build_select(&stmt);
9883
9884 assert!(
9886 sql.contains("AS \"display_name\""),
9887 "Expected AS alias in SQL, got: {}",
9888 sql
9889 );
9890 assert!(
9891 !sql.contains("::\"display_name\""),
9892 "Should NOT contain type cast syntax, got: {}",
9893 sql
9894 );
9895 }
9896}