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 {
3361 writer.push_space();
3362 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3363 }
3364
3365 if let Some(owner) = &stmt.owner {
3367 writer.push_keyword("OWNER");
3368 writer.push_space();
3369 writer.push_identifier(&Iden::to_string(owner.as_ref()), |s| self.escape_iden(s));
3370 }
3371
3372 if let Some(template) = &stmt.template {
3374 writer.push_keyword("TEMPLATE");
3375 writer.push_space();
3376 writer.push_identifier(&Iden::to_string(template.as_ref()), |s| self.escape_iden(s));
3377 }
3378
3379 if let Some(encoding) = &stmt.encoding {
3381 writer.push_keyword("ENCODING");
3382 writer.push_space();
3383 let escaped = encoding.replace('\'', "''");
3384 writer.push(&format!("'{}'", escaped));
3385 }
3386
3387 if let Some(lc_collate) = &stmt.lc_collate {
3389 writer.push_keyword("LC_COLLATE");
3390 writer.push_space();
3391 let escaped = lc_collate.replace('\'', "''");
3392 writer.push(&format!("'{}'", escaped));
3393 }
3394
3395 if let Some(lc_ctype) = &stmt.lc_ctype {
3397 writer.push_keyword("LC_CTYPE");
3398 writer.push_space();
3399 let escaped = lc_ctype.replace('\'', "''");
3400 writer.push(&format!("'{}'", escaped));
3401 }
3402
3403 writer.finish()
3404 }
3405
3406 fn build_alter_database(
3407 &self,
3408 stmt: &crate::query::AlterDatabaseStatement,
3409 ) -> (String, Values) {
3410 use crate::types::{DatabaseOperation, Iden};
3411
3412 let mut writer = SqlWriter::new();
3413
3414 writer.push_keyword("ALTER DATABASE");
3416
3417 if let Some(name) = &stmt.database_name {
3419 writer.push_space();
3420 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3421 }
3422
3423 for (i, operation) in stmt.operations.iter().enumerate() {
3425 if i == 0 {
3426 writer.push_space();
3427 } else {
3428 writer.push(", ");
3429 }
3430 match operation {
3431 DatabaseOperation::RenameDatabase(new_name) => {
3432 writer.push_keyword("RENAME TO");
3433 writer.push_space();
3434 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
3435 self.escape_iden(s)
3436 });
3437 }
3438 DatabaseOperation::OwnerTo(new_owner) => {
3439 writer.push_keyword("OWNER TO");
3440 writer.push_space();
3441 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
3442 self.escape_iden(s)
3443 });
3444 }
3445 DatabaseOperation::AddRegion(region) => {
3447 writer.push_keyword("ADD REGION");
3449 writer.push_space();
3450 let escaped = region.replace('\'', "''");
3451 writer.push(&format!("'{}'", escaped));
3452 }
3453 DatabaseOperation::DropRegion(region) => {
3454 writer.push_keyword("DROP REGION");
3456 writer.push_space();
3457 let escaped = region.replace('\'', "''");
3458 writer.push(&format!("'{}'", escaped));
3459 }
3460 DatabaseOperation::SetPrimaryRegion(region) => {
3461 writer.push_keyword("PRIMARY REGION");
3463 writer.push_space();
3464 let escaped = region.replace('\'', "''");
3465 writer.push(&format!("'{}'", escaped));
3466 }
3467 DatabaseOperation::ConfigureZone(zone_config) => {
3468 writer.push_keyword("CONFIGURE ZONE USING");
3470 writer.push_space();
3471
3472 let mut parts = Vec::new();
3473
3474 if let Some(num_replicas) = zone_config.num_replicas {
3475 parts.push(format!("num_replicas = {}", num_replicas));
3476 }
3477
3478 if !zone_config.constraints.is_empty() {
3479 let constraints: Vec<String> = zone_config
3480 .constraints
3481 .iter()
3482 .map(ToString::to_string)
3483 .collect();
3484 parts.push(format!("constraints = '[{}]'", constraints.join(", ")));
3485 }
3486
3487 if !zone_config.lease_preferences.is_empty() {
3488 let prefs: Vec<String> = zone_config
3489 .lease_preferences
3490 .iter()
3491 .map(|p| format!("[{}]", p))
3492 .collect();
3493 parts.push(format!("lease_preferences = '[{}]'", prefs.join(", ")));
3494 }
3495
3496 writer.push(&parts.join(", "));
3497 }
3498 }
3499 }
3500
3501 writer.finish()
3502 }
3503
3504 fn build_drop_database(&self, stmt: &crate::query::DropDatabaseStatement) -> (String, Values) {
3505 use crate::types::Iden;
3506
3507 let mut writer = SqlWriter::new();
3508
3509 writer.push_keyword("DROP DATABASE");
3511
3512 if stmt.if_exists {
3514 writer.push_keyword("IF EXISTS");
3515 }
3516
3517 if let Some(name) = &stmt.database_name {
3519 writer.push_space();
3520 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3521 }
3522
3523 if stmt.force {
3525 writer.push_space();
3526 writer.push_keyword("WITH");
3527 writer.push(" (");
3528 writer.push("FORCE");
3529 writer.push(")");
3530 }
3531
3532 writer.finish()
3533 }
3534
3535 fn build_analyze(&self, stmt: &crate::query::AnalyzeStatement) -> (String, Values) {
3536 use crate::types::Iden;
3537 let mut writer = SqlWriter::new();
3538
3539 writer.push_keyword("ANALYZE");
3540
3541 if stmt.verbose {
3542 writer.push_keyword("VERBOSE");
3543 }
3544
3545 if !stmt.tables.is_empty() {
3547 writer.push_space();
3548 writer.push_list(&stmt.tables, ", ", |w, table| {
3549 w.push_identifier(&Iden::to_string(table.table.as_ref()), |s| {
3550 self.escape_iden(s)
3551 });
3552 if !table.columns.is_empty() {
3553 w.push(" (");
3554 w.push_list(&table.columns, ", ", |w2, col| {
3555 w2.push_identifier(&Iden::to_string(col.as_ref()), |s| self.escape_iden(s));
3556 });
3557 w.push(")");
3558 }
3559 });
3560 }
3561
3562 writer.finish()
3563 }
3564
3565 fn build_vacuum(&self, stmt: &crate::query::VacuumStatement) -> (String, Values) {
3566 use crate::types::Iden;
3567 let mut writer = SqlWriter::new();
3568
3569 writer.push_keyword("VACUUM");
3570
3571 if stmt.full {
3573 writer.push_keyword("FULL");
3574 }
3575 if stmt.freeze {
3576 writer.push_keyword("FREEZE");
3577 }
3578 if stmt.verbose {
3579 writer.push_keyword("VERBOSE");
3580 }
3581 if stmt.analyze {
3582 writer.push_keyword("ANALYZE");
3583 }
3584
3585 if !stmt.tables.is_empty() {
3587 writer.push_space();
3588 writer.push_list(&stmt.tables, ", ", |w, table| {
3589 w.push_identifier(&Iden::to_string(table.as_ref()), |s| self.escape_iden(s));
3590 });
3591 }
3592
3593 writer.finish()
3594 }
3595
3596 fn build_create_materialized_view(
3597 &self,
3598 stmt: &crate::query::CreateMaterializedViewStatement,
3599 ) -> (String, Values) {
3600 use crate::types::Iden;
3601 let mut writer = SqlWriter::new();
3602
3603 writer.push_keyword("CREATE MATERIALIZED VIEW");
3604
3605 if stmt.def.if_not_exists {
3607 writer.push_keyword("IF NOT EXISTS");
3608 }
3609
3610 writer.push_space();
3612 writer.push_identifier(&Iden::to_string(stmt.def.name.as_ref()), |s| {
3613 self.escape_iden(s)
3614 });
3615
3616 if !stmt.def.columns.is_empty() {
3618 writer.push_space();
3619 writer.push("(");
3620 writer.push_list(&stmt.def.columns, ", ", |w, col| {
3621 w.push_identifier(&Iden::to_string(col.as_ref()), |s| self.escape_iden(s));
3622 });
3623 writer.push(")");
3624 }
3625
3626 if let Some(ref tablespace) = stmt.def.tablespace {
3628 writer.push_keyword("TABLESPACE");
3629 writer.push_space();
3630 writer.push_identifier(&Iden::to_string(tablespace.as_ref()), |s| {
3631 self.escape_iden(s)
3632 });
3633 }
3634
3635 if let Some(ref select) = stmt.select {
3637 writer.push_keyword("AS");
3638 writer.push_space();
3639 let (select_sql, select_values) = self.build_select(select);
3640 writer.push(&select_sql);
3641
3642 if let Some(with_data) = stmt.def.with_data {
3644 writer.push_space();
3645 if with_data {
3646 writer.push_keyword("WITH DATA");
3647 } else {
3648 writer.push_keyword("WITH NO DATA");
3649 }
3650 }
3651
3652 let (sql, _) = writer.finish();
3653 return (sql, select_values);
3654 }
3655
3656 writer.finish()
3657 }
3658
3659 fn build_alter_materialized_view(
3660 &self,
3661 stmt: &crate::query::AlterMaterializedViewStatement,
3662 ) -> (String, Values) {
3663 use crate::types::{Iden, MaterializedViewOperation};
3664 let mut writer = SqlWriter::new();
3665
3666 writer.push_keyword("ALTER MATERIALIZED VIEW");
3667
3668 if let Some(ref name) = stmt.name {
3670 writer.push_space();
3671 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3672 }
3673
3674 for operation in &stmt.operations {
3676 writer.push_space();
3677 match operation {
3678 MaterializedViewOperation::Rename(new_name) => {
3679 writer.push_keyword("RENAME TO");
3680 writer.push_space();
3681 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
3682 self.escape_iden(s)
3683 });
3684 }
3685 MaterializedViewOperation::OwnerTo(new_owner) => {
3686 writer.push_keyword("OWNER TO");
3687 writer.push_space();
3688 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
3689 self.escape_iden(s)
3690 });
3691 }
3692 MaterializedViewOperation::SetSchema(schema_name) => {
3693 writer.push_keyword("SET SCHEMA");
3694 writer.push_space();
3695 writer.push_identifier(&Iden::to_string(schema_name.as_ref()), |s| {
3696 self.escape_iden(s)
3697 });
3698 }
3699 }
3700 }
3701
3702 writer.finish()
3703 }
3704
3705 fn build_drop_materialized_view(
3706 &self,
3707 stmt: &crate::query::DropMaterializedViewStatement,
3708 ) -> (String, Values) {
3709 use crate::types::Iden;
3710 let mut writer = SqlWriter::new();
3711
3712 writer.push_keyword("DROP MATERIALIZED VIEW");
3713
3714 if stmt.if_exists {
3716 writer.push_keyword("IF EXISTS");
3717 }
3718
3719 writer.push_space();
3721 writer.push_list(&stmt.names, ", ", |w, name| {
3722 w.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3723 });
3724
3725 if stmt.cascade {
3727 writer.push_keyword("CASCADE");
3728 } else if stmt.restrict {
3729 writer.push_keyword("RESTRICT");
3730 }
3731
3732 writer.finish()
3733 }
3734
3735 fn build_refresh_materialized_view(
3736 &self,
3737 stmt: &crate::query::RefreshMaterializedViewStatement,
3738 ) -> (String, Values) {
3739 use crate::types::Iden;
3740 let mut writer = SqlWriter::new();
3741
3742 writer.push_keyword("REFRESH MATERIALIZED VIEW");
3743
3744 if stmt.concurrently {
3746 writer.push_keyword("CONCURRENTLY");
3747 }
3748
3749 if let Some(ref name) = stmt.name {
3751 writer.push_space();
3752 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3753 }
3754
3755 if let Some(with_data) = stmt.with_data {
3757 writer.push_space();
3758 if with_data {
3759 writer.push_keyword("WITH DATA");
3760 } else {
3761 writer.push_keyword("WITH NO DATA");
3762 }
3763 }
3764
3765 writer.finish()
3766 }
3767
3768 fn build_create_procedure(
3769 &self,
3770 stmt: &crate::query::CreateProcedureStatement,
3771 ) -> (String, Values) {
3772 use crate::types::{
3773 Iden,
3774 function::{FunctionBehavior, FunctionLanguage, FunctionSecurity},
3775 };
3776
3777 let mut writer = SqlWriter::new();
3778
3779 writer.push_keyword("CREATE");
3781 if stmt.procedure_def.or_replace {
3782 writer.push_keyword("OR REPLACE");
3783 }
3784 writer.push_keyword("PROCEDURE");
3785
3786 writer.push_space();
3788 writer.push_identifier(&Iden::to_string(stmt.procedure_def.name.as_ref()), |s| {
3789 self.escape_iden(s)
3790 });
3791
3792 writer.push("(");
3794 let mut first = true;
3795 for param in &stmt.procedure_def.parameters {
3796 if !first {
3797 writer.push(", ");
3798 }
3799 first = false;
3800
3801 if let Some(mode) = ¶m.mode {
3803 use crate::types::function::ParameterMode;
3804 match mode {
3805 ParameterMode::In => writer.push("IN "),
3806 ParameterMode::Out => writer.push("OUT "),
3807 ParameterMode::InOut => writer.push("INOUT "),
3808 ParameterMode::Variadic => writer.push("VARIADIC "),
3809 }
3810 }
3811
3812 if let Some(name) = ¶m.name {
3814 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3815 writer.push(" ");
3816 }
3817
3818 if let Some(param_type) = ¶m.param_type {
3820 writer.push(param_type);
3821 }
3822
3823 if let Some(default) = ¶m.default_value {
3825 writer.push(" DEFAULT ");
3826 writer.push(default);
3827 }
3828 }
3829 writer.push(")");
3830
3831 if let Some(language) = &stmt.procedure_def.language {
3833 writer.push_keyword("LANGUAGE");
3834 writer.push_space();
3835 match language {
3836 FunctionLanguage::Sql => writer.push("SQL"),
3837 FunctionLanguage::PlPgSql => writer.push("PLPGSQL"),
3838 FunctionLanguage::C => writer.push("C"),
3839 FunctionLanguage::Custom(lang) => writer.push(lang),
3840 }
3841 }
3842
3843 if let Some(behavior) = &stmt.procedure_def.behavior {
3845 writer.push_space();
3846 match behavior {
3847 FunctionBehavior::Immutable => writer.push_keyword("IMMUTABLE"),
3848 FunctionBehavior::Stable => writer.push_keyword("STABLE"),
3849 FunctionBehavior::Volatile => writer.push_keyword("VOLATILE"),
3850 }
3851 }
3852
3853 if let Some(security) = &stmt.procedure_def.security {
3855 writer.push_space();
3856 match security {
3857 FunctionSecurity::Definer => writer.push_keyword("SECURITY DEFINER"),
3858 FunctionSecurity::Invoker => writer.push_keyword("SECURITY INVOKER"),
3859 }
3860 }
3861
3862 if let Some(body) = &stmt.procedure_def.body {
3864 writer.push_keyword("AS");
3865 writer.push_space();
3866 let delimiter = generate_safe_dollar_quote_delimiter(body);
3867 writer.push(&delimiter);
3868 writer.push(body);
3869 writer.push(&delimiter);
3870 }
3871
3872 writer.finish()
3873 }
3874
3875 fn build_alter_procedure(
3876 &self,
3877 stmt: &crate::query::AlterProcedureStatement,
3878 ) -> (String, Values) {
3879 use crate::types::{
3880 Iden,
3881 function::{FunctionBehavior, FunctionSecurity},
3882 procedure::ProcedureOperation,
3883 };
3884
3885 let mut writer = SqlWriter::new();
3886
3887 writer.push_keyword("ALTER PROCEDURE");
3889
3890 if let Some(name) = &stmt.name {
3892 writer.push_space();
3893 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3894 }
3895
3896 if !stmt.parameters.is_empty() {
3898 writer.push("(");
3899 let mut first = true;
3900 for param in &stmt.parameters {
3901 if !first {
3902 writer.push(", ");
3903 }
3904 first = false;
3905
3906 if let Some(name) = ¶m.name {
3908 let name_str = Iden::to_string(name.as_ref());
3909 if !name_str.is_empty() {
3910 writer.push_identifier(&name_str, |s| self.escape_iden(s));
3911 writer.push(" ");
3912 }
3913 }
3914
3915 if let Some(param_type) = ¶m.param_type {
3917 writer.push(param_type);
3918 }
3919 }
3920 writer.push(")");
3921 }
3922
3923 if let Some(operation) = &stmt.operation {
3925 writer.push_space();
3926 match operation {
3927 ProcedureOperation::RenameTo(new_name) => {
3928 writer.push_keyword("RENAME TO");
3929 writer.push_space();
3930 writer.push_identifier(&Iden::to_string(new_name.as_ref()), |s| {
3931 self.escape_iden(s)
3932 });
3933 }
3934 ProcedureOperation::OwnerTo(new_owner) => {
3935 writer.push_keyword("OWNER TO");
3936 writer.push_space();
3937 writer.push_identifier(&Iden::to_string(new_owner.as_ref()), |s| {
3938 self.escape_iden(s)
3939 });
3940 }
3941 ProcedureOperation::SetSchema(new_schema) => {
3942 writer.push_keyword("SET SCHEMA");
3943 writer.push_space();
3944 writer.push_identifier(&Iden::to_string(new_schema.as_ref()), |s| {
3945 self.escape_iden(s)
3946 });
3947 }
3948 ProcedureOperation::SetBehavior(behavior) => match behavior {
3949 FunctionBehavior::Immutable => writer.push_keyword("IMMUTABLE"),
3950 FunctionBehavior::Stable => writer.push_keyword("STABLE"),
3951 FunctionBehavior::Volatile => writer.push_keyword("VOLATILE"),
3952 },
3953 ProcedureOperation::SetSecurity(security) => match security {
3954 FunctionSecurity::Definer => writer.push_keyword("SECURITY DEFINER"),
3955 FunctionSecurity::Invoker => writer.push_keyword("SECURITY INVOKER"),
3956 },
3957 }
3958 }
3959
3960 writer.finish()
3961 }
3962
3963 fn build_drop_procedure(
3964 &self,
3965 stmt: &crate::query::DropProcedureStatement,
3966 ) -> (String, Values) {
3967 use crate::types::Iden;
3968
3969 let mut writer = SqlWriter::new();
3970
3971 writer.push_keyword("DROP PROCEDURE");
3973
3974 if stmt.if_exists {
3976 writer.push_keyword("IF EXISTS");
3977 }
3978
3979 if let Some(name) = &stmt.name {
3981 writer.push_space();
3982 writer.push_identifier(&Iden::to_string(name.as_ref()), |s| self.escape_iden(s));
3983 }
3984
3985 if !stmt.parameters.is_empty() {
3987 writer.push("(");
3988 let mut first = true;
3989 for param in &stmt.parameters {
3990 if !first {
3991 writer.push(", ");
3992 }
3993 first = false;
3994
3995 if let Some(name) = ¶m.name {
3997 let name_str = Iden::to_string(name.as_ref());
3998 if !name_str.is_empty() {
3999 writer.push_identifier(&name_str, |s| self.escape_iden(s));
4000 writer.push(" ");
4001 }
4002 }
4003
4004 if let Some(param_type) = ¶m.param_type {
4006 writer.push(param_type);
4007 }
4008 }
4009 writer.push(")");
4010 }
4011
4012 if stmt.cascade {
4014 writer.push_keyword("CASCADE");
4015 }
4016
4017 writer.finish()
4018 }
4019 fn build_create_type(&self, stmt: &crate::query::CreateTypeStatement) -> (String, Values) {
4021 let mut writer = SqlWriter::new();
4022
4023 writer.push_keyword("CREATE TYPE");
4024 writer.push_space();
4025
4026 if let Some(name) = &stmt.name {
4028 writer.push_identifier(&name.to_string(), |s| self.escape_iden(s));
4029 }
4030
4031 if let Some(kind) = &stmt.kind {
4033 use crate::types::type_def::TypeKind;
4034 match kind {
4035 TypeKind::Enum { values } => {
4036 writer.push_space();
4037 writer.push_keyword("AS ENUM");
4038 writer.push_space();
4039 writer.push("(");
4040 writer.push_list(values, ", ", |w, value| {
4041 w.push("'");
4042 w.push(&value.replace('\'', "''"));
4043 w.push("'");
4044 });
4045 writer.push(")");
4046 }
4047 TypeKind::Composite { attributes } => {
4048 writer.push_space();
4049 writer.push_keyword("AS");
4050 writer.push_space();
4051 writer.push("(");
4052 writer.push_list(attributes, ", ", |w, (name, type_name)| {
4053 w.push_identifier(name, |s| self.escape_iden(s));
4054 w.push_space();
4055 w.push(type_name);
4056 });
4057 writer.push(")");
4058 }
4059 TypeKind::Domain {
4060 base_type,
4061 constraint,
4062 default,
4063 not_null,
4064 } => {
4065 writer.push_space();
4066 writer.push_keyword("AS");
4067 writer.push_space();
4068 writer.push(base_type);
4069
4070 if let Some(default_val) = default {
4072 writer.push_space();
4073 writer.push_keyword("DEFAULT");
4074 writer.push_space();
4075 writer.push(default_val);
4076 }
4077
4078 if let Some(check) = constraint {
4080 writer.push_space();
4081 writer.push(check);
4082 }
4083
4084 if *not_null {
4086 writer.push_space();
4087 writer.push_keyword("NOT NULL");
4088 }
4089 }
4090 TypeKind::Range {
4091 subtype,
4092 subtype_diff,
4093 canonical,
4094 } => {
4095 writer.push_space();
4096 writer.push_keyword("AS RANGE");
4097 writer.push_space();
4098 writer.push("(");
4099 writer.push("SUBTYPE = ");
4100 writer.push(subtype);
4101
4102 if let Some(diff_fn) = subtype_diff {
4104 writer.push(", SUBTYPE_DIFF = ");
4105 writer.push(diff_fn);
4106 }
4107
4108 if let Some(canonical_fn) = canonical {
4110 writer.push(", CANONICAL = ");
4111 writer.push(canonical_fn);
4112 }
4113
4114 writer.push(")");
4115 }
4116 }
4117 }
4118
4119 writer.finish()
4120 }
4121
4122 fn build_alter_type(&self, stmt: &crate::query::AlterTypeStatement) -> (String, Values) {
4123 let mut writer = SqlWriter::new();
4124
4125 writer.push_keyword("ALTER TYPE");
4126 writer.push_space();
4127 writer.push_identifier(&stmt.name.to_string(), |s| self.escape_iden(s));
4128
4129 for operation in &stmt.operations {
4131 writer.push_space();
4132 use crate::types::type_def::TypeOperation;
4133 match operation {
4134 TypeOperation::RenameTo(new_name) => {
4135 writer.push_keyword("RENAME TO");
4136 writer.push_space();
4137 writer.push_identifier(&new_name.to_string(), |s| self.escape_iden(s));
4138 }
4139 TypeOperation::OwnerTo(owner) => {
4140 writer.push_keyword("OWNER TO");
4141 writer.push_space();
4142 writer.push_identifier(&owner.to_string(), |s| self.escape_iden(s));
4143 }
4144 TypeOperation::SetSchema(schema) => {
4145 writer.push_keyword("SET SCHEMA");
4146 writer.push_space();
4147 writer.push_identifier(&schema.to_string(), |s| self.escape_iden(s));
4148 }
4149 TypeOperation::AddValue(value, position) => {
4150 writer.push_keyword("ADD VALUE");
4151 writer.push_space();
4152 writer.push("'");
4153 writer.push(&value.replace('\'', "''"));
4154 writer.push("'");
4155
4156 if let Some(pos) = position {
4157 writer.push_space();
4158 writer.push_keyword("BEFORE");
4159 writer.push_space();
4160 writer.push("'");
4161 writer.push(&pos.replace('\'', "''"));
4162 writer.push("'");
4163 }
4164 }
4165 TypeOperation::RenameValue(old_value, new_value) => {
4166 writer.push_keyword("RENAME VALUE");
4167 writer.push_space();
4168 writer.push("'");
4169 writer.push(&old_value.replace('\'', "''"));
4170 writer.push("'");
4171 writer.push_space();
4172 writer.push_keyword("TO");
4173 writer.push_space();
4174 writer.push("'");
4175 writer.push(&new_value.replace('\'', "''"));
4176 writer.push("'");
4177 }
4178 TypeOperation::AddConstraint(name, check) => {
4179 writer.push_keyword("ADD CONSTRAINT");
4180 writer.push_space();
4181 writer.push_identifier(name, |s| self.escape_iden(s));
4182 writer.push_space();
4183 writer.push(check);
4184 }
4185 TypeOperation::DropConstraint(name, if_exists) => {
4186 writer.push_keyword("DROP CONSTRAINT");
4187 if *if_exists {
4188 writer.push_space();
4189 writer.push_keyword("IF EXISTS");
4190 }
4191 writer.push_space();
4192 writer.push_identifier(name, |s| self.escape_iden(s));
4193 }
4194 TypeOperation::SetDefault(value) => {
4195 writer.push_keyword("SET DEFAULT");
4196 writer.push_space();
4197 writer.push(value);
4198 }
4199 TypeOperation::DropDefault => {
4200 writer.push_keyword("DROP DEFAULT");
4201 }
4202 TypeOperation::SetNotNull => {
4203 writer.push_keyword("SET NOT NULL");
4204 }
4205 TypeOperation::DropNotNull => {
4206 writer.push_keyword("DROP NOT NULL");
4207 }
4208 }
4209 }
4210
4211 writer.finish()
4212 }
4213
4214 fn build_drop_type(&self, stmt: &crate::query::DropTypeStatement) -> (String, Values) {
4215 let mut writer = SqlWriter::new();
4216
4217 writer.push_keyword("DROP TYPE");
4218 writer.push_space();
4219
4220 if stmt.if_exists {
4222 writer.push_keyword("IF EXISTS");
4223 writer.push_space();
4224 }
4225
4226 writer.push_identifier(&stmt.name.to_string(), |s| self.escape_iden(s));
4228
4229 if stmt.cascade {
4231 writer.push_space();
4232 writer.push_keyword("CASCADE");
4233 } else if stmt.restrict {
4234 writer.push_space();
4235 writer.push_keyword("RESTRICT");
4236 }
4237
4238 writer.finish()
4239 }
4240}
4241
4242impl PostgresQueryBuilder {
4244 #[allow(clippy::only_used_in_recursion)]
4251 fn column_type_to_sql(&self, col_type: &crate::types::ColumnType) -> String {
4252 use crate::types::ColumnType;
4253 match col_type {
4254 ColumnType::Char(len) => format!("CHAR({})", len.unwrap_or(1)),
4255 ColumnType::String(len) => {
4256 if let Some(l) = len {
4257 format!("VARCHAR({})", l)
4258 } else {
4259 "VARCHAR".to_string()
4260 }
4261 }
4262 ColumnType::Text => "TEXT".to_string(),
4263 ColumnType::TinyInteger => "SMALLINT".to_string(),
4264 ColumnType::SmallInteger => "SMALLINT".to_string(),
4265 ColumnType::Integer => "INTEGER".to_string(),
4266 ColumnType::BigInteger => "BIGINT".to_string(),
4267 ColumnType::Float => "REAL".to_string(),
4268 ColumnType::Double => "DOUBLE PRECISION".to_string(),
4269 ColumnType::Decimal(precision) => {
4270 if let Some((p, s)) = precision {
4272 format!("NUMERIC({}, {})", p, s)
4273 } else {
4274 "NUMERIC".to_string()
4275 }
4276 }
4277 ColumnType::DateTime => "TIMESTAMP".to_string(),
4278 ColumnType::Timestamp => "TIMESTAMP".to_string(),
4279 ColumnType::TimestampWithTimeZone => "TIMESTAMP WITH TIME ZONE".to_string(),
4280 ColumnType::Time => "TIME".to_string(),
4281 ColumnType::Date => "DATE".to_string(),
4282 ColumnType::Binary(_len) => {
4283 "BYTEA".to_string()
4285 }
4286 ColumnType::VarBinary(_len) => {
4287 "BYTEA".to_string()
4289 }
4290 ColumnType::Blob => "BYTEA".to_string(),
4291 ColumnType::Boolean => "BOOLEAN".to_string(),
4292 ColumnType::Json => "JSON".to_string(),
4293 ColumnType::JsonBinary => "JSONB".to_string(),
4294 ColumnType::Uuid => "UUID".to_string(),
4295 ColumnType::Array(inner_type) => {
4296 format!("{}[]", self.column_type_to_sql(inner_type))
4297 }
4298 ColumnType::Custom(name) => name.clone(),
4299 }
4300 }
4301
4302 fn write_table_constraint(
4303 &self,
4304 writer: &mut SqlWriter,
4305 constraint: &crate::types::TableConstraint,
4306 ) {
4307 use crate::types::TableConstraint;
4308 match constraint {
4309 TableConstraint::PrimaryKey { name, columns } => {
4310 if let Some(n) = name {
4311 writer.push_keyword("CONSTRAINT");
4312 writer.push_space();
4313 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4314 writer.push_space();
4315 }
4316 writer.push_keyword("PRIMARY KEY");
4317 writer.push_space();
4318 writer.push("(");
4319 writer.push_list(columns, ", ", |w, col| {
4320 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4321 });
4322 writer.push(")");
4323 }
4324 TableConstraint::Unique { name, columns } => {
4325 if let Some(n) = name {
4326 writer.push_keyword("CONSTRAINT");
4327 writer.push_space();
4328 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4329 writer.push_space();
4330 }
4331 writer.push_keyword("UNIQUE");
4332 writer.push_space();
4333 writer.push("(");
4334 writer.push_list(columns, ", ", |w, col| {
4335 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4336 });
4337 writer.push(")");
4338 }
4339 TableConstraint::ForeignKey {
4340 name,
4341 columns,
4342 ref_table,
4343 ref_columns,
4344 on_delete,
4345 on_update,
4346 } => {
4347 if let Some(n) = name {
4348 writer.push_keyword("CONSTRAINT");
4349 writer.push_space();
4350 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4351 writer.push_space();
4352 }
4353 writer.push_keyword("FOREIGN KEY");
4354 writer.push_space();
4355 writer.push("(");
4356 writer.push_list(columns, ", ", |w, col| {
4357 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4358 });
4359 writer.push(")");
4360 writer.push_space();
4361 writer.push_keyword("REFERENCES");
4362 writer.push_space();
4363 self.write_table_ref(writer, ref_table);
4364 writer.push_space();
4365 writer.push("(");
4366 writer.push_list(ref_columns, ", ", |w, col| {
4367 w.push_identifier(&col.to_string(), |s| self.escape_iden(s));
4368 });
4369 writer.push(")");
4370 if let Some(action) = on_delete {
4371 writer.push_space();
4372 writer.push_keyword("ON DELETE");
4373 writer.push_space();
4374 writer.push_keyword(self.foreign_key_action_to_sql(action));
4375 }
4376 if let Some(action) = on_update {
4377 writer.push_space();
4378 writer.push_keyword("ON UPDATE");
4379 writer.push_space();
4380 writer.push_keyword(self.foreign_key_action_to_sql(action));
4381 }
4382 }
4383 TableConstraint::Check { name, expr } => {
4384 if let Some(n) = name {
4385 writer.push_keyword("CONSTRAINT");
4386 writer.push_space();
4387 writer.push_identifier(&n.to_string(), |s| self.escape_iden(s));
4388 writer.push_space();
4389 }
4390 writer.push_keyword("CHECK");
4391 writer.push_space();
4392 writer.push("(");
4393 self.write_simple_expr(writer, expr);
4394 writer.push(")");
4395 }
4396 }
4397 }
4398
4399 fn foreign_key_action_to_sql(&self, action: &crate::types::ForeignKeyAction) -> &'static str {
4400 use crate::types::ForeignKeyAction;
4401 match action {
4402 ForeignKeyAction::Restrict => "RESTRICT",
4403 ForeignKeyAction::Cascade => "CASCADE",
4404 ForeignKeyAction::SetNull => "SET NULL",
4405 ForeignKeyAction::SetDefault => "SET DEFAULT",
4406 ForeignKeyAction::NoAction => "NO ACTION",
4407 }
4408 }
4409
4410 fn index_method_to_sql(&self, method: &crate::query::IndexMethod) -> &'static str {
4411 use crate::query::IndexMethod;
4412 match method {
4413 IndexMethod::BTree => "BTREE",
4414 IndexMethod::Hash => "HASH",
4415 IndexMethod::Gist => "GIST",
4416 IndexMethod::Gin => "GIN",
4417 IndexMethod::Brin => "BRIN",
4418 IndexMethod::FullText => "GIN", IndexMethod::Spatial => "GIST", }
4421 }
4422}
4423
4424impl PostgresQueryBuilder {
4425 fn format_role_specification(spec: &crate::dcl::RoleSpecification) -> &str {
4435 use crate::dcl::RoleSpecification;
4436
4437 match spec {
4438 RoleSpecification::RoleName(name) => name,
4439 RoleSpecification::CurrentRole => "CURRENT_ROLE",
4440 RoleSpecification::CurrentUser => "CURRENT_USER",
4441 RoleSpecification::SessionUser => "SESSION_USER",
4442 }
4443 }
4444}
4445
4446fn collect_dollar_quote_delimiters(body: &str) -> std::collections::HashSet<String> {
4457 let mut delimiters = std::collections::HashSet::new();
4458 let bytes = body.as_bytes();
4459 let len = bytes.len();
4460 let mut i = 0;
4461
4462 while i < len {
4463 if bytes[i] == b'$' {
4464 let start = i;
4466 i += 1;
4467
4468 if i < len && bytes[i] == b'$' {
4470 delimiters.insert("$$".to_string());
4471 i += 1;
4472 continue;
4473 }
4474
4475 if i < len && (bytes[i].is_ascii_alphabetic() || bytes[i] == b'_') {
4477 let tag_start = i;
4478 i += 1;
4479 while i < len && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_') {
4480 i += 1;
4481 }
4482 if i < len && bytes[i] == b'$' {
4484 let delimiter = &body[start..=i];
4485 delimiters.insert(delimiter.to_string());
4486 i += 1;
4487 continue;
4488 }
4489 i = tag_start;
4491 continue;
4492 }
4493
4494 continue;
4496 }
4497 i += 1;
4498 }
4499
4500 delimiters
4501}
4502
4503fn generate_safe_dollar_quote_delimiter(body: &str) -> String {
4510 let existing = collect_dollar_quote_delimiters(body);
4511
4512 if !existing.contains("$$") {
4513 return "$$".to_string();
4514 }
4515
4516 for i in 0u64.. {
4518 let candidate = format!("$body_{}$", i);
4519 if !existing.contains(&candidate) {
4520 return candidate;
4521 }
4522 }
4523
4524 "$$".to_string()
4526}
4527
4528fn utf8_char_width(leading_byte: u8) -> usize {
4534 if leading_byte < 0x80 {
4535 1
4536 } else if leading_byte < 0xE0 {
4537 2
4538 } else if leading_byte < 0xF0 {
4539 3
4540 } else {
4541 4
4542 }
4543}
4544
4545impl crate::query::QueryBuilderTrait for PostgresQueryBuilder {
4546 fn placeholder(&self) -> (&str, bool) {
4547 ("$", true)
4548 }
4549
4550 fn quote_char(&self) -> char {
4551 '"'
4552 }
4553}
4554
4555#[cfg(test)]
4556mod tests {
4557 use super::*;
4558 use crate::{
4559 expr::{Expr, ExprTrait},
4560 query::Query,
4561 types::{Alias, IntoIden},
4562 };
4563 use rstest::rstest;
4564
4565 #[test]
4566 fn test_escape_identifier() {
4567 let builder = PostgresQueryBuilder::new();
4568 assert_eq!(builder.escape_identifier("user"), "\"user\"");
4569 assert_eq!(builder.escape_identifier("table_name"), "\"table_name\"");
4570 }
4571
4572 #[test]
4573 fn test_escape_identifier_with_quotes() {
4574 let builder = PostgresQueryBuilder::new();
4575 assert_eq!(builder.escape_identifier("user\"name"), "\"user\"\"name\"");
4576 }
4577
4578 #[test]
4579 fn test_format_placeholder() {
4580 let builder = PostgresQueryBuilder::new();
4581 assert_eq!(builder.format_placeholder(1), "$1");
4582 assert_eq!(builder.format_placeholder(2), "$2");
4583 assert_eq!(builder.format_placeholder(10), "$10");
4584 }
4585
4586 #[test]
4587 fn test_select_basic() {
4588 let builder = PostgresQueryBuilder::new();
4589 let mut stmt = Query::select();
4590 stmt.column("id").column("name").from("users");
4591
4592 let (sql, values) = builder.build_select(&stmt);
4593 assert_eq!(sql, "SELECT \"id\", \"name\" FROM \"users\"");
4594 assert_eq!(values.len(), 0);
4595 }
4596
4597 #[test]
4598 fn test_select_asterisk() {
4599 let builder = PostgresQueryBuilder::new();
4600 let mut stmt = Query::select();
4601 stmt.from("users");
4602
4603 let (sql, values) = builder.build_select(&stmt);
4604 assert_eq!(sql, "SELECT * FROM \"users\"");
4605 assert_eq!(values.len(), 0);
4606 }
4607
4608 #[test]
4609 fn test_select_with_where() {
4610 let builder = PostgresQueryBuilder::new();
4611 let mut stmt = Query::select();
4612 stmt.column("id")
4613 .from("users")
4614 .and_where(Expr::col("active").eq(true));
4615
4616 let (sql, _values) = builder.build_select(&stmt);
4617 assert!(sql.contains("SELECT"));
4619 assert!(sql.contains("FROM"));
4620 assert!(sql.contains("WHERE"));
4621 }
4622
4623 #[test]
4624 fn test_select_with_limit_offset() {
4625 let builder = PostgresQueryBuilder::new();
4626 let mut stmt = Query::select();
4627 stmt.column("id").from("users").limit(10).offset(20);
4628
4629 let (sql, values) = builder.build_select(&stmt);
4630 assert!(sql.contains("SELECT"));
4631 assert!(sql.contains("FROM"));
4632 assert!(sql.contains("LIMIT"));
4633 assert!(sql.contains("OFFSET"));
4634 assert_eq!(values.len(), 2);
4635 }
4636
4637 #[test]
4638 fn test_insert_basic() {
4639 let builder = PostgresQueryBuilder::new();
4640 let mut stmt = Query::insert();
4641 stmt.into_table("users")
4642 .columns(["name", "email"])
4643 .values_panic(["Alice", "alice@example.com"]);
4644
4645 let (sql, values) = builder.build_insert(&stmt);
4646 assert_eq!(
4647 sql,
4648 "INSERT INTO \"users\" (\"name\", \"email\") VALUES ($1, $2)"
4649 );
4650 assert_eq!(values.len(), 2);
4651 }
4652
4653 #[test]
4654 fn test_insert_multiple_rows() {
4655 let builder = PostgresQueryBuilder::new();
4656 let mut stmt = Query::insert();
4657 stmt.into_table("users")
4658 .columns(["name", "email"])
4659 .values_panic(["Alice", "alice@example.com"])
4660 .values_panic(["Bob", "bob@example.com"]);
4661
4662 let (sql, values) = builder.build_insert(&stmt);
4663 assert_eq!(
4664 sql,
4665 "INSERT INTO \"users\" (\"name\", \"email\") VALUES ($1, $2), ($3, $4)"
4666 );
4667 assert_eq!(values.len(), 4);
4668 }
4669
4670 #[test]
4671 fn test_insert_with_returning() {
4672 let builder = PostgresQueryBuilder::new();
4673 let mut stmt = Query::insert();
4674 stmt.into_table("users")
4675 .columns(["name"])
4676 .values_panic(["Alice"])
4677 .returning(["id", "created_at"]);
4678
4679 let (sql, values) = builder.build_insert(&stmt);
4680 assert!(sql.contains("INSERT INTO"));
4681 assert!(sql.contains("VALUES"));
4682 assert!(sql.contains("RETURNING"));
4683 assert!(sql.contains("\"id\""));
4684 assert!(sql.contains("\"created_at\""));
4685 assert_eq!(values.len(), 1);
4686 }
4687
4688 #[test]
4689 fn test_insert_with_returning_all() {
4690 let builder = PostgresQueryBuilder::new();
4691 let mut stmt = Query::insert();
4692 stmt.into_table("users")
4693 .columns(["name"])
4694 .values_panic(["Alice"])
4695 .returning_all();
4696
4697 let (sql, values) = builder.build_insert(&stmt);
4698 assert!(sql.contains("RETURNING *"));
4699 assert_eq!(values.len(), 1);
4700 }
4701
4702 #[test]
4703 fn test_insert_from_subquery() {
4704 let builder = PostgresQueryBuilder::new();
4705
4706 let select = Query::select()
4708 .column("name")
4709 .column("email")
4710 .from("temp_users")
4711 .to_owned();
4712
4713 let mut stmt = Query::insert();
4715 stmt.into_table("users")
4716 .columns(["name", "email"])
4717 .from_subquery(select);
4718
4719 let (sql, values) = builder.build_insert(&stmt);
4720 assert!(sql.contains("INSERT INTO \"users\""));
4721 assert!(sql.contains("\"name\", \"email\""));
4722 assert!(sql.contains("SELECT \"name\", \"email\" FROM \"temp_users\""));
4723 assert!(!sql.contains("VALUES"));
4724 assert_eq!(values.len(), 0);
4725 }
4726
4727 #[test]
4728 fn test_insert_from_subquery_with_where() {
4729 let builder = PostgresQueryBuilder::new();
4730
4731 let select = Query::select()
4733 .column("name")
4734 .column("email")
4735 .from("temp_users")
4736 .and_where(Expr::col("active").eq(true))
4737 .to_owned();
4738
4739 let mut stmt = Query::insert();
4741 stmt.into_table("users")
4742 .columns(["name", "email"])
4743 .from_subquery(select);
4744
4745 let (sql, values) = builder.build_insert(&stmt);
4746 assert!(sql.contains("INSERT INTO \"users\""));
4747 assert!(sql.contains("SELECT"));
4748 assert!(sql.contains("FROM \"temp_users\""));
4749 assert!(sql.contains("WHERE"));
4750 assert_eq!(values.len(), 1); }
4752
4753 #[test]
4754 fn test_update_basic() {
4755 let builder = PostgresQueryBuilder::new();
4756 let mut stmt = Query::update();
4757 stmt.table("users")
4758 .value("name", "Alice")
4759 .value("email", "alice@example.com");
4760
4761 let (sql, values) = builder.build_update(&stmt);
4762 assert_eq!(sql, "UPDATE \"users\" SET \"name\" = $1, \"email\" = $2");
4763 assert_eq!(values.len(), 2);
4764 }
4765
4766 #[test]
4767 fn test_update_with_where() {
4768 let builder = PostgresQueryBuilder::new();
4769 let mut stmt = Query::update();
4770 stmt.table("users")
4771 .value("active", false)
4772 .and_where(Expr::col("id").eq(1));
4773
4774 let (sql, values) = builder.build_update(&stmt);
4775 assert!(sql.contains("UPDATE"));
4776 assert!(sql.contains("SET"));
4777 assert!(sql.contains("WHERE"));
4778 assert_eq!(values.len(), 2); }
4780
4781 #[test]
4782 fn test_update_with_returning() {
4783 let builder = PostgresQueryBuilder::new();
4784 let mut stmt = Query::update();
4785 stmt.table("users")
4786 .value("active", false)
4787 .and_where(Expr::col("id").eq(1))
4788 .returning(["id", "updated_at"]);
4789
4790 let (sql, values) = builder.build_update(&stmt);
4791 assert!(sql.contains("UPDATE"));
4792 assert!(sql.contains("RETURNING"));
4793 assert!(sql.contains("\"id\""));
4794 assert!(sql.contains("\"updated_at\""));
4795 assert_eq!(values.len(), 2);
4796 }
4797
4798 #[test]
4799 fn test_delete_basic() {
4800 let builder = PostgresQueryBuilder::new();
4801 let mut stmt = Query::delete();
4802 stmt.from_table("users")
4803 .and_where(Expr::col("active").eq(false));
4804
4805 let (sql, values) = builder.build_delete(&stmt);
4806 assert!(sql.contains("DELETE FROM"));
4807 assert!(sql.contains("\"users\""));
4808 assert!(sql.contains("WHERE"));
4809 assert_eq!(values.len(), 1); }
4811
4812 #[test]
4813 fn test_delete_no_where() {
4814 let builder = PostgresQueryBuilder::new();
4815 let mut stmt = Query::delete();
4816 stmt.from_table("users");
4817
4818 let (sql, values) = builder.build_delete(&stmt);
4819 assert_eq!(sql, "DELETE FROM \"users\"");
4820 assert_eq!(values.len(), 0);
4821 }
4822
4823 #[test]
4824 fn test_delete_with_returning() {
4825 let builder = PostgresQueryBuilder::new();
4826 let mut stmt = Query::delete();
4827 stmt.from_table("users")
4828 .and_where(Expr::col("id").eq(1))
4829 .returning(["id", "name"]);
4830
4831 let (sql, values) = builder.build_delete(&stmt);
4832 assert!(sql.contains("DELETE FROM"));
4833 assert!(sql.contains("RETURNING"));
4834 assert!(sql.contains("\"id\""));
4835 assert!(sql.contains("\"name\""));
4836 assert_eq!(values.len(), 1);
4837 }
4838
4839 #[test]
4840 fn test_delete_with_returning_all() {
4841 let builder = PostgresQueryBuilder::new();
4842 let mut stmt = Query::delete();
4843 stmt.from_table("users")
4844 .and_where(Expr::col("id").eq(1))
4845 .returning_all();
4846
4847 let (sql, values) = builder.build_delete(&stmt);
4848 assert!(sql.contains("RETURNING *"));
4849 assert_eq!(values.len(), 1);
4850 }
4851
4852 #[test]
4855 fn test_inner_join_simple() {
4856 let builder = PostgresQueryBuilder::new();
4857 let mut stmt = Query::select();
4858 stmt.column("users.name")
4859 .column("orders.amount")
4860 .from("users")
4861 .inner_join(
4862 "orders",
4863 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4864 );
4865
4866 let (sql, _values) = builder.build_select(&stmt);
4867 assert!(sql.contains("FROM \"users\""));
4868 assert!(sql.contains("INNER JOIN \"orders\""));
4869 assert!(sql.contains("ON \"users\".\"id\" = \"orders\".\"user_id\""));
4870 }
4871
4872 #[test]
4873 fn test_left_join() {
4874 let builder = PostgresQueryBuilder::new();
4875 let mut stmt = Query::select();
4876 stmt.column("users.name")
4877 .column("profiles.bio")
4878 .from("users")
4879 .left_join(
4880 "profiles",
4881 Expr::col(("users", "id")).eq(Expr::col(("profiles", "user_id"))),
4882 );
4883
4884 let (sql, _values) = builder.build_select(&stmt);
4885 assert!(sql.contains("LEFT JOIN \"profiles\""));
4886 assert!(sql.contains("ON \"users\".\"id\" = \"profiles\".\"user_id\""));
4887 }
4888
4889 #[test]
4890 fn test_right_join() {
4891 let builder = PostgresQueryBuilder::new();
4892 let mut stmt = Query::select();
4893 stmt.column("users.name")
4894 .column("orders.amount")
4895 .from("users")
4896 .right_join(
4897 "orders",
4898 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4899 );
4900
4901 let (sql, _values) = builder.build_select(&stmt);
4902 assert!(sql.contains("RIGHT JOIN \"orders\""));
4903 assert!(sql.contains("ON \"users\".\"id\" = \"orders\".\"user_id\""));
4904 }
4905
4906 #[test]
4907 fn test_full_outer_join() {
4908 let builder = PostgresQueryBuilder::new();
4909 let mut stmt = Query::select();
4910 stmt.column("users.name")
4911 .column("orders.amount")
4912 .from("users")
4913 .full_outer_join(
4914 "orders",
4915 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4916 );
4917
4918 let (sql, _values) = builder.build_select(&stmt);
4919 assert!(sql.contains("FULL OUTER JOIN \"orders\""));
4920 assert!(sql.contains("ON \"users\".\"id\" = \"orders\".\"user_id\""));
4921 }
4922
4923 #[test]
4924 fn test_cross_join() {
4925 let builder = PostgresQueryBuilder::new();
4926 let mut stmt = Query::select();
4927 stmt.column("users.name")
4928 .column("roles.title")
4929 .from("users")
4930 .cross_join("roles");
4931
4932 let (sql, _values) = builder.build_select(&stmt);
4933 assert!(sql.contains("CROSS JOIN \"roles\""));
4934 assert!(!sql.contains("ON"));
4935 }
4936
4937 #[test]
4938 fn test_multiple_joins() {
4939 let builder = PostgresQueryBuilder::new();
4940 let mut stmt = Query::select();
4941 stmt.column("users.name")
4942 .column("orders.amount")
4943 .column("products.title")
4944 .from("users")
4945 .inner_join(
4946 "orders",
4947 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
4948 )
4949 .inner_join(
4950 "products",
4951 Expr::col(("orders", "product_id")).eq(Expr::col(("products", "id"))),
4952 );
4953
4954 let (sql, _values) = builder.build_select(&stmt);
4955 assert!(sql.contains("INNER JOIN \"orders\""));
4956 assert!(sql.contains("INNER JOIN \"products\""));
4957 assert!(sql.contains("\"users\".\"id\" = \"orders\".\"user_id\""));
4958 assert!(sql.contains("\"orders\".\"product_id\" = \"products\".\"id\""));
4959 }
4960
4961 #[test]
4962 fn test_join_with_complex_condition() {
4963 let builder = PostgresQueryBuilder::new();
4964 let mut stmt = Query::select();
4965 stmt.column("users.name")
4966 .column("orders.amount")
4967 .from("users")
4968 .inner_join(
4969 "orders",
4970 Expr::col(("users", "id"))
4971 .eq(Expr::col(("orders", "user_id")))
4972 .and(Expr::col(("orders", "status")).eq("active")),
4973 );
4974
4975 let (sql, values) = builder.build_select(&stmt);
4976 assert!(sql.contains("INNER JOIN \"orders\""));
4977 assert!(sql.contains("ON"));
4978 assert!(sql.contains("\"users\".\"id\" = \"orders\".\"user_id\""));
4979 assert!(sql.contains("AND"));
4980 assert!(sql.contains("\"orders\".\"status\" = $"));
4981 assert_eq!(values.len(), 1);
4982 }
4983
4984 #[test]
4987 fn test_group_by_single_column() {
4988 let builder = PostgresQueryBuilder::new();
4989 let mut stmt = Query::select();
4990 stmt.column("category")
4991 .from("products")
4992 .group_by("category");
4993
4994 let (sql, _values) = builder.build_select(&stmt);
4995 assert!(sql.contains("GROUP BY \"category\""));
4996 }
4997
4998 #[test]
4999 fn test_group_by_multiple_columns() {
5000 let builder = PostgresQueryBuilder::new();
5001 let mut stmt = Query::select();
5002 stmt.column("category")
5003 .column("brand")
5004 .from("products")
5005 .group_by("category")
5006 .group_by("brand");
5007
5008 let (sql, _values) = builder.build_select(&stmt);
5009 assert!(sql.contains("GROUP BY \"category\", \"brand\""));
5010 }
5011
5012 #[test]
5013 fn test_group_by_with_count() {
5014 use crate::expr::SimpleExpr;
5015 use crate::types::{ColumnRef, IntoIden};
5016
5017 let builder = PostgresQueryBuilder::new();
5018 let mut stmt = Query::select();
5019 stmt.column("category")
5020 .expr(SimpleExpr::FunctionCall(
5021 "COUNT".into_iden(),
5022 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
5023 ))
5024 .from("products")
5025 .group_by("category");
5026
5027 let (sql, _values) = builder.build_select(&stmt);
5028 assert!(sql.contains("COUNT(*)"));
5029 assert!(sql.contains("GROUP BY \"category\""));
5030 }
5031
5032 #[test]
5033 fn test_having_simple() {
5034 use crate::expr::SimpleExpr;
5035 use crate::types::{BinOper, ColumnRef, IntoIden};
5036
5037 let builder = PostgresQueryBuilder::new();
5038 let mut stmt = Query::select();
5039 let count_expr = SimpleExpr::FunctionCall(
5040 "COUNT".into_iden(),
5041 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
5042 );
5043
5044 stmt.column("category")
5045 .expr(count_expr.clone())
5046 .from("products")
5047 .group_by("category")
5048 .and_having(SimpleExpr::Binary(
5049 Box::new(count_expr),
5050 BinOper::GreaterThan,
5051 Box::new(SimpleExpr::Value(5.into())),
5052 ));
5053
5054 let (sql, values) = builder.build_select(&stmt);
5055 assert!(sql.contains("GROUP BY \"category\""));
5056 assert!(sql.contains("HAVING"));
5057 assert!(sql.contains("COUNT(*)"));
5058 assert!(sql.contains(">"));
5059 assert_eq!(values.len(), 1);
5060 }
5061
5062 #[test]
5063 fn test_group_by_having_with_sum() {
5064 use crate::expr::SimpleExpr;
5065 use crate::types::{BinOper, ColumnRef, IntoIden};
5066
5067 let builder = PostgresQueryBuilder::new();
5068 let mut stmt = Query::select();
5069 let sum_expr = SimpleExpr::FunctionCall(
5070 "SUM".into_iden(),
5071 vec![SimpleExpr::Column(ColumnRef::column("amount"))],
5072 );
5073
5074 stmt.column("user_id")
5075 .expr(sum_expr.clone())
5076 .from("orders")
5077 .group_by("user_id")
5078 .and_having(SimpleExpr::Binary(
5079 Box::new(sum_expr),
5080 BinOper::GreaterThan,
5081 Box::new(SimpleExpr::Value(1000.into())),
5082 ));
5083
5084 let (sql, values) = builder.build_select(&stmt);
5085 assert!(sql.contains("SUM(\"amount\")"));
5086 assert!(sql.contains("GROUP BY \"user_id\""));
5087 assert!(sql.contains("HAVING"));
5088 assert_eq!(values.len(), 1);
5089 }
5090
5091 #[test]
5092 fn test_select_distinct() {
5093 let builder = PostgresQueryBuilder::new();
5094 let mut stmt = Query::select();
5095 stmt.column("category").from("products").distinct();
5096
5097 let (sql, _values) = builder.build_select(&stmt);
5098 assert!(sql.starts_with("SELECT DISTINCT"));
5099 assert!(sql.contains("\"category\""));
5100 assert!(sql.contains("FROM \"products\""));
5101 }
5102
5103 #[test]
5104 fn test_select_distinct_on() {
5105 let builder = PostgresQueryBuilder::new();
5106 let mut stmt = Query::select();
5107 stmt.column("id")
5108 .column("name")
5109 .from("users")
5110 .distinct_on(vec!["category"])
5111 .order_by("category", crate::types::Order::Asc);
5112
5113 let (sql, _values) = builder.build_select(&stmt);
5114 assert!(sql.contains("SELECT DISTINCT ON (\"category\")"));
5115 assert!(sql.contains("\"id\""));
5116 assert!(sql.contains("\"name\""));
5117 assert!(sql.contains("ORDER BY \"category\" ASC"));
5118 }
5119
5120 #[test]
5121 #[should_panic(expected = "PostgreSQL does not support DISTINCT ROW")]
5122 fn test_select_distinct_row_panics() {
5123 use crate::query::SelectDistinct;
5124
5125 let builder = PostgresQueryBuilder::new();
5126 let mut stmt = Query::select();
5127 stmt.column("name").from("products");
5128 stmt.distinct = Some(SelectDistinct::DistinctRow);
5129
5130 let _ = builder.build_select(&stmt);
5131 }
5132
5133 #[test]
5134 fn test_select_union() {
5135 let builder = PostgresQueryBuilder::new();
5136 let mut stmt1 = Query::select();
5137 stmt1.column("id").from("users");
5138
5139 let mut stmt2 = Query::select();
5140 stmt2.column("id").from("customers");
5141
5142 stmt1.union(stmt2);
5143
5144 let (sql, _values) = builder.build_select(&stmt1);
5145 assert!(sql.contains("SELECT \"id\" FROM \"users\""));
5146 assert!(sql.contains("UNION SELECT \"id\" FROM \"customers\""));
5147 }
5148
5149 #[test]
5150 fn test_select_union_all() {
5151 let builder = PostgresQueryBuilder::new();
5152 let mut stmt1 = Query::select();
5153 stmt1.column("name").from("products");
5154
5155 let mut stmt2 = Query::select();
5156 stmt2.column("name").from("archived_products");
5157
5158 stmt1.union_all(stmt2);
5159
5160 let (sql, _values) = builder.build_select(&stmt1);
5161 assert!(sql.contains("SELECT \"name\" FROM \"products\""));
5162 assert!(sql.contains("UNION ALL SELECT \"name\" FROM \"archived_products\""));
5163 }
5164
5165 #[test]
5166 fn test_select_intersect() {
5167 let builder = PostgresQueryBuilder::new();
5168 let mut stmt1 = Query::select();
5169 stmt1.column("email").from("subscribers");
5170
5171 let mut stmt2 = Query::select();
5172 stmt2.column("email").from("customers");
5173
5174 stmt1.intersect(stmt2);
5175
5176 let (sql, _values) = builder.build_select(&stmt1);
5177 assert!(sql.contains("SELECT \"email\" FROM \"subscribers\""));
5178 assert!(sql.contains("INTERSECT SELECT \"email\" FROM \"customers\""));
5179 }
5180
5181 #[test]
5182 fn test_select_except() {
5183 let builder = PostgresQueryBuilder::new();
5184 let mut stmt1 = Query::select();
5185 stmt1.column("id").from("all_users");
5186
5187 let mut stmt2 = Query::select();
5188 stmt2.column("id").from("banned_users");
5189
5190 stmt1.except(stmt2);
5191
5192 let (sql, _values) = builder.build_select(&stmt1);
5193 assert!(sql.contains("SELECT \"id\" FROM \"all_users\""));
5194 assert!(sql.contains("EXCEPT SELECT \"id\" FROM \"banned_users\""));
5195 }
5196
5197 #[test]
5198 fn test_select_multiple_unions() {
5199 let builder = PostgresQueryBuilder::new();
5200 let mut stmt1 = Query::select();
5201 stmt1.column("id").from("table1");
5202
5203 let mut stmt2 = Query::select();
5204 stmt2.column("id").from("table2");
5205
5206 let mut stmt3 = Query::select();
5207 stmt3.column("id").from("table3");
5208
5209 stmt1.union(stmt2);
5210 stmt1.union_all(stmt3);
5211
5212 let (sql, _values) = builder.build_select(&stmt1);
5213 assert!(sql.contains("SELECT \"id\" FROM \"table1\""));
5214 assert!(sql.contains("UNION SELECT \"id\" FROM \"table2\""));
5215 assert!(sql.contains("UNION ALL SELECT \"id\" FROM \"table3\""));
5216 }
5217
5218 #[test]
5219 fn test_select_exists_subquery() {
5220 use crate::expr::Expr;
5221
5222 let builder = PostgresQueryBuilder::new();
5223 let mut stmt = Query::select();
5224
5225 stmt.column("name").from("users");
5227
5228 let mut subquery = Query::select();
5230 subquery
5231 .column("id")
5232 .from("orders")
5233 .and_where(Expr::col(("orders", "user_id")).eq(Expr::col(("users", "id"))));
5234
5235 stmt.and_where(Expr::exists(subquery));
5237
5238 let (sql, _values) = builder.build_select(&stmt);
5239 assert!(sql.contains("SELECT \"name\" FROM \"users\""));
5240 assert!(sql.contains("WHERE"));
5241 assert!(sql.contains("EXISTS"));
5242 assert!(sql.contains("SELECT \"id\" FROM \"orders\""));
5243 }
5244
5245 #[test]
5246 fn test_select_in_subquery() {
5247 use crate::expr::Expr;
5248
5249 let builder = PostgresQueryBuilder::new();
5250 let mut stmt = Query::select();
5251
5252 stmt.column("name").from("users");
5254
5255 let mut subquery = Query::select();
5257 subquery.column("user_id").from("premium_users");
5258
5259 stmt.and_where(Expr::col("id").in_subquery(subquery));
5261
5262 let (sql, _values) = builder.build_select(&stmt);
5263 assert!(sql.contains("SELECT \"name\" FROM \"users\""));
5264 assert!(sql.contains("WHERE"));
5265 assert!(sql.contains("\"id\""));
5266 assert!(sql.contains("IN"));
5267 assert!(sql.contains("SELECT \"user_id\" FROM \"premium_users\""));
5268 }
5269
5270 #[test]
5271 fn test_select_not_exists_subquery() {
5272 use crate::expr::Expr;
5273
5274 let builder = PostgresQueryBuilder::new();
5275 let mut stmt = Query::select();
5276
5277 stmt.column("email").from("users");
5279
5280 let mut subquery = Query::select();
5282 subquery
5283 .column("id")
5284 .from("banned_users")
5285 .and_where(Expr::col(("banned_users", "user_id")).eq(Expr::col(("users", "id"))));
5286
5287 stmt.and_where(Expr::not_exists(subquery));
5289
5290 let (sql, _values) = builder.build_select(&stmt);
5291 assert!(sql.contains("SELECT \"email\" FROM \"users\""));
5292 assert!(sql.contains("WHERE"));
5293 assert!(sql.contains("NOT EXISTS"));
5294 assert!(sql.contains("SELECT \"id\" FROM \"banned_users\""));
5295 }
5296
5297 #[test]
5300 fn test_not_in_subquery() {
5301 let builder = PostgresQueryBuilder::new();
5302
5303 let mut subquery = Query::select();
5304 subquery
5305 .column("user_id")
5306 .from("blocked_users")
5307 .and_where(Expr::col("reason").eq("spam"));
5308
5309 let mut stmt = Query::select();
5310 stmt.column("name")
5311 .from("users")
5312 .and_where(Expr::col("id").not_in_subquery(subquery));
5313
5314 let (sql, values) = builder.build_select(&stmt);
5315 assert!(sql.contains("NOT IN"));
5316 assert!(sql.contains("SELECT \"user_id\" FROM \"blocked_users\""));
5317 assert!(sql.contains("\"reason\" = $"));
5318 assert_eq!(values.len(), 1);
5319 }
5320
5321 #[test]
5322 fn test_subquery_in_select_list() {
5323 let builder = PostgresQueryBuilder::new();
5324
5325 let mut subquery = Query::select();
5326 subquery
5327 .expr(Expr::col("count"))
5328 .from("order_counts")
5329 .and_where(Expr::col(("order_counts", "user_id")).eq(Expr::col(("users", "id"))));
5330
5331 let mut stmt = Query::select();
5332 stmt.column("name")
5333 .expr(Expr::subquery(subquery))
5334 .from("users");
5335
5336 let (sql, _values) = builder.build_select(&stmt);
5337 assert!(sql.contains("\"name\""));
5338 assert!(sql.contains("(SELECT \"count\" FROM \"order_counts\""));
5339 assert!(sql.contains("\"order_counts\".\"user_id\" = \"users\".\"id\""));
5340 }
5341
5342 #[test]
5343 fn test_multiple_exists_conditions() {
5344 let builder = PostgresQueryBuilder::new();
5345
5346 let mut sub1 = Query::select();
5347 sub1.column("id")
5348 .from("orders")
5349 .and_where(Expr::col(("orders", "user_id")).eq(Expr::col(("users", "id"))));
5350
5351 let mut sub2 = Query::select();
5352 sub2.column("id")
5353 .from("reviews")
5354 .and_where(Expr::col(("reviews", "user_id")).eq(Expr::col(("users", "id"))));
5355
5356 let mut stmt = Query::select();
5357 stmt.column("name")
5358 .from("users")
5359 .and_where(Expr::exists(sub1))
5360 .and_where(Expr::exists(sub2));
5361
5362 let (sql, _values) = builder.build_select(&stmt);
5363 assert!(sql.contains("EXISTS (SELECT \"id\" FROM \"orders\""));
5364 assert!(sql.contains("EXISTS (SELECT \"id\" FROM \"reviews\""));
5365 }
5366
5367 #[test]
5368 fn test_nested_subquery() {
5369 let builder = PostgresQueryBuilder::new();
5370
5371 let mut inner_subquery = Query::select();
5372 inner_subquery
5373 .column("department_id")
5374 .from("top_departments")
5375 .and_where(Expr::col("revenue").gt(1000000));
5376
5377 let mut outer_subquery = Query::select();
5378 outer_subquery
5379 .column("id")
5380 .from("employees")
5381 .and_where(Expr::col("department_id").in_subquery(inner_subquery));
5382
5383 let mut stmt = Query::select();
5384 stmt.column("name")
5385 .from("users")
5386 .and_where(Expr::col("employee_id").in_subquery(outer_subquery));
5387
5388 let (sql, values) = builder.build_select(&stmt);
5389 assert!(sql.contains("IN (SELECT \"id\" FROM \"employees\""));
5390 assert!(sql.contains("IN (SELECT \"department_id\" FROM \"top_departments\""));
5391 assert!(sql.contains("\"revenue\" > $"));
5392 assert_eq!(values.len(), 1);
5393 }
5394
5395 #[test]
5396 fn test_subquery_with_complex_where() {
5397 let builder = PostgresQueryBuilder::new();
5398
5399 let mut subquery = Query::select();
5400 subquery
5401 .column("product_id")
5402 .from("inventory")
5403 .and_where(Expr::col("quantity").gt(0))
5404 .and_where(Expr::col("warehouse").eq("main"))
5405 .and_where(Expr::col("status").eq("available"));
5406
5407 let mut stmt = Query::select();
5408 stmt.column("name")
5409 .column("price")
5410 .from("products")
5411 .and_where(Expr::col("id").in_subquery(subquery))
5412 .and_where(Expr::col("active").eq(true));
5413
5414 let (sql, values) = builder.build_select(&stmt);
5415 assert!(sql.contains("IN (SELECT \"product_id\" FROM \"inventory\""));
5416 assert!(sql.contains("\"quantity\" > $"));
5417 assert!(sql.contains("\"warehouse\" = $"));
5418 assert!(sql.contains("\"status\" = $"));
5419 assert!(sql.contains("\"active\" = $"));
5420 assert_eq!(values.len(), 4); }
5422
5423 #[test]
5424 fn test_from_subquery_preserves_parameter_values() {
5425 let builder = PostgresQueryBuilder::new();
5426
5427 let mut subquery = Query::select();
5429 subquery
5430 .column("id")
5431 .column("name")
5432 .from("users")
5433 .and_where(Expr::col("active").eq(true))
5434 .and_where(Expr::col("role").eq("admin"));
5435
5436 let mut stmt = Query::select();
5437 stmt.column("name")
5438 .from_subquery(subquery, Alias::new("active_admins"))
5439 .and_where(Expr::col("name").like("A%"));
5440
5441 let (sql, values) = builder.build_select(&stmt);
5443
5444 assert!(sql.contains("(SELECT"));
5446 assert!(sql.contains(") AS \"active_admins\""));
5447 assert_eq!(values.len(), 3);
5449 }
5450
5451 #[test]
5452 fn test_from_subquery_postgres_placeholder_renumbering() {
5453 let builder = PostgresQueryBuilder::new();
5454
5455 let mut subquery = Query::select();
5457 subquery
5458 .column("id")
5459 .from("users")
5460 .and_where(Expr::col("role").eq("admin"));
5461
5462 let mut stmt = Query::select();
5463 stmt.column("name")
5464 .from_subquery(subquery, Alias::new("sub"))
5465 .and_where(Expr::col("status").eq("active"));
5466
5467 let (sql, values) = builder.build_select(&stmt);
5469
5470 assert!(sql.contains("$1"));
5472 assert!(sql.contains("$2"));
5473 assert_eq!(values.len(), 2);
5474 }
5475
5476 #[test]
5479 fn test_where_is_null() {
5480 let builder = PostgresQueryBuilder::new();
5481 let mut stmt = Query::select();
5482 stmt.column("name")
5483 .from("users")
5484 .and_where(Expr::col("deleted_at").is_null());
5485
5486 let (sql, values) = builder.build_select(&stmt);
5487 assert!(sql.contains("\"deleted_at\" IS"));
5488 assert!(sql.to_uppercase().contains("NULL"));
5489 assert_eq!(values.len(), 0);
5490 }
5491
5492 #[test]
5493 fn test_where_is_not_null() {
5494 let builder = PostgresQueryBuilder::new();
5495 let mut stmt = Query::select();
5496 stmt.column("name")
5497 .from("users")
5498 .and_where(Expr::col("email").is_not_null());
5499
5500 let (sql, values) = builder.build_select(&stmt);
5501 assert!(sql.contains("\"email\" IS NOT"));
5502 assert!(sql.to_uppercase().contains("NULL"));
5503 assert_eq!(values.len(), 0);
5504 }
5505
5506 #[test]
5507 fn test_is_null_combined_with_other_conditions() {
5508 let builder = PostgresQueryBuilder::new();
5509 let mut stmt = Query::select();
5510 stmt.column("name")
5511 .from("users")
5512 .and_where(Expr::col("active").eq(true))
5513 .and_where(Expr::col("deleted_at").is_null())
5514 .and_where(Expr::col("email").is_not_null());
5515
5516 let (sql, values) = builder.build_select(&stmt);
5517 assert!(sql.contains("\"active\" = $"));
5518 assert!(sql.contains("\"deleted_at\" IS"));
5519 assert!(sql.contains("\"email\" IS NOT"));
5520 assert_eq!(values.len(), 1);
5521 }
5522
5523 #[test]
5524 fn test_is_null_with_join() {
5525 let builder = PostgresQueryBuilder::new();
5526 let mut stmt = Query::select();
5527 stmt.column(("users", "name"))
5528 .from("users")
5529 .left_join(
5530 "profiles",
5531 Expr::col(("users", "id")).eq(Expr::col(("profiles", "user_id"))),
5532 )
5533 .and_where(Expr::col(("profiles", "id")).is_null());
5534
5535 let (sql, values) = builder.build_select(&stmt);
5536 assert!(sql.contains("LEFT JOIN \"profiles\""));
5537 assert!(sql.contains("\"profiles\".\"id\" IS"));
5538 assert_eq!(values.len(), 0);
5539 }
5540
5541 #[test]
5543 fn test_where_or_condition() {
5544 use crate::expr::Condition;
5545
5546 let builder = PostgresQueryBuilder::new();
5547 let mut stmt = Query::select();
5548 stmt.column("name").from("users").cond_where(
5549 Condition::any()
5550 .add(Expr::col("status").eq("active"))
5551 .add(Expr::col("status").eq("pending")),
5552 );
5553
5554 let (sql, values) = builder.build_select(&stmt);
5555 assert!(sql.contains("\"status\" = $"));
5556 assert!(sql.contains(" OR "));
5557 assert_eq!(values.len(), 2);
5558 }
5559
5560 #[test]
5561 fn test_where_between() {
5562 let builder = PostgresQueryBuilder::new();
5563 let mut stmt = Query::select();
5564 stmt.column("name")
5565 .from("products")
5566 .and_where(Expr::col("price").between(100, 500));
5567
5568 let (sql, values) = builder.build_select(&stmt);
5569 assert!(sql.contains("\"price\" BETWEEN $"));
5570 assert!(sql.contains("AND $"));
5571 assert_eq!(values.len(), 2);
5572 }
5573
5574 #[test]
5575 fn test_where_not_between() {
5576 let builder = PostgresQueryBuilder::new();
5577 let mut stmt = Query::select();
5578 stmt.column("name")
5579 .from("products")
5580 .and_where(Expr::col("price").not_between(0, 10));
5581
5582 let (sql, values) = builder.build_select(&stmt);
5583 assert!(sql.contains("\"price\" NOT BETWEEN $"));
5584 assert!(sql.contains("AND $"));
5585 assert_eq!(values.len(), 2);
5586 }
5587
5588 #[test]
5589 fn test_where_like() {
5590 let builder = PostgresQueryBuilder::new();
5591 let mut stmt = Query::select();
5592 stmt.column("name")
5593 .from("users")
5594 .and_where(Expr::col("email").like("%@gmail.com"));
5595
5596 let (sql, values) = builder.build_select(&stmt);
5597 assert!(sql.contains("\"email\" LIKE $"));
5598 assert_eq!(values.len(), 1);
5599 }
5600
5601 #[test]
5602 fn test_where_in_values() {
5603 let builder = PostgresQueryBuilder::new();
5604 let mut stmt = Query::select();
5605 stmt.column("name")
5606 .from("users")
5607 .and_where(Expr::col("role").is_in(vec!["admin", "moderator", "editor"]));
5608
5609 let (sql, values) = builder.build_select(&stmt);
5610 assert!(sql.contains("\"role\" IN"));
5611 assert_eq!(values.len(), 3);
5612 }
5613
5614 #[test]
5615 fn test_insert_with_null_value() {
5616 use crate::value::Value;
5617
5618 let builder = PostgresQueryBuilder::new();
5619 let mut stmt = Query::insert();
5620 stmt.into_table("users")
5621 .columns(vec!["name", "email", "phone"])
5622 .values(vec![
5623 Value::String(Some(Box::new("John".to_string()))),
5624 Value::String(Some(Box::new("john@example.com".to_string()))),
5625 Value::String(None),
5626 ])
5627 .unwrap();
5628
5629 let (sql, values) = builder.build_insert(&stmt);
5630 assert!(sql.contains("INSERT INTO \"users\""));
5631 assert!(sql.contains("\"name\""));
5632 assert!(sql.contains("\"email\""));
5633 assert!(sql.contains("\"phone\""));
5634 assert!(sql.contains("NULL"));
5636 assert_eq!(values.len(), 2);
5637 }
5638
5639 #[test]
5640 fn test_select_with_single_cte() {
5641 let builder = PostgresQueryBuilder::new();
5642
5643 let mut cte_query = Query::select();
5645 cte_query
5646 .column("id")
5647 .column("name")
5648 .from("employees")
5649 .and_where(Expr::col("department").eq("Engineering"));
5650
5651 let mut stmt = Query::select();
5653 stmt.with_cte("eng_employees", cte_query)
5654 .column("name")
5655 .from("eng_employees");
5656
5657 let (sql, _values) = builder.build_select(&stmt);
5658 assert!(sql.contains("WITH"));
5659 assert!(sql.contains("\"eng_employees\""));
5660 assert!(sql.contains("AS"));
5661 assert!(sql.contains("SELECT \"id\", \"name\" FROM \"employees\""));
5662 assert!(sql.contains("SELECT \"name\" FROM \"eng_employees\""));
5663 }
5664
5665 #[test]
5666 fn test_select_with_multiple_ctes() {
5667 let builder = PostgresQueryBuilder::new();
5668
5669 let mut cte1 = Query::select();
5671 cte1.column("id")
5672 .column("name")
5673 .from("employees")
5674 .and_where(Expr::col("department").eq("Engineering"));
5675
5676 let mut cte2 = Query::select();
5678 cte2.column("id")
5679 .column("name")
5680 .from("employees")
5681 .and_where(Expr::col("department").eq("Sales"));
5682
5683 let mut stmt = Query::select();
5685 stmt.with_cte("eng_emp", cte1)
5686 .with_cte("sales_emp", cte2)
5687 .column("name")
5688 .from("eng_emp");
5689
5690 let (sql, _values) = builder.build_select(&stmt);
5691 assert!(sql.contains("WITH"));
5692 assert!(sql.contains("\"eng_emp\""));
5693 assert!(sql.contains("\"sales_emp\""));
5694 assert!(sql.contains("AS"));
5695 assert!(sql.contains("\"eng_emp\" AS"));
5697 assert!(sql.contains("\"sales_emp\" AS"));
5698 }
5699
5700 #[test]
5701 fn test_select_with_recursive_cte() {
5702 let builder = PostgresQueryBuilder::new();
5703
5704 let mut cte_query = Query::select();
5706 cte_query
5707 .column("id")
5708 .column("name")
5709 .column("manager_id")
5710 .from("employees");
5711
5712 let mut stmt = Query::select();
5714 stmt.with_recursive_cte("employee_hierarchy", cte_query)
5715 .column("name")
5716 .from("employee_hierarchy");
5717
5718 let (sql, _values) = builder.build_select(&stmt);
5719 assert!(sql.contains("WITH RECURSIVE"));
5720 assert!(sql.contains("\"employee_hierarchy\""));
5721 assert!(sql.contains("AS"));
5722 assert!(sql.contains("SELECT \"id\", \"name\", \"manager_id\" FROM \"employees\""));
5723 assert!(sql.contains("SELECT \"name\" FROM \"employee_hierarchy\""));
5724 }
5725
5726 #[test]
5729 fn test_window_row_number_with_partition_and_order() {
5730 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5731
5732 let builder = PostgresQueryBuilder::new();
5733 let mut stmt = Query::select();
5734
5735 let window = WindowStatement {
5736 partition_by: vec![Expr::col("department").into_simple_expr()],
5737 order_by: vec![OrderExpr {
5738 expr: OrderExprKind::Column("salary".into_iden()),
5739 order: Order::Desc,
5740 nulls: None,
5741 }],
5742 frame: None,
5743 };
5744
5745 stmt.expr(Expr::row_number().over(window))
5746 .column("name")
5747 .from("employees");
5748
5749 let (sql, _values) = builder.build_select(&stmt);
5750 assert_eq!(
5751 sql,
5752 r#"SELECT ROW_NUMBER() OVER ( PARTITION BY "department" ORDER BY "salary" DESC ), "name" FROM "employees""#
5753 );
5754 }
5755
5756 #[test]
5757 fn test_window_row_number_order_only() {
5758 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5759
5760 let builder = PostgresQueryBuilder::new();
5761 let mut stmt = Query::select();
5762
5763 let window = WindowStatement {
5764 partition_by: vec![],
5765 order_by: vec![OrderExpr {
5766 expr: OrderExprKind::Column("id".into_iden()),
5767 order: Order::Asc,
5768 nulls: None,
5769 }],
5770 frame: None,
5771 };
5772
5773 stmt.expr(Expr::row_number().over(window)).from("users");
5774
5775 let (sql, _values) = builder.build_select(&stmt);
5776 assert_eq!(
5777 sql,
5778 r#"SELECT ROW_NUMBER() OVER ( ORDER BY "id" ASC ) FROM "users""#
5779 );
5780 }
5781
5782 #[test]
5783 fn test_window_rank_basic() {
5784 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5785
5786 let builder = PostgresQueryBuilder::new();
5787 let mut stmt = Query::select();
5788
5789 let window = WindowStatement {
5790 partition_by: vec![],
5791 order_by: vec![OrderExpr {
5792 expr: OrderExprKind::Column("score".into_iden()),
5793 order: Order::Desc,
5794 nulls: None,
5795 }],
5796 frame: None,
5797 };
5798
5799 stmt.expr(Expr::rank().over(window))
5800 .column("name")
5801 .from("students");
5802
5803 let (sql, _values) = builder.build_select(&stmt);
5804 assert_eq!(
5805 sql,
5806 r#"SELECT RANK() OVER ( ORDER BY "score" DESC ), "name" FROM "students""#
5807 );
5808 }
5809
5810 #[test]
5811 fn test_window_rank_with_partition() {
5812 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5813
5814 let builder = PostgresQueryBuilder::new();
5815 let mut stmt = Query::select();
5816
5817 let window = WindowStatement {
5818 partition_by: vec![Expr::col("class").into_simple_expr()],
5819 order_by: vec![OrderExpr {
5820 expr: OrderExprKind::Column("score".into_iden()),
5821 order: Order::Desc,
5822 nulls: None,
5823 }],
5824 frame: None,
5825 };
5826
5827 stmt.expr(Expr::rank().over(window))
5828 .column("name")
5829 .from("students");
5830
5831 let (sql, _values) = builder.build_select(&stmt);
5832 assert_eq!(
5833 sql,
5834 r#"SELECT RANK() OVER ( PARTITION BY "class" ORDER BY "score" DESC ), "name" FROM "students""#
5835 );
5836 }
5837
5838 #[test]
5839 fn test_window_dense_rank_basic() {
5840 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5841
5842 let builder = PostgresQueryBuilder::new();
5843 let mut stmt = Query::select();
5844
5845 let window = WindowStatement {
5846 partition_by: vec![],
5847 order_by: vec![OrderExpr {
5848 expr: OrderExprKind::Column("points".into_iden()),
5849 order: Order::Desc,
5850 nulls: None,
5851 }],
5852 frame: None,
5853 };
5854
5855 stmt.expr(Expr::dense_rank().over(window))
5856 .column("player")
5857 .from("scores");
5858
5859 let (sql, _values) = builder.build_select(&stmt);
5860 assert_eq!(
5861 sql,
5862 r#"SELECT DENSE_RANK() OVER ( ORDER BY "points" DESC ), "player" FROM "scores""#
5863 );
5864 }
5865
5866 #[test]
5867 fn test_window_dense_rank_with_partition() {
5868 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5869
5870 let builder = PostgresQueryBuilder::new();
5871 let mut stmt = Query::select();
5872
5873 let window = WindowStatement {
5874 partition_by: vec![Expr::col("league").into_simple_expr()],
5875 order_by: vec![OrderExpr {
5876 expr: OrderExprKind::Column("points".into_iden()),
5877 order: Order::Desc,
5878 nulls: None,
5879 }],
5880 frame: None,
5881 };
5882
5883 stmt.expr(Expr::dense_rank().over(window))
5884 .column("player")
5885 .from("scores");
5886
5887 let (sql, _values) = builder.build_select(&stmt);
5888 assert_eq!(
5889 sql,
5890 r#"SELECT DENSE_RANK() OVER ( PARTITION BY "league" ORDER BY "points" DESC ), "player" FROM "scores""#
5891 );
5892 }
5893
5894 #[test]
5895 fn test_window_ntile_four_buckets() {
5896 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5897
5898 let builder = PostgresQueryBuilder::new();
5899 let mut stmt = Query::select();
5900
5901 let window = WindowStatement {
5902 partition_by: vec![],
5903 order_by: vec![OrderExpr {
5904 expr: OrderExprKind::Column("salary".into_iden()),
5905 order: Order::Asc,
5906 nulls: None,
5907 }],
5908 frame: None,
5909 };
5910
5911 stmt.expr(Expr::ntile(4).over(window))
5912 .column("name")
5913 .from("employees");
5914
5915 let (sql, _values) = builder.build_select(&stmt);
5916 assert_eq!(
5917 sql,
5918 r#"SELECT NTILE($1) OVER ( ORDER BY "salary" ASC ), "name" FROM "employees""#
5919 );
5920 }
5921
5922 #[test]
5923 fn test_window_ntile_custom_buckets() {
5924 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5925
5926 let builder = PostgresQueryBuilder::new();
5927 let mut stmt = Query::select();
5928
5929 let window = WindowStatement {
5930 partition_by: vec![Expr::col("department").into_simple_expr()],
5931 order_by: vec![OrderExpr {
5932 expr: OrderExprKind::Column("salary".into_iden()),
5933 order: Order::Desc,
5934 nulls: None,
5935 }],
5936 frame: None,
5937 };
5938
5939 stmt.expr(Expr::ntile(3).over(window))
5940 .column("name")
5941 .from("employees");
5942
5943 let (sql, _values) = builder.build_select(&stmt);
5944 assert_eq!(
5945 sql,
5946 r#"SELECT NTILE($1) OVER ( PARTITION BY "department" ORDER BY "salary" DESC ), "name" FROM "employees""#
5947 );
5948 }
5949
5950 #[test]
5951 fn test_window_lead_basic() {
5952 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5953
5954 let builder = PostgresQueryBuilder::new();
5955 let mut stmt = Query::select();
5956
5957 let window = WindowStatement {
5958 partition_by: vec![],
5959 order_by: vec![OrderExpr {
5960 expr: OrderExprKind::Column("date".into_iden()),
5961 order: Order::Asc,
5962 nulls: None,
5963 }],
5964 frame: None,
5965 };
5966
5967 stmt.expr(Expr::lead(Expr::col("price").into_simple_expr(), None, None).over(window))
5968 .column("date")
5969 .from("stocks");
5970
5971 let (sql, values) = builder.build_select(&stmt);
5972 assert_eq!(
5973 sql,
5974 r#"SELECT LEAD("price") OVER ( ORDER BY "date" ASC ), "date" FROM "stocks""#
5975 );
5976 assert_eq!(values.len(), 0);
5977 }
5978
5979 #[test]
5980 fn test_window_lead_with_offset_and_default() {
5981 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
5982
5983 let builder = PostgresQueryBuilder::new();
5984 let mut stmt = Query::select();
5985
5986 let window = WindowStatement {
5987 partition_by: vec![Expr::col("ticker").into_simple_expr()],
5988 order_by: vec![OrderExpr {
5989 expr: OrderExprKind::Column("date".into_iden()),
5990 order: Order::Asc,
5991 nulls: None,
5992 }],
5993 frame: None,
5994 };
5995
5996 stmt.expr(
5997 Expr::lead(
5998 Expr::col("price").into_simple_expr(),
5999 Some(2),
6000 Some(0.0.into()),
6001 )
6002 .over(window),
6003 )
6004 .column("date")
6005 .from("stocks");
6006
6007 let (sql, values) = builder.build_select(&stmt);
6008 assert!(sql.contains("LEAD"));
6009 assert!(sql.contains("OVER"));
6010 assert!(sql.contains(r#"PARTITION BY "ticker""#));
6011 assert!(sql.contains(r#"ORDER BY "date" ASC"#));
6012 assert_eq!(values.len(), 2);
6013 }
6014
6015 #[test]
6016 fn test_window_lag_basic() {
6017 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6018
6019 let builder = PostgresQueryBuilder::new();
6020 let mut stmt = Query::select();
6021
6022 let window = WindowStatement {
6023 partition_by: vec![],
6024 order_by: vec![OrderExpr {
6025 expr: OrderExprKind::Column("month".into_iden()),
6026 order: Order::Asc,
6027 nulls: None,
6028 }],
6029 frame: None,
6030 };
6031
6032 stmt.expr(Expr::lag(Expr::col("revenue").into_simple_expr(), None, None).over(window))
6033 .column("month")
6034 .from("sales");
6035
6036 let (sql, values) = builder.build_select(&stmt);
6037 assert_eq!(
6038 sql,
6039 r#"SELECT LAG("revenue") OVER ( ORDER BY "month" ASC ), "month" FROM "sales""#
6040 );
6041 assert_eq!(values.len(), 0);
6042 }
6043
6044 #[test]
6045 fn test_window_lag_with_offset_and_default() {
6046 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6047
6048 let builder = PostgresQueryBuilder::new();
6049 let mut stmt = Query::select();
6050
6051 let window = WindowStatement {
6052 partition_by: vec![Expr::col("product").into_simple_expr()],
6053 order_by: vec![OrderExpr {
6054 expr: OrderExprKind::Column("month".into_iden()),
6055 order: Order::Asc,
6056 nulls: None,
6057 }],
6058 frame: None,
6059 };
6060
6061 stmt.expr(
6062 Expr::lag(
6063 Expr::col("revenue").into_simple_expr(),
6064 Some(3),
6065 Some(0.0.into()),
6066 )
6067 .over(window),
6068 )
6069 .column("month")
6070 .from("sales");
6071
6072 let (sql, values) = builder.build_select(&stmt);
6073 assert!(sql.contains("LAG"));
6074 assert!(sql.contains("OVER"));
6075 assert!(sql.contains(r#"PARTITION BY "product""#));
6076 assert!(sql.contains(r#"ORDER BY "month" ASC"#));
6077 assert_eq!(values.len(), 2);
6078 }
6079
6080 #[test]
6081 fn test_window_first_value() {
6082 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6083
6084 let builder = PostgresQueryBuilder::new();
6085 let mut stmt = Query::select();
6086
6087 let window = WindowStatement {
6088 partition_by: vec![Expr::col("category").into_simple_expr()],
6089 order_by: vec![OrderExpr {
6090 expr: OrderExprKind::Column("price".into_iden()),
6091 order: Order::Asc,
6092 nulls: None,
6093 }],
6094 frame: None,
6095 };
6096
6097 stmt.expr(Expr::first_value(Expr::col("name").into_simple_expr()).over(window))
6098 .column("name")
6099 .from("products");
6100
6101 let (sql, _values) = builder.build_select(&stmt);
6102 assert_eq!(
6103 sql,
6104 r#"SELECT FIRST_VALUE("name") OVER ( PARTITION BY "category" ORDER BY "price" ASC ), "name" FROM "products""#
6105 );
6106 }
6107
6108 #[test]
6109 fn test_window_last_value() {
6110 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6111
6112 let builder = PostgresQueryBuilder::new();
6113 let mut stmt = Query::select();
6114
6115 let window = WindowStatement {
6116 partition_by: vec![Expr::col("category").into_simple_expr()],
6117 order_by: vec![OrderExpr {
6118 expr: OrderExprKind::Column("price".into_iden()),
6119 order: Order::Desc,
6120 nulls: None,
6121 }],
6122 frame: None,
6123 };
6124
6125 stmt.expr(Expr::last_value(Expr::col("name").into_simple_expr()).over(window))
6126 .column("name")
6127 .from("products");
6128
6129 let (sql, _values) = builder.build_select(&stmt);
6130 assert_eq!(
6131 sql,
6132 r#"SELECT LAST_VALUE("name") OVER ( PARTITION BY "category" ORDER BY "price" DESC ), "name" FROM "products""#
6133 );
6134 }
6135
6136 #[test]
6137 fn test_window_nth_value() {
6138 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6139
6140 let builder = PostgresQueryBuilder::new();
6141 let mut stmt = Query::select();
6142
6143 let window = WindowStatement {
6144 partition_by: vec![Expr::col("department").into_simple_expr()],
6145 order_by: vec![OrderExpr {
6146 expr: OrderExprKind::Column("salary".into_iden()),
6147 order: Order::Desc,
6148 nulls: None,
6149 }],
6150 frame: None,
6151 };
6152
6153 stmt.expr(Expr::nth_value(Expr::col("name").into_simple_expr(), 2).over(window))
6154 .column("name")
6155 .from("employees");
6156
6157 let (sql, values) = builder.build_select(&stmt);
6158 assert!(sql.contains("NTH_VALUE"));
6159 assert!(sql.contains(r#"PARTITION BY "department""#));
6160 assert!(sql.contains(r#"ORDER BY "salary" DESC"#));
6161 assert_eq!(values.len(), 1); }
6163
6164 #[test]
6165 fn test_window_row_number_multiple_partition_columns() {
6166 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6167
6168 let builder = PostgresQueryBuilder::new();
6169 let mut stmt = Query::select();
6170
6171 let window = WindowStatement {
6172 partition_by: vec![
6173 Expr::col("country").into_simple_expr(),
6174 Expr::col("city").into_simple_expr(),
6175 ],
6176 order_by: vec![OrderExpr {
6177 expr: OrderExprKind::Column("population".into_iden()),
6178 order: Order::Desc,
6179 nulls: None,
6180 }],
6181 frame: None,
6182 };
6183
6184 stmt.expr(Expr::row_number().over(window))
6185 .column("name")
6186 .from("cities");
6187
6188 let (sql, _values) = builder.build_select(&stmt);
6189 assert_eq!(
6190 sql,
6191 r#"SELECT ROW_NUMBER() OVER ( PARTITION BY "country", "city" ORDER BY "population" DESC ), "name" FROM "cities""#
6192 );
6193 }
6194
6195 #[test]
6196 fn test_window_ntile_with_partition() {
6197 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6198
6199 let builder = PostgresQueryBuilder::new();
6200 let mut stmt = Query::select();
6201
6202 let window = WindowStatement {
6203 partition_by: vec![Expr::col("region").into_simple_expr()],
6204 order_by: vec![OrderExpr {
6205 expr: OrderExprKind::Column("revenue".into_iden()),
6206 order: Order::Desc,
6207 nulls: None,
6208 }],
6209 frame: None,
6210 };
6211
6212 stmt.expr(Expr::ntile(5).over(window))
6213 .column("store_name")
6214 .from("stores");
6215
6216 let (sql, _values) = builder.build_select(&stmt);
6217 assert_eq!(
6218 sql,
6219 r#"SELECT NTILE($1) OVER ( PARTITION BY "region" ORDER BY "revenue" DESC ), "store_name" FROM "stores""#
6220 );
6221 }
6222
6223 #[test]
6224 fn test_window_lead_with_offset_no_default() {
6225 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6226
6227 let builder = PostgresQueryBuilder::new();
6228 let mut stmt = Query::select();
6229
6230 let window = WindowStatement {
6231 partition_by: vec![],
6232 order_by: vec![OrderExpr {
6233 expr: OrderExprKind::Column("quarter".into_iden()),
6234 order: Order::Asc,
6235 nulls: None,
6236 }],
6237 frame: None,
6238 };
6239
6240 stmt.expr(Expr::lead(Expr::col("sales").into_simple_expr(), Some(2), None).over(window))
6241 .column("quarter")
6242 .from("quarterly_sales");
6243
6244 let (sql, _values) = builder.build_select(&stmt);
6245 assert_eq!(
6246 sql,
6247 r#"SELECT LEAD("sales", $1) OVER ( ORDER BY "quarter" ASC ), "quarter" FROM "quarterly_sales""#
6248 );
6249 }
6250
6251 #[test]
6252 fn test_window_lag_with_different_offset() {
6253 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6254
6255 let builder = PostgresQueryBuilder::new();
6256 let mut stmt = Query::select();
6257
6258 let window = WindowStatement {
6259 partition_by: vec![Expr::col("sensor_id").into_simple_expr()],
6260 order_by: vec![OrderExpr {
6261 expr: OrderExprKind::Column("timestamp".into_iden()),
6262 order: Order::Asc,
6263 nulls: None,
6264 }],
6265 frame: None,
6266 };
6267
6268 stmt.expr(Expr::lag(Expr::col("reading").into_simple_expr(), Some(5), None).over(window))
6269 .column("timestamp")
6270 .from("sensor_data");
6271
6272 let (sql, values) = builder.build_select(&stmt);
6273 assert_eq!(
6274 sql,
6275 r#"SELECT LAG("reading", $1) OVER ( PARTITION BY "sensor_id" ORDER BY "timestamp" ASC ), "timestamp" FROM "sensor_data""#
6276 );
6277 assert_eq!(values.len(), 1);
6278 }
6279
6280 #[test]
6281 fn test_window_multiple_functions_in_query() {
6282 use crate::types::{Order, OrderExpr, OrderExprKind, WindowStatement};
6283
6284 let builder = PostgresQueryBuilder::new();
6285 let mut stmt = Query::select();
6286
6287 let window1 = WindowStatement {
6288 partition_by: vec![Expr::col("department").into_simple_expr()],
6289 order_by: vec![OrderExpr {
6290 expr: OrderExprKind::Column("salary".into_iden()),
6291 order: Order::Desc,
6292 nulls: None,
6293 }],
6294 frame: None,
6295 };
6296
6297 let window2 = WindowStatement {
6298 partition_by: vec![],
6299 order_by: vec![OrderExpr {
6300 expr: OrderExprKind::Column("hire_date".into_iden()),
6301 order: Order::Asc,
6302 nulls: None,
6303 }],
6304 frame: None,
6305 };
6306
6307 stmt.expr(Expr::row_number().over(window1))
6308 .expr(Expr::rank().over(window2))
6309 .column("name")
6310 .from("employees");
6311
6312 let (sql, _values) = builder.build_select(&stmt);
6313 assert!(
6314 sql.contains(
6315 r#"ROW_NUMBER() OVER ( PARTITION BY "department" ORDER BY "salary" DESC )"#
6316 )
6317 );
6318 assert!(sql.contains(r#"RANK() OVER ( ORDER BY "hire_date" ASC )"#));
6319 assert!(sql.contains(r#""name""#));
6320 assert!(sql.contains(r#"FROM "employees""#));
6321 }
6322
6323 #[test]
6326 fn test_join_three_tables() {
6327 let builder = PostgresQueryBuilder::new();
6328 let mut stmt = Query::select();
6329 stmt.column(("users", "name"))
6330 .column(("orders", "order_date"))
6331 .column(("products", "product_name"))
6332 .from("users")
6333 .inner_join(
6334 "orders",
6335 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6336 )
6337 .inner_join(
6338 "products",
6339 Expr::col(("orders", "product_id")).eq(Expr::col(("products", "id"))),
6340 );
6341
6342 let (sql, _values) = builder.build_select(&stmt);
6343 assert_eq!(
6344 sql,
6345 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""#
6346 );
6347 }
6348
6349 #[test]
6350 fn test_self_join() {
6351 use crate::types::TableRef;
6352
6353 let builder = PostgresQueryBuilder::new();
6354 let mut stmt = Query::select();
6355 stmt.column(("e1", "name"))
6356 .column(("e2", "name"))
6357 .from(TableRef::table_alias("employees", "e1"))
6358 .inner_join(
6359 TableRef::table_alias("employees", "e2"),
6360 Expr::col(("e1", "manager_id")).eq(Expr::col(("e2", "id"))),
6361 );
6362
6363 let (sql, _values) = builder.build_select(&stmt);
6364 assert!(sql.contains(r#"FROM "employees" AS "e1""#));
6365 assert!(sql.contains(r#"INNER JOIN "employees" AS "e2""#));
6366 assert!(sql.contains(r#"ON "e1"."manager_id" = "e2"."id""#));
6367 }
6368
6369 #[test]
6370 fn test_join_complex_conditions() {
6371 let builder = PostgresQueryBuilder::new();
6372 let mut stmt = Query::select();
6373 stmt.from("orders").left_join(
6374 "customers",
6375 Expr::col(("orders", "customer_id"))
6376 .eq(Expr::col(("customers", "id")))
6377 .and(Expr::col(("customers", "active")).eq(true))
6378 .and(
6379 Expr::col(("orders", "created_at"))
6380 .gt(Expr::col(("customers", "registered_at"))),
6381 ),
6382 );
6383
6384 let (sql, values) = builder.build_select(&stmt);
6385 assert!(sql.contains("LEFT JOIN \"customers\""));
6386 assert!(sql.contains("\"orders\".\"customer_id\" = \"customers\".\"id\""));
6387 assert!(sql.contains("AND \"customers\".\"active\" = $"));
6388 assert!(sql.contains("AND \"orders\".\"created_at\" > \"customers\".\"registered_at\""));
6389 assert_eq!(values.len(), 1); }
6391
6392 #[test]
6393 fn test_join_with_subquery_in_condition() {
6394 let builder = PostgresQueryBuilder::new();
6395
6396 let mut subquery = Query::select();
6397 subquery.expr(Expr::col("max_id")).from("user_stats");
6398
6399 let mut stmt = Query::select();
6400 stmt.from("users").inner_join(
6401 "profiles",
6402 Expr::col(("users", "id"))
6403 .eq(Expr::col(("profiles", "user_id")))
6404 .and(Expr::col(("users", "id")).in_subquery(subquery)),
6405 );
6406
6407 let (sql, _values) = builder.build_select(&stmt);
6408 assert!(sql.contains("INNER JOIN \"profiles\""));
6409 assert!(sql.contains("\"users\".\"id\" = \"profiles\".\"user_id\""));
6410 assert!(sql.contains("IN"));
6411 assert!(sql.contains("SELECT \"max_id\" FROM \"user_stats\""));
6412 }
6413
6414 #[test]
6415 fn test_multiple_left_joins() {
6416 let builder = PostgresQueryBuilder::new();
6417 let mut stmt = Query::select();
6418 stmt.column(("users", "name"))
6419 .column(("profiles", "bio"))
6420 .column(("addresses", "city"))
6421 .column(("phone_numbers", "number"))
6422 .from("users")
6423 .left_join(
6424 "profiles",
6425 Expr::col(("users", "id")).eq(Expr::col(("profiles", "user_id"))),
6426 )
6427 .left_join(
6428 "addresses",
6429 Expr::col(("users", "id")).eq(Expr::col(("addresses", "user_id"))),
6430 )
6431 .left_join(
6432 "phone_numbers",
6433 Expr::col(("users", "id")).eq(Expr::col(("phone_numbers", "user_id"))),
6434 );
6435
6436 let (sql, _values) = builder.build_select(&stmt);
6437 assert!(sql.contains("LEFT JOIN \"profiles\""));
6438 assert!(sql.contains("LEFT JOIN \"addresses\""));
6439 assert!(sql.contains("LEFT JOIN \"phone_numbers\""));
6440 }
6441
6442 #[test]
6443 fn test_mixed_join_types() {
6444 let builder = PostgresQueryBuilder::new();
6445 let mut stmt = Query::select();
6446 stmt.column(("users", "name"))
6447 .from("users")
6448 .inner_join(
6449 "orders",
6450 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6451 )
6452 .left_join(
6453 "reviews",
6454 Expr::col(("orders", "id")).eq(Expr::col(("reviews", "order_id"))),
6455 )
6456 .right_join(
6457 "refunds",
6458 Expr::col(("orders", "id")).eq(Expr::col(("refunds", "order_id"))),
6459 );
6460
6461 let (sql, _values) = builder.build_select(&stmt);
6462 assert!(sql.contains("INNER JOIN \"orders\""));
6463 assert!(sql.contains("LEFT JOIN \"reviews\""));
6464 assert!(sql.contains("RIGHT JOIN \"refunds\""));
6465 }
6466
6467 #[test]
6468 fn test_join_with_group_by() {
6469 use crate::expr::SimpleExpr;
6470 use crate::types::{BinOper, ColumnRef, IntoIden};
6471
6472 let builder = PostgresQueryBuilder::new();
6473 let mut stmt = Query::select();
6474 let count_expr = SimpleExpr::FunctionCall(
6475 "COUNT".into_iden(),
6476 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
6477 );
6478
6479 stmt.column(("users", "name"))
6480 .expr(count_expr.clone())
6481 .from("users")
6482 .inner_join(
6483 "orders",
6484 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6485 )
6486 .group_by(("users", "name"))
6487 .and_having(SimpleExpr::Binary(
6488 Box::new(count_expr),
6489 BinOper::GreaterThan,
6490 Box::new(SimpleExpr::Value(5.into())),
6491 ));
6492
6493 let (sql, values) = builder.build_select(&stmt);
6494 assert!(sql.contains("INNER JOIN \"orders\""));
6495 assert!(sql.contains("GROUP BY \"users\".\"name\""));
6496 assert!(sql.contains("HAVING"));
6497 assert!(sql.contains("COUNT(*) > $"));
6498 assert_eq!(values.len(), 1);
6499 }
6500
6501 #[test]
6502 fn test_join_with_window_function() {
6503 use crate::types::{IntoIden, Order, OrderExpr, OrderExprKind, WindowStatement};
6504
6505 let builder = PostgresQueryBuilder::new();
6506 let mut stmt = Query::select();
6507
6508 let window = WindowStatement {
6509 partition_by: vec![Expr::col(("departments", "name")).into_simple_expr()],
6510 order_by: vec![OrderExpr {
6511 expr: OrderExprKind::TableColumn("employees".into_iden(), "salary".into_iden()),
6512 order: Order::Desc,
6513 nulls: None,
6514 }],
6515 frame: None,
6516 };
6517
6518 stmt.column(("employees", "name"))
6519 .expr(Expr::row_number().over(window))
6520 .from("employees")
6521 .inner_join(
6522 "departments",
6523 Expr::col(("employees", "department_id")).eq(Expr::col(("departments", "id"))),
6524 );
6525
6526 let (sql, _values) = builder.build_select(&stmt);
6527 assert!(sql.contains("INNER JOIN \"departments\""));
6528 assert!(sql.contains("ROW_NUMBER() OVER"));
6529 assert!(sql.contains(r#"PARTITION BY "departments"."name""#));
6530 }
6531
6532 #[test]
6533 fn test_four_table_join() {
6534 let builder = PostgresQueryBuilder::new();
6535 let mut stmt = Query::select();
6536 stmt.column(("users", "name"))
6537 .column(("orders", "order_date"))
6538 .column(("products", "product_name"))
6539 .column(("categories", "category_name"))
6540 .from("users")
6541 .inner_join(
6542 "orders",
6543 Expr::col(("users", "id")).eq(Expr::col(("orders", "user_id"))),
6544 )
6545 .inner_join(
6546 "products",
6547 Expr::col(("orders", "product_id")).eq(Expr::col(("products", "id"))),
6548 )
6549 .inner_join(
6550 "categories",
6551 Expr::col(("products", "category_id")).eq(Expr::col(("categories", "id"))),
6552 );
6553
6554 let (sql, _values) = builder.build_select(&stmt);
6555 assert!(sql.contains("FROM \"users\""));
6556 assert!(sql.contains("INNER JOIN \"orders\""));
6557 assert!(sql.contains("INNER JOIN \"products\""));
6558 assert!(sql.contains("INNER JOIN \"categories\""));
6559 }
6560
6561 #[test]
6562 fn test_join_with_cte() {
6563 use crate::types::TableRef;
6564
6565 let builder = PostgresQueryBuilder::new();
6566
6567 let mut cte = Query::select();
6568 cte.column("user_id")
6569 .expr(Expr::col("total"))
6570 .from("order_totals")
6571 .and_where(Expr::col("total").gt(1000));
6572
6573 let mut stmt = Query::select();
6574 stmt.with_cte("high_value_customers", cte)
6575 .column(("users", "name"))
6576 .column(("hvc", "total"))
6577 .from("users")
6578 .inner_join(
6579 TableRef::table_alias("high_value_customers", "hvc"),
6580 Expr::col(("users", "id")).eq(Expr::col(("hvc", "user_id"))),
6581 );
6582
6583 let (sql, values) = builder.build_select(&stmt);
6584 assert!(sql.contains("WITH \"high_value_customers\" AS"));
6585 assert!(sql.contains("INNER JOIN \"high_value_customers\" AS \"hvc\""));
6586 assert_eq!(values.len(), 1); }
6588
6589 #[test]
6590 fn test_cte_with_where_and_params() {
6591 let builder = PostgresQueryBuilder::new();
6592
6593 let mut cte_query = Query::select();
6594 cte_query
6595 .column("id")
6596 .column("total")
6597 .from("orders")
6598 .and_where(Expr::col("status").eq("completed"))
6599 .and_where(Expr::col("amount").gt(1000));
6600
6601 let mut stmt = Query::select();
6602 stmt.with_cte("large_orders", cte_query)
6603 .column("id")
6604 .column("total")
6605 .from("large_orders");
6606
6607 let (sql, values) = builder.build_select(&stmt);
6608 assert!(sql.contains("WITH"));
6609 assert!(sql.contains(r#""large_orders" AS"#));
6610 assert!(sql.contains(r#""status" = $"#));
6611 assert!(sql.contains(r#""amount" > $"#));
6612 assert_eq!(values.len(), 2);
6613 }
6614
6615 #[test]
6616 fn test_cte_used_in_join() {
6617 use crate::types::TableRef;
6618
6619 let builder = PostgresQueryBuilder::new();
6620
6621 let mut cte_query = Query::select();
6622 cte_query
6623 .column("user_id")
6624 .column("order_count")
6625 .from("orders")
6626 .group_by("user_id");
6627
6628 let mut stmt = Query::select();
6629 stmt.with_cte("user_orders", cte_query)
6630 .column(("users", "name"))
6631 .column(("uo", "order_count"))
6632 .from("users")
6633 .inner_join(
6634 TableRef::table_alias("user_orders", "uo"),
6635 Expr::col(("users", "id")).eq(Expr::col(("uo", "user_id"))),
6636 );
6637
6638 let (sql, values) = builder.build_select(&stmt);
6639 assert!(sql.contains("WITH"));
6640 assert!(sql.contains(r#""user_orders" AS"#));
6641 assert!(sql.contains(r#"INNER JOIN "user_orders" AS "uo""#));
6642 assert!(sql.contains(r#""users"."id" = "uo"."user_id""#));
6643 assert_eq!(values.len(), 0);
6644 }
6645
6646 #[test]
6647 fn test_cte_with_aggregation() {
6648 use crate::expr::SimpleExpr;
6649 use crate::types::{ColumnRef, IntoIden};
6650
6651 let builder = PostgresQueryBuilder::new();
6652
6653 let mut cte_query = Query::select();
6654 cte_query
6655 .column("category")
6656 .expr(SimpleExpr::FunctionCall(
6657 "COUNT".into_iden(),
6658 vec![SimpleExpr::Column(ColumnRef::Asterisk)],
6659 ))
6660 .expr(SimpleExpr::FunctionCall(
6661 "SUM".into_iden(),
6662 vec![SimpleExpr::Column(ColumnRef::column("price"))],
6663 ))
6664 .from("products")
6665 .group_by("category");
6666
6667 let mut stmt = Query::select();
6668 stmt.with_cte("category_stats", cte_query)
6669 .column("category")
6670 .from("category_stats");
6671
6672 let (sql, values) = builder.build_select(&stmt);
6673 assert!(sql.contains("WITH"));
6674 assert!(sql.contains(r#""category_stats" AS"#));
6675 assert!(sql.contains("COUNT(*)"));
6676 assert!(sql.contains(r#"SUM("price")"#));
6677 assert!(sql.contains(r#"GROUP BY "category""#));
6678 assert_eq!(values.len(), 0);
6679 }
6680
6681 #[test]
6682 fn test_cte_with_subquery() {
6683 let builder = PostgresQueryBuilder::new();
6684
6685 let mut sub = Query::select();
6686 sub.column("user_id").from("vip_users");
6687
6688 let mut cte_query = Query::select();
6689 cte_query
6690 .column("id")
6691 .column("total")
6692 .from("orders")
6693 .and_where(Expr::col("user_id").in_subquery(sub))
6694 .and_where(Expr::col("status").eq("shipped"));
6695
6696 let mut stmt = Query::select();
6697 stmt.with_cte("vip_orders", cte_query)
6698 .column("id")
6699 .column("total")
6700 .from("vip_orders");
6701
6702 let (sql, values) = builder.build_select(&stmt);
6703 assert!(sql.contains("WITH"));
6704 assert!(sql.contains(r#""vip_orders" AS"#));
6705 assert!(sql.contains("IN"));
6706 assert!(sql.contains(r#"SELECT "user_id" FROM "vip_users""#));
6707 assert!(sql.contains(r#""status" = $"#));
6708 assert_eq!(values.len(), 1);
6709 }
6710
6711 #[test]
6712 fn test_multiple_recursive_and_regular_ctes() {
6713 let builder = PostgresQueryBuilder::new();
6714
6715 let mut regular_cte = Query::select();
6717 regular_cte
6718 .column("id")
6719 .column("name")
6720 .from("departments")
6721 .and_where(Expr::col("active").eq(true));
6722
6723 let mut recursive_cte = Query::select();
6725 recursive_cte
6726 .column("id")
6727 .column("name")
6728 .column("parent_id")
6729 .from("categories");
6730
6731 let mut stmt = Query::select();
6733 stmt.with_cte("active_depts", regular_cte)
6734 .with_recursive_cte("category_tree", recursive_cte)
6735 .column("name")
6736 .from("category_tree");
6737
6738 let (sql, values) = builder.build_select(&stmt);
6739 assert!(sql.contains("WITH RECURSIVE"));
6740 assert!(sql.contains(r#""active_depts" AS"#));
6741 assert!(sql.contains(r#""category_tree" AS"#));
6742 assert!(sql.contains(r#""active" = $"#));
6743 assert!(sql.contains(r#"FROM "category_tree""#));
6744 assert_eq!(values.len(), 1);
6745 }
6746
6747 #[test]
6750 fn test_case_simple_when_else() {
6751 let builder = PostgresQueryBuilder::new();
6752
6753 let case_expr = Expr::case()
6754 .when(Expr::col("status").eq("active"), "Active")
6755 .else_result("Inactive");
6756
6757 let mut stmt = Query::select();
6758 stmt.expr_as(case_expr, "status_label").from("users");
6759
6760 let (sql, values) = builder.build_select(&stmt);
6761 assert!(sql.contains("CASE"));
6762 assert!(sql.contains("WHEN"));
6763 assert!(sql.contains(r#""status" = $"#));
6764 assert!(sql.contains("THEN"));
6765 assert!(sql.contains("ELSE"));
6766 assert!(sql.contains("END"));
6767 assert!(sql.contains(r#"AS "status_label""#));
6768 assert_eq!(values.len(), 3);
6769 }
6770
6771 #[test]
6772 fn test_case_multiple_when_clauses() {
6773 let builder = PostgresQueryBuilder::new();
6774
6775 let case_expr = Expr::case()
6776 .when(Expr::col("score").gte(90), "A")
6777 .when(Expr::col("score").gte(80), "B")
6778 .when(Expr::col("score").gte(70), "C")
6779 .else_result("F");
6780
6781 let mut stmt = Query::select();
6782 stmt.expr_as(case_expr, "grade").from("students");
6783
6784 let (sql, values) = builder.build_select(&stmt);
6785 assert!(sql.contains("CASE"));
6786 let when_count = sql.matches("WHEN").count();
6788 assert_eq!(when_count, 3);
6789 let then_count = sql.matches("THEN").count();
6790 assert_eq!(then_count, 3);
6791 assert!(sql.contains("ELSE"));
6792 assert!(sql.contains("END"));
6793 assert_eq!(values.len(), 7);
6795 }
6796
6797 #[test]
6798 fn test_case_without_else() {
6799 let builder = PostgresQueryBuilder::new();
6800
6801 let case_expr = Expr::case()
6802 .when(Expr::col("type").eq("admin"), "Administrator")
6803 .when(Expr::col("type").eq("user"), "Regular User")
6804 .build();
6805
6806 let mut stmt = Query::select();
6807 stmt.expr_as(case_expr, "type_label").from("accounts");
6808
6809 let (sql, values) = builder.build_select(&stmt);
6810 assert!(sql.contains("CASE"));
6811 assert!(sql.contains("WHEN"));
6812 assert!(sql.contains("THEN"));
6813 assert!(!sql.contains("ELSE"));
6814 assert!(sql.contains("END"));
6815 assert_eq!(values.len(), 4);
6816 }
6817
6818 #[test]
6819 fn test_case_in_where_clause() {
6820 let builder = PostgresQueryBuilder::new();
6821
6822 let case_expr = Expr::case()
6823 .when(Expr::col("role").eq("admin"), 1)
6824 .else_result(0);
6825
6826 let mut stmt = Query::select();
6827 stmt.column("name").from("users").and_where(case_expr.eq(1));
6828
6829 let (sql, values) = builder.build_select(&stmt);
6830 assert!(sql.contains("WHERE"));
6831 assert!(sql.contains("CASE"));
6832 assert!(sql.contains("WHEN"));
6833 assert!(sql.contains("END"));
6834 assert!(values.len() >= 3);
6835 }
6836
6837 #[test]
6838 fn test_case_in_order_by() {
6839 let builder = PostgresQueryBuilder::new();
6840
6841 let case_expr = Expr::case()
6842 .when(Expr::col("priority").eq("high"), 1)
6843 .when(Expr::col("priority").eq("medium"), 2)
6844 .else_result(3);
6845
6846 let mut stmt = Query::select();
6847 stmt.column("name")
6848 .column("priority")
6849 .from("tasks")
6850 .order_by_expr(case_expr, crate::types::Order::Asc);
6851
6852 let (sql, values) = builder.build_select(&stmt);
6853 assert!(sql.contains("ORDER BY"));
6854 assert!(sql.contains("CASE"));
6855 assert!(sql.contains("WHEN"));
6856 assert!(sql.contains("END"));
6857 assert!(sql.contains("ASC"));
6858 assert_eq!(values.len(), 5);
6859 }
6860
6861 #[test]
6864 fn test_order_by_multiple_columns_mixed() {
6865 let builder = PostgresQueryBuilder::new();
6866
6867 let mut stmt = Query::select();
6868 stmt.column("name")
6869 .column("age")
6870 .column("score")
6871 .from("students")
6872 .order_by("name", crate::types::Order::Asc)
6873 .order_by("age", crate::types::Order::Desc)
6874 .order_by("score", crate::types::Order::Asc);
6875
6876 let (sql, _values) = builder.build_select(&stmt);
6877 assert!(sql.contains("ORDER BY"));
6878 assert!(sql.contains(r#""name" ASC"#));
6879 assert!(sql.contains(r#""age" DESC"#));
6880 assert!(sql.contains(r#""score" ASC"#));
6881 }
6882
6883 #[test]
6884 fn test_order_by_nulls_first() {
6885 use crate::types::{IntoColumnRef, NullOrdering, OrderExpr, OrderExprKind};
6886
6887 let builder = PostgresQueryBuilder::new();
6888
6889 let mut stmt = Query::select();
6890 stmt.column("name").column("created_at").from("events");
6891 stmt.orders.push(OrderExpr {
6892 expr: OrderExprKind::Expr(Box::new(SimpleExpr::Column("created_at".into_column_ref()))),
6893 order: crate::types::Order::Desc,
6894 nulls: Some(NullOrdering::First),
6895 });
6896
6897 let (sql, _values) = builder.build_select(&stmt);
6898 assert!(sql.contains("ORDER BY"));
6899 assert!(sql.contains("DESC"));
6900 assert!(sql.contains("NULLS FIRST"));
6901 }
6902
6903 #[test]
6904 fn test_order_by_nulls_last() {
6905 use crate::types::{IntoColumnRef, NullOrdering, OrderExpr, OrderExprKind};
6906
6907 let builder = PostgresQueryBuilder::new();
6908
6909 let mut stmt = Query::select();
6910 stmt.column("name").column("updated_at").from("posts");
6911 stmt.orders.push(OrderExpr {
6912 expr: OrderExprKind::Expr(Box::new(SimpleExpr::Column("updated_at".into_column_ref()))),
6913 order: crate::types::Order::Asc,
6914 nulls: Some(NullOrdering::Last),
6915 });
6916
6917 let (sql, _values) = builder.build_select(&stmt);
6918 assert!(sql.contains("ORDER BY"));
6919 assert!(sql.contains("ASC"));
6920 assert!(sql.contains("NULLS LAST"));
6921 }
6922
6923 #[test]
6924 fn test_limit_without_offset() {
6925 let builder = PostgresQueryBuilder::new();
6926
6927 let mut stmt = Query::select();
6928 stmt.column("id").from("items").limit(5);
6929
6930 let (sql, values) = builder.build_select(&stmt);
6931 assert!(sql.contains("LIMIT"));
6932 assert!(!sql.contains("OFFSET"));
6933 assert_eq!(values.len(), 1);
6934 }
6935
6936 #[test]
6939 fn test_arithmetic_add_sub() {
6940 let builder = PostgresQueryBuilder::new();
6941 let mut stmt = Query::select();
6942 stmt.column("name").from("products");
6943 stmt.and_where(Expr::col("price").add(10i32).gt(100i32));
6944
6945 let (sql, values) = builder.build_select(&stmt);
6946 assert!(sql.contains(r#""price" + $1"#));
6947 assert!(sql.contains("> $2"));
6948 assert_eq!(values.len(), 2);
6949 }
6950
6951 #[test]
6952 fn test_arithmetic_mul_div_mod() {
6953 let builder = PostgresQueryBuilder::new();
6954 let mut stmt = Query::select();
6955 stmt.column("name").from("items");
6956 stmt.and_where(
6957 Expr::col("quantity")
6958 .mul(Expr::col("unit_price"))
6959 .gt(1000i32),
6960 );
6961
6962 let (sql, values) = builder.build_select(&stmt);
6963 assert!(sql.contains(r#""quantity" * "unit_price""#));
6964 assert!(sql.contains("> $1"));
6965 assert_eq!(values.len(), 1);
6966 }
6967
6968 #[test]
6969 fn test_like_ilike_pattern() {
6970 let builder = PostgresQueryBuilder::new();
6971 let mut stmt = Query::select();
6972 stmt.column("name").from("users");
6973 stmt.and_where(Expr::col("email").like("%@example.com"));
6974
6975 let (sql, values) = builder.build_select(&stmt);
6976 assert!(sql.contains(r#""email" LIKE $1"#));
6977 assert_eq!(values.len(), 1);
6978 }
6979
6980 #[test]
6981 fn test_pg_concat_operator() {
6982 use crate::types::{BinOper, IntoColumnRef, PgBinOper};
6983 let builder = PostgresQueryBuilder::new();
6984 let mut stmt = Query::select();
6985 stmt.expr(SimpleExpr::Binary(
6986 Box::new(SimpleExpr::Column("first_name".into_column_ref())),
6987 BinOper::PgOperator(PgBinOper::Concatenate),
6988 Box::new(SimpleExpr::Column("last_name".into_column_ref())),
6989 ));
6990 stmt.from("users");
6991
6992 let (sql, _values) = builder.build_select(&stmt);
6993 assert!(sql.contains(r#""first_name" || "last_name""#));
6994 }
6995
6996 #[test]
6999 fn test_drop_table_basic() {
7000 let builder = PostgresQueryBuilder::new();
7001 let mut stmt = Query::drop_table();
7002 stmt.table("users");
7003
7004 let (sql, values) = builder.build_drop_table(&stmt);
7005 assert_eq!(sql, "DROP TABLE \"users\"");
7006 assert_eq!(values.len(), 0);
7007 }
7008
7009 #[test]
7010 fn test_drop_table_if_exists() {
7011 let builder = PostgresQueryBuilder::new();
7012 let mut stmt = Query::drop_table();
7013 stmt.table("users").if_exists();
7014
7015 let (sql, values) = builder.build_drop_table(&stmt);
7016 assert_eq!(sql, "DROP TABLE IF EXISTS \"users\"");
7017 assert_eq!(values.len(), 0);
7018 }
7019
7020 #[test]
7021 fn test_drop_table_cascade() {
7022 let builder = PostgresQueryBuilder::new();
7023 let mut stmt = Query::drop_table();
7024 stmt.table("users").cascade();
7025
7026 let (sql, values) = builder.build_drop_table(&stmt);
7027 assert_eq!(sql, "DROP TABLE \"users\" CASCADE");
7028 assert_eq!(values.len(), 0);
7029 }
7030
7031 #[test]
7032 fn test_drop_table_restrict() {
7033 let builder = PostgresQueryBuilder::new();
7034 let mut stmt = Query::drop_table();
7035 stmt.table("users").restrict();
7036
7037 let (sql, values) = builder.build_drop_table(&stmt);
7038 assert_eq!(sql, "DROP TABLE \"users\" RESTRICT");
7039 assert_eq!(values.len(), 0);
7040 }
7041
7042 #[test]
7043 fn test_drop_table_multiple() {
7044 let builder = PostgresQueryBuilder::new();
7045 let mut stmt = Query::drop_table();
7046 stmt.table("users").table("posts");
7047
7048 let (sql, values) = builder.build_drop_table(&stmt);
7049 assert_eq!(sql, "DROP TABLE \"users\", \"posts\"");
7050 assert_eq!(values.len(), 0);
7051 }
7052
7053 #[test]
7054 fn test_drop_index_basic() {
7055 let builder = PostgresQueryBuilder::new();
7056 let mut stmt = Query::drop_index();
7057 stmt.name("idx_email");
7058
7059 let (sql, values) = builder.build_drop_index(&stmt);
7060 assert_eq!(sql, "DROP INDEX \"idx_email\"");
7061 assert_eq!(values.len(), 0);
7062 }
7063
7064 #[test]
7065 fn test_drop_index_if_exists() {
7066 let builder = PostgresQueryBuilder::new();
7067 let mut stmt = Query::drop_index();
7068 stmt.name("idx_email").if_exists();
7069
7070 let (sql, values) = builder.build_drop_index(&stmt);
7071 assert_eq!(sql, "DROP INDEX IF EXISTS \"idx_email\"");
7072 assert_eq!(values.len(), 0);
7073 }
7074
7075 #[test]
7076 fn test_drop_index_cascade() {
7077 let builder = PostgresQueryBuilder::new();
7078 let mut stmt = Query::drop_index();
7079 stmt.name("idx_email").cascade();
7080
7081 let (sql, values) = builder.build_drop_index(&stmt);
7082 assert_eq!(sql, "DROP INDEX \"idx_email\" CASCADE");
7083 assert_eq!(values.len(), 0);
7084 }
7085
7086 #[test]
7087 fn test_drop_index_restrict() {
7088 let builder = PostgresQueryBuilder::new();
7089 let mut stmt = Query::drop_index();
7090 stmt.name("idx_email").restrict();
7091
7092 let (sql, values) = builder.build_drop_index(&stmt);
7093 assert_eq!(sql, "DROP INDEX \"idx_email\" RESTRICT");
7094 assert_eq!(values.len(), 0);
7095 }
7096
7097 #[test]
7100 fn test_create_table_basic() {
7101 use crate::types::{ColumnDef, ColumnType};
7102
7103 let builder = PostgresQueryBuilder::new();
7104 let mut stmt = Query::create_table();
7105 stmt.table("users");
7106 stmt.columns.push(ColumnDef {
7107 name: "id".into_iden(),
7108 column_type: Some(ColumnType::Integer),
7109 not_null: false,
7110 unique: false,
7111 primary_key: false,
7112 auto_increment: false,
7113 default: None,
7114 check: None,
7115 comment: None,
7116 });
7117 stmt.columns.push(ColumnDef {
7118 name: "name".into_iden(),
7119 column_type: Some(ColumnType::String(Some(255))),
7120 not_null: false,
7121 unique: false,
7122 primary_key: false,
7123 auto_increment: false,
7124 default: None,
7125 check: None,
7126 comment: None,
7127 });
7128
7129 let (sql, values) = builder.build_create_table(&stmt);
7130 assert!(sql.contains("CREATE TABLE \"users\""));
7131 assert!(sql.contains("\"id\" INTEGER"));
7132 assert!(sql.contains("\"name\" VARCHAR(255)"));
7133 assert_eq!(values.len(), 0);
7134 }
7135
7136 #[test]
7137 fn test_create_table_if_not_exists() {
7138 use crate::types::{ColumnDef, ColumnType};
7139
7140 let builder = PostgresQueryBuilder::new();
7141 let mut stmt = Query::create_table();
7142 stmt.table("users").if_not_exists();
7143 stmt.columns.push(ColumnDef {
7144 name: "id".into_iden(),
7145 column_type: Some(ColumnType::Integer),
7146 not_null: false,
7147 unique: false,
7148 primary_key: false,
7149 auto_increment: false,
7150 default: None,
7151 check: None,
7152 comment: None,
7153 });
7154
7155 let (sql, values) = builder.build_create_table(&stmt);
7156 assert!(sql.contains("CREATE TABLE IF NOT EXISTS \"users\""));
7157 assert!(sql.contains("\"id\" INTEGER"));
7158 assert_eq!(values.len(), 0);
7159 }
7160
7161 #[test]
7162 fn test_create_table_with_primary_key() {
7163 use crate::types::{ColumnDef, ColumnType};
7164
7165 let builder = PostgresQueryBuilder::new();
7166 let mut stmt = Query::create_table();
7167 stmt.table("users");
7168 stmt.columns.push(ColumnDef {
7169 name: "id".into_iden(),
7170 column_type: Some(ColumnType::Integer),
7171 not_null: false,
7172 unique: false,
7173 primary_key: true,
7174 auto_increment: false,
7175 default: None,
7176 check: None,
7177 comment: None,
7178 });
7179
7180 let (sql, values) = builder.build_create_table(&stmt);
7181 assert!(sql.contains("CREATE TABLE \"users\""));
7182 assert!(sql.contains("\"id\" INTEGER PRIMARY KEY"));
7183 assert_eq!(values.len(), 0);
7184 }
7185
7186 #[test]
7187 fn test_create_table_with_not_null() {
7188 use crate::types::{ColumnDef, ColumnType};
7189
7190 let builder = PostgresQueryBuilder::new();
7191 let mut stmt = Query::create_table();
7192 stmt.table("users");
7193 stmt.columns.push(ColumnDef {
7194 name: "email".into_iden(),
7195 column_type: Some(ColumnType::String(Some(255))),
7196 not_null: true,
7197 unique: false,
7198 primary_key: false,
7199 auto_increment: false,
7200 default: None,
7201 check: None,
7202 comment: None,
7203 });
7204
7205 let (sql, values) = builder.build_create_table(&stmt);
7206 assert!(sql.contains("\"email\" VARCHAR(255) NOT NULL"));
7207 assert_eq!(values.len(), 0);
7208 }
7209
7210 #[test]
7211 fn test_create_table_with_unique() {
7212 use crate::types::{ColumnDef, ColumnType};
7213
7214 let builder = PostgresQueryBuilder::new();
7215 let mut stmt = Query::create_table();
7216 stmt.table("users");
7217 stmt.columns.push(ColumnDef {
7218 name: "username".into_iden(),
7219 column_type: Some(ColumnType::String(Some(50))),
7220 not_null: false,
7221 unique: true,
7222 primary_key: false,
7223 auto_increment: false,
7224 default: None,
7225 check: None,
7226 comment: None,
7227 });
7228
7229 let (sql, values) = builder.build_create_table(&stmt);
7230 assert!(sql.contains("\"username\" VARCHAR(50) UNIQUE"));
7231 assert_eq!(values.len(), 0);
7232 }
7233
7234 #[test]
7235 fn test_create_table_with_default() {
7236 use crate::types::{ColumnDef, ColumnType};
7237
7238 let builder = PostgresQueryBuilder::new();
7239 let mut stmt = Query::create_table();
7240 stmt.table("users");
7241 stmt.columns.push(ColumnDef {
7242 name: "active".into_iden(),
7243 column_type: Some(ColumnType::Boolean),
7244 not_null: false,
7245 unique: false,
7246 primary_key: false,
7247 auto_increment: false,
7248 default: Some(Expr::value(true).into_simple_expr()),
7249 check: None,
7250 comment: None,
7251 });
7252
7253 let (sql, values) = builder.build_create_table(&stmt);
7254 assert!(sql.contains("\"active\" BOOLEAN DEFAULT"));
7255 assert_eq!(values.len(), 1);
7256 }
7257
7258 #[test]
7259 fn test_create_table_with_check() {
7260 use crate::types::{ColumnDef, ColumnType};
7261
7262 let builder = PostgresQueryBuilder::new();
7263 let mut stmt = Query::create_table();
7264 stmt.table("users");
7265 stmt.columns.push(ColumnDef {
7266 name: "age".into_iden(),
7267 column_type: Some(ColumnType::Integer),
7268 not_null: false,
7269 unique: false,
7270 primary_key: false,
7271 auto_increment: false,
7272 default: None,
7273 check: Some(Expr::col("age").gte(0).into_simple_expr()),
7274 comment: None,
7275 });
7276
7277 let (sql, values) = builder.build_create_table(&stmt);
7278 assert!(sql.contains("\"age\" INTEGER CHECK"));
7280 assert!(sql.contains(">= 0"));
7281 assert_eq!(values.len(), 0);
7282 }
7283
7284 #[test]
7285 fn test_create_table_with_table_constraint() {
7286 use crate::types::{ColumnDef, ColumnType, TableConstraint};
7287
7288 let builder = PostgresQueryBuilder::new();
7289 let mut stmt = Query::create_table();
7290 stmt.table("users");
7291 stmt.columns.push(ColumnDef {
7292 name: "id".into_iden(),
7293 column_type: Some(ColumnType::Integer),
7294 not_null: false,
7295 unique: false,
7296 primary_key: false,
7297 auto_increment: false,
7298 default: None,
7299 check: None,
7300 comment: None,
7301 });
7302 stmt.columns.push(ColumnDef {
7303 name: "email".into_iden(),
7304 column_type: Some(ColumnType::String(Some(255))),
7305 not_null: false,
7306 unique: false,
7307 primary_key: false,
7308 auto_increment: false,
7309 default: None,
7310 check: None,
7311 comment: None,
7312 });
7313 stmt.constraints.push(TableConstraint::PrimaryKey {
7314 name: Some("pk_users".into_iden()),
7315 columns: vec!["id".into_iden()],
7316 });
7317
7318 let (sql, values) = builder.build_create_table(&stmt);
7319 assert!(sql.contains("CONSTRAINT \"pk_users\" PRIMARY KEY (\"id\")"));
7320 assert_eq!(values.len(), 0);
7321 }
7322
7323 #[test]
7324 fn test_create_table_with_foreign_key() {
7325 use crate::types::{
7326 ColumnDef, ColumnType, ForeignKeyAction, IntoTableRef, TableConstraint,
7327 };
7328
7329 let builder = PostgresQueryBuilder::new();
7330 let mut stmt = Query::create_table();
7331 stmt.table("posts");
7332 stmt.columns.push(ColumnDef {
7333 name: "id".into_iden(),
7334 column_type: Some(ColumnType::Integer),
7335 not_null: false,
7336 unique: false,
7337 primary_key: true,
7338 auto_increment: false,
7339 default: None,
7340 check: None,
7341 comment: None,
7342 });
7343 stmt.columns.push(ColumnDef {
7344 name: "user_id".into_iden(),
7345 column_type: Some(ColumnType::Integer),
7346 not_null: false,
7347 unique: false,
7348 primary_key: false,
7349 auto_increment: false,
7350 default: None,
7351 check: None,
7352 comment: None,
7353 });
7354 stmt.constraints.push(TableConstraint::ForeignKey {
7355 name: Some("fk_user".into_iden()),
7356 columns: vec!["user_id".into_iden()],
7357 ref_table: Box::new("users".into_table_ref()),
7358 ref_columns: vec!["id".into_iden()],
7359 on_delete: Some(ForeignKeyAction::Cascade),
7360 on_update: Some(ForeignKeyAction::Restrict),
7361 });
7362
7363 let (sql, values) = builder.build_create_table(&stmt);
7364 assert!(sql.contains("CONSTRAINT \"fk_user\" FOREIGN KEY (\"user_id\")"));
7365 assert!(sql.contains("REFERENCES \"users\" (\"id\")"));
7366 assert!(sql.contains("ON DELETE CASCADE"));
7367 assert!(sql.contains("ON UPDATE RESTRICT"));
7368 assert_eq!(values.len(), 0);
7369 }
7370
7371 #[test]
7372 fn test_create_index_basic() {
7373 use crate::query::IndexColumn;
7374
7375 let builder = PostgresQueryBuilder::new();
7376 let mut stmt = Query::create_index();
7377 stmt.name("idx_users_email");
7378 stmt.table("users");
7379 stmt.columns.push(IndexColumn {
7380 name: "email".into_iden(),
7381 order: None,
7382 });
7383
7384 let (sql, values) = builder.build_create_index(&stmt);
7385 assert_eq!(
7386 sql,
7387 r#"CREATE INDEX "idx_users_email" ON "users" ("email")"#
7388 );
7389 assert_eq!(values.len(), 0);
7390 }
7391
7392 #[test]
7393 fn test_create_index_unique() {
7394 use crate::query::IndexColumn;
7395
7396 let builder = PostgresQueryBuilder::new();
7397 let mut stmt = Query::create_index();
7398 stmt.name("idx_users_username");
7399 stmt.table("users");
7400 stmt.unique = true;
7401 stmt.columns.push(IndexColumn {
7402 name: "username".into_iden(),
7403 order: None,
7404 });
7405
7406 let (sql, values) = builder.build_create_index(&stmt);
7407 assert_eq!(
7408 sql,
7409 r#"CREATE UNIQUE INDEX "idx_users_username" ON "users" ("username")"#
7410 );
7411 assert_eq!(values.len(), 0);
7412 }
7413
7414 #[test]
7415 fn test_create_index_if_not_exists() {
7416 use crate::query::IndexColumn;
7417
7418 let builder = PostgresQueryBuilder::new();
7419 let mut stmt = Query::create_index();
7420 stmt.name("idx_users_email");
7421 stmt.table("users");
7422 stmt.if_not_exists = true;
7423 stmt.columns.push(IndexColumn {
7424 name: "email".into_iden(),
7425 order: None,
7426 });
7427
7428 let (sql, values) = builder.build_create_index(&stmt);
7429 assert_eq!(
7430 sql,
7431 r#"CREATE INDEX IF NOT EXISTS "idx_users_email" ON "users" ("email")"#
7432 );
7433 assert_eq!(values.len(), 0);
7434 }
7435
7436 #[test]
7437 fn test_create_index_with_order() {
7438 use crate::query::IndexColumn;
7439 use crate::types::Order;
7440
7441 let builder = PostgresQueryBuilder::new();
7442 let mut stmt = Query::create_index();
7443 stmt.name("idx_users_created");
7444 stmt.table("users");
7445 stmt.columns.push(IndexColumn {
7446 name: "created_at".into_iden(),
7447 order: Some(Order::Desc),
7448 });
7449
7450 let (sql, values) = builder.build_create_index(&stmt);
7451 assert_eq!(
7452 sql,
7453 r#"CREATE INDEX "idx_users_created" ON "users" ("created_at" DESC)"#
7454 );
7455 assert_eq!(values.len(), 0);
7456 }
7457
7458 #[test]
7459 fn test_create_index_multiple_columns() {
7460 use crate::query::IndexColumn;
7461 use crate::types::Order;
7462
7463 let builder = PostgresQueryBuilder::new();
7464 let mut stmt = Query::create_index();
7465 stmt.name("idx_users_name");
7466 stmt.table("users");
7467 stmt.columns.push(IndexColumn {
7468 name: "last_name".into_iden(),
7469 order: Some(Order::Asc),
7470 });
7471 stmt.columns.push(IndexColumn {
7472 name: "first_name".into_iden(),
7473 order: Some(Order::Asc),
7474 });
7475
7476 let (sql, values) = builder.build_create_index(&stmt);
7477 assert_eq!(
7478 sql,
7479 r#"CREATE INDEX "idx_users_name" ON "users" ("last_name" ASC, "first_name" ASC)"#
7480 );
7481 assert_eq!(values.len(), 0);
7482 }
7483
7484 #[test]
7485 fn test_create_index_with_using_btree() {
7486 use crate::query::{IndexColumn, IndexMethod};
7487
7488 let builder = PostgresQueryBuilder::new();
7489 let mut stmt = Query::create_index();
7490 stmt.name("idx_users_id");
7491 stmt.table("users");
7492 stmt.using = Some(IndexMethod::BTree);
7493 stmt.columns.push(IndexColumn {
7494 name: "id".into_iden(),
7495 order: None,
7496 });
7497
7498 let (sql, values) = builder.build_create_index(&stmt);
7499 assert_eq!(
7500 sql,
7501 r#"CREATE INDEX "idx_users_id" ON "users" USING BTREE ("id")"#
7502 );
7503 assert_eq!(values.len(), 0);
7504 }
7505
7506 #[test]
7507 fn test_create_index_with_using_gin() {
7508 use crate::query::{IndexColumn, IndexMethod};
7509
7510 let builder = PostgresQueryBuilder::new();
7511 let mut stmt = Query::create_index();
7512 stmt.name("idx_posts_tags");
7513 stmt.table("posts");
7514 stmt.using = Some(IndexMethod::Gin);
7515 stmt.columns.push(IndexColumn {
7516 name: "tags".into_iden(),
7517 order: None,
7518 });
7519
7520 let (sql, values) = builder.build_create_index(&stmt);
7521 assert_eq!(
7522 sql,
7523 r#"CREATE INDEX "idx_posts_tags" ON "posts" USING GIN ("tags")"#
7524 );
7525 assert_eq!(values.len(), 0);
7526 }
7527
7528 #[test]
7529 fn test_create_index_partial_with_where() {
7530 use crate::query::IndexColumn;
7531
7532 let builder = PostgresQueryBuilder::new();
7533 let mut stmt = Query::create_index();
7534 stmt.name("idx_users_active_email");
7535 stmt.table("users");
7536 stmt.columns.push(IndexColumn {
7537 name: "email".into_iden(),
7538 order: None,
7539 });
7540 stmt.r#where = Some(Expr::col("active").eq(true).into_simple_expr());
7541
7542 let (sql, values) = builder.build_create_index(&stmt);
7543 assert_eq!(
7544 sql,
7545 r#"CREATE INDEX "idx_users_active_email" ON "users" ("email") WHERE "active" = $1"#
7546 );
7547 assert_eq!(values.len(), 1);
7548 }
7549
7550 #[test]
7551 fn test_alter_table_add_column() {
7552 use crate::query::AlterTableOperation;
7553 use crate::types::{ColumnDef, ColumnType};
7554
7555 let builder = PostgresQueryBuilder::new();
7556 let mut stmt = Query::alter_table();
7557 stmt.table("users");
7558 stmt.operations
7559 .push(AlterTableOperation::AddColumn(ColumnDef {
7560 name: "age".into_iden(),
7561 column_type: Some(ColumnType::Integer),
7562 not_null: false,
7563 unique: false,
7564 primary_key: false,
7565 auto_increment: false,
7566 default: None,
7567 check: None,
7568 comment: None,
7569 }));
7570
7571 let (sql, values) = builder.build_alter_table(&stmt);
7572 assert_eq!(sql, r#"ALTER TABLE "users" ADD COLUMN "age" INTEGER"#);
7573 assert_eq!(values.len(), 0);
7574 }
7575
7576 #[test]
7577 fn test_alter_table_drop_column() {
7578 use crate::query::AlterTableOperation;
7579
7580 let builder = PostgresQueryBuilder::new();
7581 let mut stmt = Query::alter_table();
7582 stmt.table("users");
7583 stmt.operations.push(AlterTableOperation::DropColumn {
7584 name: "age".into_iden(),
7585 if_exists: false,
7586 });
7587
7588 let (sql, values) = builder.build_alter_table(&stmt);
7589 assert_eq!(sql, r#"ALTER TABLE "users" DROP COLUMN "age""#);
7590 assert_eq!(values.len(), 0);
7591 }
7592
7593 #[test]
7594 fn test_alter_table_drop_column_if_exists() {
7595 use crate::query::AlterTableOperation;
7596
7597 let builder = PostgresQueryBuilder::new();
7598 let mut stmt = Query::alter_table();
7599 stmt.table("users");
7600 stmt.operations.push(AlterTableOperation::DropColumn {
7601 name: "age".into_iden(),
7602 if_exists: true,
7603 });
7604
7605 let (sql, values) = builder.build_alter_table(&stmt);
7606 assert_eq!(sql, r#"ALTER TABLE "users" DROP COLUMN IF EXISTS "age""#);
7607 assert_eq!(values.len(), 0);
7608 }
7609
7610 #[test]
7611 fn test_alter_table_rename_column() {
7612 use crate::query::AlterTableOperation;
7613
7614 let builder = PostgresQueryBuilder::new();
7615 let mut stmt = Query::alter_table();
7616 stmt.table("users");
7617 stmt.operations.push(AlterTableOperation::RenameColumn {
7618 old: "email".into_iden(),
7619 new: "email_address".into_iden(),
7620 });
7621
7622 let (sql, values) = builder.build_alter_table(&stmt);
7623 assert_eq!(
7624 sql,
7625 r#"ALTER TABLE "users" RENAME COLUMN "email" TO "email_address""#
7626 );
7627 assert_eq!(values.len(), 0);
7628 }
7629
7630 #[test]
7631 fn test_alter_table_modify_column_type() {
7632 use crate::query::AlterTableOperation;
7633 use crate::types::{ColumnDef, ColumnType};
7634
7635 let builder = PostgresQueryBuilder::new();
7636 let mut stmt = Query::alter_table();
7637 stmt.table("users");
7638 stmt.operations
7639 .push(AlterTableOperation::ModifyColumn(ColumnDef {
7640 name: "age".into_iden(),
7641 column_type: Some(ColumnType::BigInteger),
7642 not_null: false,
7643 unique: false,
7644 primary_key: false,
7645 auto_increment: false,
7646 default: None,
7647 check: None,
7648 comment: None,
7649 }));
7650
7651 let (sql, values) = builder.build_alter_table(&stmt);
7652 assert_eq!(sql, r#"ALTER TABLE "users" ALTER COLUMN "age" TYPE BIGINT"#);
7653 assert_eq!(values.len(), 0);
7654 }
7655
7656 #[test]
7657 fn test_alter_table_add_constraint() {
7658 use crate::query::AlterTableOperation;
7659 use crate::types::TableConstraint;
7660
7661 let builder = PostgresQueryBuilder::new();
7662 let mut stmt = Query::alter_table();
7663 stmt.table("users");
7664 stmt.operations.push(AlterTableOperation::AddConstraint(
7665 TableConstraint::Unique {
7666 name: Some("unique_email".into_iden()),
7667 columns: vec!["email".into_iden()],
7668 },
7669 ));
7670
7671 let (sql, values) = builder.build_alter_table(&stmt);
7672 assert_eq!(
7673 sql,
7674 r#"ALTER TABLE "users" ADD CONSTRAINT "unique_email" UNIQUE ("email")"#
7675 );
7676 assert_eq!(values.len(), 0);
7677 }
7678
7679 #[test]
7680 fn test_alter_table_drop_constraint() {
7681 use crate::query::AlterTableOperation;
7682
7683 let builder = PostgresQueryBuilder::new();
7684 let mut stmt = Query::alter_table();
7685 stmt.table("users");
7686 stmt.operations.push(AlterTableOperation::DropConstraint {
7687 name: "unique_email".into_iden(),
7688 if_exists: false,
7689 });
7690
7691 let (sql, values) = builder.build_alter_table(&stmt);
7692 assert_eq!(sql, r#"ALTER TABLE "users" DROP CONSTRAINT "unique_email""#);
7693 assert_eq!(values.len(), 0);
7694 }
7695
7696 #[test]
7697 fn test_alter_table_rename_table() {
7698 use crate::query::AlterTableOperation;
7699
7700 let builder = PostgresQueryBuilder::new();
7701 let mut stmt = Query::alter_table();
7702 stmt.table("users");
7703 stmt.operations
7704 .push(AlterTableOperation::RenameTable("accounts".into_iden()));
7705
7706 let (sql, values) = builder.build_alter_table(&stmt);
7707 assert_eq!(sql, r#"ALTER TABLE "users" RENAME TO "accounts""#);
7708 assert_eq!(values.len(), 0);
7709 }
7710
7711 #[test]
7714 fn test_truncate_table_basic() {
7715 let builder = PostgresQueryBuilder::new();
7716 let mut stmt = Query::truncate_table();
7717 stmt.table("users");
7718
7719 let (sql, values) = builder.build_truncate_table(&stmt);
7720 assert_eq!(sql, r#"TRUNCATE TABLE "users""#);
7721 assert_eq!(values.len(), 0);
7722 }
7723
7724 #[test]
7725 fn test_truncate_table_multiple() {
7726 let builder = PostgresQueryBuilder::new();
7727 let mut stmt = Query::truncate_table();
7728 stmt.table("users").table("posts").table("comments");
7729
7730 let (sql, values) = builder.build_truncate_table(&stmt);
7731 assert_eq!(sql, r#"TRUNCATE TABLE "users", "posts", "comments""#);
7732 assert_eq!(values.len(), 0);
7733 }
7734
7735 #[test]
7736 fn test_truncate_table_restart_identity() {
7737 let builder = PostgresQueryBuilder::new();
7738 let mut stmt = Query::truncate_table();
7739 stmt.table("users").restart_identity();
7740
7741 let (sql, values) = builder.build_truncate_table(&stmt);
7742 assert_eq!(sql, r#"TRUNCATE TABLE "users" RESTART IDENTITY"#);
7743 assert_eq!(values.len(), 0);
7744 }
7745
7746 #[test]
7747 fn test_truncate_table_cascade() {
7748 let builder = PostgresQueryBuilder::new();
7749 let mut stmt = Query::truncate_table();
7750 stmt.table("users").cascade();
7751
7752 let (sql, values) = builder.build_truncate_table(&stmt);
7753 assert_eq!(sql, r#"TRUNCATE TABLE "users" CASCADE"#);
7754 assert_eq!(values.len(), 0);
7755 }
7756
7757 #[test]
7758 fn test_truncate_table_restrict() {
7759 let builder = PostgresQueryBuilder::new();
7760 let mut stmt = Query::truncate_table();
7761 stmt.table("users").restrict();
7762
7763 let (sql, values) = builder.build_truncate_table(&stmt);
7764 assert_eq!(sql, r#"TRUNCATE TABLE "users" RESTRICT"#);
7765 assert_eq!(values.len(), 0);
7766 }
7767
7768 #[test]
7769 fn test_truncate_table_restart_identity_cascade() {
7770 let builder = PostgresQueryBuilder::new();
7771 let mut stmt = Query::truncate_table();
7772 stmt.table("users").restart_identity().cascade();
7773
7774 let (sql, values) = builder.build_truncate_table(&stmt);
7775 assert_eq!(sql, r#"TRUNCATE TABLE "users" RESTART IDENTITY CASCADE"#);
7776 assert_eq!(values.len(), 0);
7777 }
7778
7779 #[test]
7780 fn test_create_trigger_basic() {
7781 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
7782
7783 let builder = PostgresQueryBuilder::new();
7784 let mut stmt = Query::create_trigger();
7785 stmt.name("audit_log")
7786 .timing(TriggerTiming::After)
7787 .event(TriggerEvent::Insert)
7788 .on_table("users")
7789 .for_each(TriggerScope::Row)
7790 .execute_function("log_user_insert");
7791
7792 let (sql, values) = builder.build_create_trigger(&stmt);
7793 assert_eq!(
7794 sql,
7795 r#"CREATE TRIGGER "audit_log" AFTER INSERT ON "users" FOR EACH ROW EXECUTE FUNCTION "log_user_insert"()"#
7796 );
7797 assert_eq!(values.len(), 0);
7798 }
7799
7800 #[test]
7801 fn test_create_trigger_before_update() {
7802 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
7803
7804 let builder = PostgresQueryBuilder::new();
7805 let mut stmt = Query::create_trigger();
7806 stmt.name("update_timestamp")
7807 .timing(TriggerTiming::Before)
7808 .event(TriggerEvent::Update { columns: None })
7809 .on_table("users")
7810 .for_each(TriggerScope::Row)
7811 .execute_function("update_modified_at");
7812
7813 let (sql, values) = builder.build_create_trigger(&stmt);
7814 assert_eq!(
7815 sql,
7816 r#"CREATE TRIGGER "update_timestamp" BEFORE UPDATE ON "users" FOR EACH ROW EXECUTE FUNCTION "update_modified_at"()"#
7817 );
7818 assert_eq!(values.len(), 0);
7819 }
7820
7821 #[test]
7822 fn test_create_trigger_delete_for_statement() {
7823 use crate::types::{TriggerEvent, TriggerScope, TriggerTiming};
7824
7825 let builder = PostgresQueryBuilder::new();
7826 let mut stmt = Query::create_trigger();
7827 stmt.name("audit_delete")
7828 .timing(TriggerTiming::After)
7829 .event(TriggerEvent::Delete)
7830 .on_table("users")
7831 .for_each(TriggerScope::Statement)
7832 .execute_function("log_bulk_delete");
7833
7834 let (sql, values) = builder.build_create_trigger(&stmt);
7835 assert_eq!(
7836 sql,
7837 r#"CREATE TRIGGER "audit_delete" AFTER DELETE ON "users" FOR EACH STATEMENT EXECUTE FUNCTION "log_bulk_delete"()"#
7838 );
7839 assert_eq!(values.len(), 0);
7840 }
7841
7842 #[test]
7843 fn test_drop_trigger_basic() {
7844 let builder = PostgresQueryBuilder::new();
7845 let mut stmt = Query::drop_trigger();
7846 stmt.name("audit_log").on_table("users");
7847
7848 let (sql, values) = builder.build_drop_trigger(&stmt);
7849 assert_eq!(sql, r#"DROP TRIGGER "audit_log" ON "users""#);
7850 assert_eq!(values.len(), 0);
7851 }
7852
7853 #[test]
7854 fn test_drop_trigger_if_exists() {
7855 let builder = PostgresQueryBuilder::new();
7856 let mut stmt = Query::drop_trigger();
7857 stmt.name("audit_log").on_table("users").if_exists();
7858
7859 let (sql, values) = builder.build_drop_trigger(&stmt);
7860 assert_eq!(sql, r#"DROP TRIGGER IF EXISTS "audit_log" ON "users""#);
7861 assert_eq!(values.len(), 0);
7862 }
7863
7864 #[test]
7865 fn test_drop_trigger_cascade() {
7866 let builder = PostgresQueryBuilder::new();
7867 let mut stmt = Query::drop_trigger();
7868 stmt.name("audit_log").on_table("users").cascade();
7869
7870 let (sql, values) = builder.build_drop_trigger(&stmt);
7871 assert_eq!(sql, r#"DROP TRIGGER "audit_log" ON "users" CASCADE"#);
7872 assert_eq!(values.len(), 0);
7873 }
7874
7875 #[test]
7877 fn test_create_function_basic() {
7878 use crate::types::function::FunctionLanguage;
7879
7880 let builder = PostgresQueryBuilder::new();
7881 let mut stmt = Query::create_function();
7882 stmt.name("my_func")
7883 .returns("integer")
7884 .language(FunctionLanguage::Sql)
7885 .body("SELECT 1");
7886
7887 let (sql, values) = builder.build_create_function(&stmt);
7888 assert_eq!(
7889 sql,
7890 r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL AS $$SELECT 1$$"#
7891 );
7892 assert_eq!(values.len(), 0);
7893 }
7894
7895 #[test]
7896 fn test_create_function_or_replace() {
7897 use crate::types::function::FunctionLanguage;
7898
7899 let builder = PostgresQueryBuilder::new();
7900 let mut stmt = Query::create_function();
7901 stmt.name("my_func")
7902 .or_replace()
7903 .returns("integer")
7904 .language(FunctionLanguage::Sql)
7905 .body("SELECT 1");
7906
7907 let (sql, values) = builder.build_create_function(&stmt);
7908 assert_eq!(
7909 sql,
7910 r#"CREATE OR REPLACE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL AS $$SELECT 1$$"#
7911 );
7912 assert_eq!(values.len(), 0);
7913 }
7914
7915 #[test]
7916 fn test_create_function_with_parameters() {
7917 use crate::types::function::FunctionLanguage;
7918
7919 let builder = PostgresQueryBuilder::new();
7920 let mut stmt = Query::create_function();
7921 stmt.name("add_numbers")
7922 .add_parameter("a", "integer")
7923 .add_parameter("b", "integer")
7924 .returns("integer")
7925 .language(FunctionLanguage::Sql)
7926 .body("SELECT $1 + $2");
7927
7928 let (sql, values) = builder.build_create_function(&stmt);
7929 assert_eq!(
7930 sql,
7931 r#"CREATE FUNCTION "add_numbers"("a" integer, "b" integer) RETURNS integer LANGUAGE SQL AS $$SELECT $1 + $2$$"#
7932 );
7933 assert_eq!(values.len(), 0);
7934 }
7935
7936 #[test]
7937 fn test_create_function_with_behavior() {
7938 use crate::types::function::{FunctionBehavior, FunctionLanguage};
7939
7940 let builder = PostgresQueryBuilder::new();
7941 let mut stmt = Query::create_function();
7942 stmt.name("my_func")
7943 .returns("integer")
7944 .language(FunctionLanguage::Sql)
7945 .behavior(FunctionBehavior::Immutable)
7946 .body("SELECT 1");
7947
7948 let (sql, values) = builder.build_create_function(&stmt);
7949 assert_eq!(
7950 sql,
7951 r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL IMMUTABLE AS $$SELECT 1$$"#
7952 );
7953 assert_eq!(values.len(), 0);
7954 }
7955
7956 #[test]
7957 fn test_create_function_with_security() {
7958 use crate::types::function::{FunctionLanguage, FunctionSecurity};
7959
7960 let builder = PostgresQueryBuilder::new();
7961 let mut stmt = Query::create_function();
7962 stmt.name("my_func")
7963 .returns("integer")
7964 .language(FunctionLanguage::Sql)
7965 .security(FunctionSecurity::Definer)
7966 .body("SELECT 1");
7967
7968 let (sql, values) = builder.build_create_function(&stmt);
7969 assert_eq!(
7970 sql,
7971 r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL SECURITY DEFINER AS $$SELECT 1$$"#
7972 );
7973 assert_eq!(values.len(), 0);
7974 }
7975
7976 #[test]
7977 fn test_create_function_plpgsql() {
7978 use crate::types::function::FunctionLanguage;
7979
7980 let builder = PostgresQueryBuilder::new();
7981 let mut stmt = Query::create_function();
7982 stmt.name("increment")
7983 .add_parameter("val", "integer")
7984 .returns("integer")
7985 .language(FunctionLanguage::PlPgSql)
7986 .body("BEGIN RETURN val + 1; END;");
7987
7988 let (sql, values) = builder.build_create_function(&stmt);
7989 assert_eq!(
7990 sql,
7991 r#"CREATE FUNCTION "increment"("val" integer) RETURNS integer LANGUAGE PLPGSQL AS $$BEGIN RETURN val + 1; END;$$"#
7992 );
7993 assert_eq!(values.len(), 0);
7994 }
7995
7996 #[test]
7997 fn test_create_function_all_options() {
7998 use crate::types::function::{FunctionBehavior, FunctionLanguage, FunctionSecurity};
7999
8000 let builder = PostgresQueryBuilder::new();
8001 let mut stmt = Query::create_function();
8002 stmt.name("complex_func")
8003 .or_replace()
8004 .add_parameter("a", "integer")
8005 .add_parameter("b", "text")
8006 .returns("integer")
8007 .language(FunctionLanguage::PlPgSql)
8008 .behavior(FunctionBehavior::Stable)
8009 .security(FunctionSecurity::Definer)
8010 .body("BEGIN RETURN a + LENGTH(b); END;");
8011
8012 let (sql, values) = builder.build_create_function(&stmt);
8013 assert_eq!(
8014 sql,
8015 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;$$"#
8016 );
8017 assert_eq!(values.len(), 0);
8018 }
8019
8020 #[test]
8022 fn test_alter_function_rename_to() {
8023 let builder = PostgresQueryBuilder::new();
8024 let mut stmt = Query::alter_function();
8025 stmt.name("my_func").rename_to("new_func");
8026
8027 let (sql, values) = builder.build_alter_function(&stmt);
8028 assert_eq!(sql, r#"ALTER FUNCTION "my_func" RENAME TO "new_func""#);
8029 assert_eq!(values.len(), 0);
8030 }
8031
8032 #[test]
8033 fn test_alter_function_owner_to() {
8034 let builder = PostgresQueryBuilder::new();
8035 let mut stmt = Query::alter_function();
8036 stmt.name("my_func").owner_to("new_owner");
8037
8038 let (sql, values) = builder.build_alter_function(&stmt);
8039 assert_eq!(sql, r#"ALTER FUNCTION "my_func" OWNER TO "new_owner""#);
8040 assert_eq!(values.len(), 0);
8041 }
8042
8043 #[test]
8044 fn test_alter_function_set_schema() {
8045 let builder = PostgresQueryBuilder::new();
8046 let mut stmt = Query::alter_function();
8047 stmt.name("my_func").set_schema("new_schema");
8048
8049 let (sql, values) = builder.build_alter_function(&stmt);
8050 assert_eq!(sql, r#"ALTER FUNCTION "my_func" SET SCHEMA "new_schema""#);
8051 assert_eq!(values.len(), 0);
8052 }
8053
8054 #[test]
8055 fn test_alter_function_set_behavior_immutable() {
8056 use crate::types::function::FunctionBehavior;
8057
8058 let builder = PostgresQueryBuilder::new();
8059 let mut stmt = Query::alter_function();
8060 stmt.name("my_func")
8061 .set_behavior(FunctionBehavior::Immutable);
8062
8063 let (sql, values) = builder.build_alter_function(&stmt);
8064 assert_eq!(sql, r#"ALTER FUNCTION "my_func" IMMUTABLE"#);
8065 assert_eq!(values.len(), 0);
8066 }
8067
8068 #[test]
8069 fn test_alter_function_set_security_definer() {
8070 use crate::types::function::FunctionSecurity;
8071
8072 let builder = PostgresQueryBuilder::new();
8073 let mut stmt = Query::alter_function();
8074 stmt.name("my_func").set_security(FunctionSecurity::Definer);
8075
8076 let (sql, values) = builder.build_alter_function(&stmt);
8077 assert_eq!(sql, r#"ALTER FUNCTION "my_func" SECURITY DEFINER"#);
8078 assert_eq!(values.len(), 0);
8079 }
8080
8081 #[test]
8082 fn test_alter_function_with_parameters() {
8083 let builder = PostgresQueryBuilder::new();
8084 let mut stmt = Query::alter_function();
8085 stmt.name("my_func")
8086 .add_parameter("a", "integer")
8087 .add_parameter("b", "text")
8088 .rename_to("new_func");
8089
8090 let (sql, values) = builder.build_alter_function(&stmt);
8091 assert_eq!(
8092 sql,
8093 r#"ALTER FUNCTION "my_func"("a" integer, "b" text) RENAME TO "new_func""#
8094 );
8095 assert_eq!(values.len(), 0);
8096 }
8097
8098 #[test]
8100 fn test_drop_function_basic() {
8101 let builder = PostgresQueryBuilder::new();
8102 let mut stmt = Query::drop_function();
8103 stmt.name("my_func");
8104
8105 let (sql, values) = builder.build_drop_function(&stmt);
8106 assert_eq!(sql, r#"DROP FUNCTION "my_func""#);
8107 assert_eq!(values.len(), 0);
8108 }
8109
8110 #[test]
8111 fn test_drop_function_if_exists() {
8112 let builder = PostgresQueryBuilder::new();
8113 let mut stmt = Query::drop_function();
8114 stmt.name("my_func").if_exists();
8115
8116 let (sql, values) = builder.build_drop_function(&stmt);
8117 assert_eq!(sql, r#"DROP FUNCTION IF EXISTS "my_func""#);
8118 assert_eq!(values.len(), 0);
8119 }
8120
8121 #[test]
8122 fn test_drop_function_cascade() {
8123 let builder = PostgresQueryBuilder::new();
8124 let mut stmt = Query::drop_function();
8125 stmt.name("my_func").cascade();
8126
8127 let (sql, values) = builder.build_drop_function(&stmt);
8128 assert_eq!(sql, r#"DROP FUNCTION "my_func" CASCADE"#);
8129 assert_eq!(values.len(), 0);
8130 }
8131
8132 #[test]
8133 fn test_drop_function_with_parameters() {
8134 let builder = PostgresQueryBuilder::new();
8135 let mut stmt = Query::drop_function();
8136 stmt.name("my_func")
8137 .add_parameter("", "integer")
8138 .add_parameter("", "text");
8139
8140 let (sql, values) = builder.build_drop_function(&stmt);
8141 assert_eq!(sql, r#"DROP FUNCTION "my_func"(integer, text)"#);
8142 assert_eq!(values.len(), 0);
8143 }
8144
8145 #[test]
8146 fn test_drop_function_all_options() {
8147 let builder = PostgresQueryBuilder::new();
8148 let mut stmt = Query::drop_function();
8149 stmt.name("my_func")
8150 .if_exists()
8151 .add_parameter("", "integer")
8152 .cascade();
8153
8154 let (sql, values) = builder.build_drop_function(&stmt);
8155 assert_eq!(sql, r#"DROP FUNCTION IF EXISTS "my_func"(integer) CASCADE"#);
8156 assert_eq!(values.len(), 0);
8157 }
8158
8159 #[test]
8161 fn test_create_procedure_basic() {
8162 use crate::types::function::FunctionLanguage;
8163
8164 let builder = PostgresQueryBuilder::new();
8165 let mut stmt = Query::create_procedure();
8166 stmt.name("my_proc")
8167 .language(FunctionLanguage::Sql)
8168 .body("SELECT 1");
8169
8170 let (sql, values) = builder.build_create_procedure(&stmt);
8171 assert_eq!(
8172 sql,
8173 r#"CREATE PROCEDURE "my_proc"() LANGUAGE SQL AS $$SELECT 1$$"#
8174 );
8175 assert_eq!(values.len(), 0);
8176 }
8177
8178 #[test]
8179 fn test_create_procedure_or_replace() {
8180 use crate::types::function::FunctionLanguage;
8181
8182 let builder = PostgresQueryBuilder::new();
8183 let mut stmt = Query::create_procedure();
8184 stmt.name("my_proc")
8185 .or_replace()
8186 .language(FunctionLanguage::PlPgSql)
8187 .body("BEGIN SELECT 1; END;");
8188
8189 let (sql, values) = builder.build_create_procedure(&stmt);
8190 assert_eq!(
8191 sql,
8192 r#"CREATE OR REPLACE PROCEDURE "my_proc"() LANGUAGE PLPGSQL AS $$BEGIN SELECT 1; END;$$"#
8193 );
8194 assert_eq!(values.len(), 0);
8195 }
8196
8197 #[test]
8198 fn test_create_procedure_with_parameters() {
8199 use crate::types::function::FunctionLanguage;
8200
8201 let builder = PostgresQueryBuilder::new();
8202 let mut stmt = Query::create_procedure();
8203 stmt.name("my_proc")
8204 .add_parameter("a", "integer")
8205 .add_parameter("b", "text")
8206 .language(FunctionLanguage::PlPgSql)
8207 .body("BEGIN INSERT INTO log VALUES (a, b); END;");
8208
8209 let (sql, values) = builder.build_create_procedure(&stmt);
8210 assert_eq!(
8211 sql,
8212 r#"CREATE PROCEDURE "my_proc"("a" integer, "b" text) LANGUAGE PLPGSQL AS $$BEGIN INSERT INTO log VALUES (a, b); END;$$"#
8213 );
8214 assert_eq!(values.len(), 0);
8215 }
8216
8217 #[test]
8218 fn test_create_procedure_with_behavior() {
8219 use crate::types::function::{FunctionBehavior, FunctionLanguage};
8220
8221 let builder = PostgresQueryBuilder::new();
8222 let mut stmt = Query::create_procedure();
8223 stmt.name("my_proc")
8224 .language(FunctionLanguage::Sql)
8225 .behavior(FunctionBehavior::Immutable)
8226 .body("SELECT 1");
8227
8228 let (sql, values) = builder.build_create_procedure(&stmt);
8229 assert_eq!(
8230 sql,
8231 r#"CREATE PROCEDURE "my_proc"() LANGUAGE SQL IMMUTABLE AS $$SELECT 1$$"#
8232 );
8233 assert_eq!(values.len(), 0);
8234 }
8235
8236 #[test]
8237 fn test_create_procedure_with_security() {
8238 use crate::types::function::{FunctionLanguage, FunctionSecurity};
8239
8240 let builder = PostgresQueryBuilder::new();
8241 let mut stmt = Query::create_procedure();
8242 stmt.name("my_proc")
8243 .language(FunctionLanguage::Sql)
8244 .security(FunctionSecurity::Definer)
8245 .body("SELECT 1");
8246
8247 let (sql, values) = builder.build_create_procedure(&stmt);
8248 assert_eq!(
8249 sql,
8250 r#"CREATE PROCEDURE "my_proc"() LANGUAGE SQL SECURITY DEFINER AS $$SELECT 1$$"#
8251 );
8252 assert_eq!(values.len(), 0);
8253 }
8254
8255 #[test]
8256 fn test_create_procedure_all_options() {
8257 use crate::types::function::{FunctionBehavior, FunctionLanguage, FunctionSecurity};
8258
8259 let builder = PostgresQueryBuilder::new();
8260 let mut stmt = Query::create_procedure();
8261 stmt.name("my_proc")
8262 .or_replace()
8263 .add_parameter("a", "integer")
8264 .add_parameter("b", "text")
8265 .language(FunctionLanguage::PlPgSql)
8266 .behavior(FunctionBehavior::Immutable)
8267 .security(FunctionSecurity::Definer)
8268 .body("BEGIN INSERT INTO log VALUES (a, b); END;");
8269
8270 let (sql, values) = builder.build_create_procedure(&stmt);
8271 assert_eq!(
8272 sql,
8273 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;$$"#
8274 );
8275 assert_eq!(values.len(), 0);
8276 }
8277
8278 #[test]
8279 fn test_alter_procedure_rename_to() {
8280 let builder = PostgresQueryBuilder::new();
8281 let mut stmt = Query::alter_procedure();
8282 stmt.name("my_proc").rename_to("new_proc");
8283
8284 let (sql, values) = builder.build_alter_procedure(&stmt);
8285 assert_eq!(sql, r#"ALTER PROCEDURE "my_proc" RENAME TO "new_proc""#);
8286 assert_eq!(values.len(), 0);
8287 }
8288
8289 #[test]
8290 fn test_alter_procedure_owner_to() {
8291 let builder = PostgresQueryBuilder::new();
8292 let mut stmt = Query::alter_procedure();
8293 stmt.name("my_proc").owner_to("new_owner");
8294
8295 let (sql, values) = builder.build_alter_procedure(&stmt);
8296 assert_eq!(sql, r#"ALTER PROCEDURE "my_proc" OWNER TO "new_owner""#);
8297 assert_eq!(values.len(), 0);
8298 }
8299
8300 #[test]
8301 fn test_alter_procedure_set_schema() {
8302 let builder = PostgresQueryBuilder::new();
8303 let mut stmt = Query::alter_procedure();
8304 stmt.name("my_proc").set_schema("new_schema");
8305
8306 let (sql, values) = builder.build_alter_procedure(&stmt);
8307 assert_eq!(sql, r#"ALTER PROCEDURE "my_proc" SET SCHEMA "new_schema""#);
8308 assert_eq!(values.len(), 0);
8309 }
8310
8311 #[test]
8312 fn test_alter_procedure_with_signature() {
8313 let builder = PostgresQueryBuilder::new();
8314 let mut stmt = Query::alter_procedure();
8315 stmt.name("my_proc")
8316 .add_parameter("a", "integer")
8317 .rename_to("new_proc");
8318
8319 let (sql, values) = builder.build_alter_procedure(&stmt);
8320 assert_eq!(
8321 sql,
8322 r#"ALTER PROCEDURE "my_proc"("a" integer) RENAME TO "new_proc""#
8323 );
8324 assert_eq!(values.len(), 0);
8325 }
8326
8327 #[test]
8328 fn test_drop_procedure_basic() {
8329 let builder = PostgresQueryBuilder::new();
8330 let mut stmt = Query::drop_procedure();
8331 stmt.name("my_proc");
8332
8333 let (sql, values) = builder.build_drop_procedure(&stmt);
8334 assert_eq!(sql, r#"DROP PROCEDURE "my_proc""#);
8335 assert_eq!(values.len(), 0);
8336 }
8337
8338 #[test]
8339 fn test_drop_procedure_if_exists() {
8340 let builder = PostgresQueryBuilder::new();
8341 let mut stmt = Query::drop_procedure();
8342 stmt.name("my_proc").if_exists();
8343
8344 let (sql, values) = builder.build_drop_procedure(&stmt);
8345 assert_eq!(sql, r#"DROP PROCEDURE IF EXISTS "my_proc""#);
8346 assert_eq!(values.len(), 0);
8347 }
8348
8349 #[test]
8350 fn test_drop_procedure_cascade() {
8351 let builder = PostgresQueryBuilder::new();
8352 let mut stmt = Query::drop_procedure();
8353 stmt.name("my_proc").cascade();
8354
8355 let (sql, values) = builder.build_drop_procedure(&stmt);
8356 assert_eq!(sql, r#"DROP PROCEDURE "my_proc" CASCADE"#);
8357 assert_eq!(values.len(), 0);
8358 }
8359
8360 #[test]
8361 fn test_drop_procedure_with_signature() {
8362 let builder = PostgresQueryBuilder::new();
8363 let mut stmt = Query::drop_procedure();
8364 stmt.name("my_proc").add_parameter("", "integer");
8365
8366 let (sql, values) = builder.build_drop_procedure(&stmt);
8367 assert_eq!(sql, r#"DROP PROCEDURE "my_proc"(integer)"#);
8368 assert_eq!(values.len(), 0);
8369 }
8370
8371 #[test]
8372 fn test_drop_procedure_all_options() {
8373 let builder = PostgresQueryBuilder::new();
8374 let mut stmt = Query::drop_procedure();
8375 stmt.name("my_proc")
8376 .if_exists()
8377 .add_parameter("", "integer")
8378 .cascade();
8379
8380 let (sql, values) = builder.build_drop_procedure(&stmt);
8381 assert_eq!(
8382 sql,
8383 r#"DROP PROCEDURE IF EXISTS "my_proc"(integer) CASCADE"#
8384 );
8385 assert_eq!(values.len(), 0);
8386 }
8387
8388 #[test]
8390 fn test_create_type_enum() {
8391 let builder = PostgresQueryBuilder::new();
8392 let mut stmt = Query::create_type();
8393 stmt.name("mood")
8394 .as_enum(vec!["happy".to_string(), "sad".to_string()]);
8395
8396 let (sql, values) = builder.build_create_type(&stmt);
8397 assert_eq!(sql, r#"CREATE TYPE "mood" AS ENUM ('happy', 'sad')"#);
8398 assert_eq!(values.len(), 0);
8399 }
8400
8401 #[test]
8402 fn test_create_type_enum_with_single_quote() {
8403 let builder = PostgresQueryBuilder::new();
8404 let mut stmt = Query::create_type();
8405 stmt.name("test").as_enum(vec!["it's".to_string()]);
8406
8407 let (sql, values) = builder.build_create_type(&stmt);
8408 assert_eq!(sql, r#"CREATE TYPE "test" AS ENUM ('it''s')"#);
8409 assert_eq!(values.len(), 0);
8410 }
8411
8412 #[test]
8413 fn test_create_type_composite() {
8414 let builder = PostgresQueryBuilder::new();
8415 let mut stmt = Query::create_type();
8416 stmt.name("address").as_composite(vec![
8417 ("street".to_string(), "text".to_string()),
8418 ("city".to_string(), "text".to_string()),
8419 ]);
8420
8421 let (sql, values) = builder.build_create_type(&stmt);
8422 assert_eq!(
8423 sql,
8424 r#"CREATE TYPE "address" AS ("street" text, "city" text)"#
8425 );
8426 assert_eq!(values.len(), 0);
8427 }
8428
8429 #[test]
8430 fn test_create_type_domain_minimal() {
8431 let builder = PostgresQueryBuilder::new();
8432 let mut stmt = Query::create_type();
8433 stmt.name("positive_int").as_domain("integer".to_string());
8434
8435 let (sql, values) = builder.build_create_type(&stmt);
8436 assert_eq!(sql, r#"CREATE TYPE "positive_int" AS integer"#);
8437 assert_eq!(values.len(), 0);
8438 }
8439
8440 #[test]
8441 fn test_create_type_domain_with_constraint() {
8442 let builder = PostgresQueryBuilder::new();
8443 let mut stmt = Query::create_type();
8444 stmt.name("positive_int")
8445 .as_domain("integer".to_string())
8446 .constraint(
8447 "check_positive".to_string(),
8448 "CHECK (VALUE > 0)".to_string(),
8449 );
8450
8451 let (sql, values) = builder.build_create_type(&stmt);
8452 assert_eq!(
8453 sql,
8454 r#"CREATE TYPE "positive_int" AS integer CHECK (VALUE > 0)"#
8455 );
8456 assert_eq!(values.len(), 0);
8457 }
8458
8459 #[test]
8460 fn test_create_type_domain_with_default() {
8461 let builder = PostgresQueryBuilder::new();
8462 let mut stmt = Query::create_type();
8463 stmt.name("my_domain")
8464 .as_domain("integer".to_string())
8465 .default_value("0".to_string());
8466
8467 let (sql, values) = builder.build_create_type(&stmt);
8468 assert_eq!(sql, r#"CREATE TYPE "my_domain" AS integer DEFAULT 0"#);
8469 assert_eq!(values.len(), 0);
8470 }
8471
8472 #[test]
8473 fn test_create_type_domain_not_null() {
8474 let builder = PostgresQueryBuilder::new();
8475 let mut stmt = Query::create_type();
8476 stmt.name("my_domain")
8477 .as_domain("integer".to_string())
8478 .not_null();
8479
8480 let (sql, values) = builder.build_create_type(&stmt);
8481 assert_eq!(sql, r#"CREATE TYPE "my_domain" AS integer NOT NULL"#);
8482 assert_eq!(values.len(), 0);
8483 }
8484
8485 #[test]
8486 fn test_create_type_domain_full() {
8487 let builder = PostgresQueryBuilder::new();
8488 let mut stmt = Query::create_type();
8489 stmt.name("positive_int")
8490 .as_domain("integer".to_string())
8491 .default_value("1".to_string())
8492 .constraint(
8493 "check_positive".to_string(),
8494 "CHECK (VALUE > 0)".to_string(),
8495 )
8496 .not_null();
8497
8498 let (sql, values) = builder.build_create_type(&stmt);
8499 assert_eq!(
8500 sql,
8501 r#"CREATE TYPE "positive_int" AS integer DEFAULT 1 CHECK (VALUE > 0) NOT NULL"#
8502 );
8503 assert_eq!(values.len(), 0);
8504 }
8505
8506 #[test]
8507 fn test_create_type_range_minimal() {
8508 let builder = PostgresQueryBuilder::new();
8509 let mut stmt = Query::create_type();
8510 stmt.name("int_range").as_range("integer".to_string());
8511
8512 let (sql, values) = builder.build_create_type(&stmt);
8513 assert_eq!(
8514 sql,
8515 r#"CREATE TYPE "int_range" AS RANGE (SUBTYPE = integer)"#
8516 );
8517 assert_eq!(values.len(), 0);
8518 }
8519
8520 #[test]
8521 fn test_create_type_range_with_subtype_diff() {
8522 let builder = PostgresQueryBuilder::new();
8523 let mut stmt = Query::create_type();
8524 stmt.name("int_range")
8525 .as_range("integer".to_string())
8526 .subtype_diff("int4range_subdiff".to_string());
8527
8528 let (sql, values) = builder.build_create_type(&stmt);
8529 assert_eq!(
8530 sql,
8531 r#"CREATE TYPE "int_range" AS RANGE (SUBTYPE = integer, SUBTYPE_DIFF = int4range_subdiff)"#
8532 );
8533 assert_eq!(values.len(), 0);
8534 }
8535
8536 #[test]
8537 fn test_create_type_range_full() {
8538 let builder = PostgresQueryBuilder::new();
8539 let mut stmt = Query::create_type();
8540 stmt.name("int_range")
8541 .as_range("integer".to_string())
8542 .subtype_diff("int4range_subdiff".to_string())
8543 .canonical("int4range_canonical".to_string());
8544
8545 let (sql, values) = builder.build_create_type(&stmt);
8546 assert_eq!(
8547 sql,
8548 r#"CREATE TYPE "int_range" AS RANGE (SUBTYPE = integer, SUBTYPE_DIFF = int4range_subdiff, CANONICAL = int4range_canonical)"#
8549 );
8550 assert_eq!(values.len(), 0);
8551 }
8552
8553 #[test]
8555 fn test_alter_type_rename_to() {
8556 let builder = PostgresQueryBuilder::new();
8557 let mut stmt = Query::alter_type();
8558 stmt.name("old_name").rename_to("new_name");
8559
8560 let (sql, values) = builder.build_alter_type(&stmt);
8561 assert_eq!(sql, r#"ALTER TYPE "old_name" RENAME TO "new_name""#);
8562 assert_eq!(values.len(), 0);
8563 }
8564
8565 #[test]
8566 fn test_alter_type_owner_to() {
8567 let builder = PostgresQueryBuilder::new();
8568 let mut stmt = Query::alter_type();
8569 stmt.name("my_type").owner_to("new_owner");
8570
8571 let (sql, values) = builder.build_alter_type(&stmt);
8572 assert_eq!(sql, r#"ALTER TYPE "my_type" OWNER TO "new_owner""#);
8573 assert_eq!(values.len(), 0);
8574 }
8575
8576 #[test]
8577 fn test_alter_type_set_schema() {
8578 let builder = PostgresQueryBuilder::new();
8579 let mut stmt = Query::alter_type();
8580 stmt.name("my_type").set_schema("new_schema");
8581
8582 let (sql, values) = builder.build_alter_type(&stmt);
8583 assert_eq!(sql, r#"ALTER TYPE "my_type" SET SCHEMA "new_schema""#);
8584 assert_eq!(values.len(), 0);
8585 }
8586
8587 #[test]
8588 fn test_alter_type_add_value() {
8589 let builder = PostgresQueryBuilder::new();
8590 let mut stmt = Query::alter_type();
8591 stmt.name("mood").add_value("excited", None);
8592
8593 let (sql, values) = builder.build_alter_type(&stmt);
8594 assert_eq!(sql, r#"ALTER TYPE "mood" ADD VALUE 'excited'"#);
8595 assert_eq!(values.len(), 0);
8596 }
8597
8598 #[test]
8599 fn test_alter_type_add_value_before() {
8600 let builder = PostgresQueryBuilder::new();
8601 let mut stmt = Query::alter_type();
8602 stmt.name("mood").add_value("excited", Some("happy"));
8603
8604 let (sql, values) = builder.build_alter_type(&stmt);
8605 assert_eq!(
8606 sql,
8607 r#"ALTER TYPE "mood" ADD VALUE 'excited' BEFORE 'happy'"#
8608 );
8609 assert_eq!(values.len(), 0);
8610 }
8611
8612 #[test]
8613 fn test_alter_type_rename_value() {
8614 let builder = PostgresQueryBuilder::new();
8615 let mut stmt = Query::alter_type();
8616 stmt.name("mood").rename_value("happy", "joyful");
8617
8618 let (sql, values) = builder.build_alter_type(&stmt);
8619 assert_eq!(sql, r#"ALTER TYPE "mood" RENAME VALUE 'happy' TO 'joyful'"#);
8620 assert_eq!(values.len(), 0);
8621 }
8622
8623 #[test]
8624 fn test_alter_type_add_constraint() {
8625 let builder = PostgresQueryBuilder::new();
8626 let mut stmt = Query::alter_type();
8627 stmt.name("my_domain")
8628 .add_constraint("positive_check", "CHECK (VALUE > 0)");
8629
8630 let (sql, values) = builder.build_alter_type(&stmt);
8631 assert_eq!(
8632 sql,
8633 r#"ALTER TYPE "my_domain" ADD CONSTRAINT "positive_check" CHECK (VALUE > 0)"#
8634 );
8635 assert_eq!(values.len(), 0);
8636 }
8637
8638 #[test]
8639 fn test_alter_type_drop_constraint() {
8640 let builder = PostgresQueryBuilder::new();
8641 let mut stmt = Query::alter_type();
8642 stmt.name("my_domain")
8643 .drop_constraint("my_constraint", false);
8644
8645 let (sql, values) = builder.build_alter_type(&stmt);
8646 assert_eq!(
8647 sql,
8648 r#"ALTER TYPE "my_domain" DROP CONSTRAINT "my_constraint""#
8649 );
8650 assert_eq!(values.len(), 0);
8651 }
8652
8653 #[test]
8654 fn test_alter_type_drop_constraint_if_exists() {
8655 let builder = PostgresQueryBuilder::new();
8656 let mut stmt = Query::alter_type();
8657 stmt.name("my_domain")
8658 .drop_constraint("my_constraint", true);
8659
8660 let (sql, values) = builder.build_alter_type(&stmt);
8661 assert_eq!(
8662 sql,
8663 r#"ALTER TYPE "my_domain" DROP CONSTRAINT IF EXISTS "my_constraint""#
8664 );
8665 assert_eq!(values.len(), 0);
8666 }
8667
8668 #[test]
8669 fn test_alter_type_set_default() {
8670 let builder = PostgresQueryBuilder::new();
8671 let mut stmt = Query::alter_type();
8672 stmt.name("my_domain").set_default("0");
8673
8674 let (sql, values) = builder.build_alter_type(&stmt);
8675 assert_eq!(sql, r#"ALTER TYPE "my_domain" SET DEFAULT 0"#);
8676 assert_eq!(values.len(), 0);
8677 }
8678
8679 #[test]
8680 fn test_alter_type_drop_default() {
8681 let builder = PostgresQueryBuilder::new();
8682 let mut stmt = Query::alter_type();
8683 stmt.name("my_domain").drop_default();
8684
8685 let (sql, values) = builder.build_alter_type(&stmt);
8686 assert_eq!(sql, r#"ALTER TYPE "my_domain" DROP DEFAULT"#);
8687 assert_eq!(values.len(), 0);
8688 }
8689
8690 #[test]
8691 fn test_alter_type_set_not_null() {
8692 let builder = PostgresQueryBuilder::new();
8693 let mut stmt = Query::alter_type();
8694 stmt.name("my_domain").set_not_null();
8695
8696 let (sql, values) = builder.build_alter_type(&stmt);
8697 assert_eq!(sql, r#"ALTER TYPE "my_domain" SET NOT NULL"#);
8698 assert_eq!(values.len(), 0);
8699 }
8700
8701 #[test]
8702 fn test_alter_type_drop_not_null() {
8703 let builder = PostgresQueryBuilder::new();
8704 let mut stmt = Query::alter_type();
8705 stmt.name("my_domain").drop_not_null();
8706
8707 let (sql, values) = builder.build_alter_type(&stmt);
8708 assert_eq!(sql, r#"ALTER TYPE "my_domain" DROP NOT NULL"#);
8709 assert_eq!(values.len(), 0);
8710 }
8711
8712 #[test]
8714 fn test_drop_type_basic() {
8715 let builder = PostgresQueryBuilder::new();
8716 let mut stmt = Query::drop_type();
8717 stmt.name("my_type");
8718
8719 let (sql, values) = builder.build_drop_type(&stmt);
8720 assert_eq!(sql, r#"DROP TYPE "my_type""#);
8721 assert_eq!(values.len(), 0);
8722 }
8723
8724 #[test]
8725 fn test_drop_type_if_exists() {
8726 let builder = PostgresQueryBuilder::new();
8727 let mut stmt = Query::drop_type();
8728 stmt.name("my_type").if_exists();
8729
8730 let (sql, values) = builder.build_drop_type(&stmt);
8731 assert_eq!(sql, r#"DROP TYPE IF EXISTS "my_type""#);
8732 assert_eq!(values.len(), 0);
8733 }
8734
8735 #[test]
8736 fn test_drop_type_cascade() {
8737 let builder = PostgresQueryBuilder::new();
8738 let mut stmt = Query::drop_type();
8739 stmt.name("my_type").cascade();
8740
8741 let (sql, values) = builder.build_drop_type(&stmt);
8742 assert_eq!(sql, r#"DROP TYPE "my_type" CASCADE"#);
8743 assert_eq!(values.len(), 0);
8744 }
8745
8746 #[test]
8747 fn test_drop_type_restrict() {
8748 let builder = PostgresQueryBuilder::new();
8749 let mut stmt = Query::drop_type();
8750 stmt.name("my_type").restrict();
8751
8752 let (sql, values) = builder.build_drop_type(&stmt);
8753 assert_eq!(sql, r#"DROP TYPE "my_type" RESTRICT"#);
8754 assert_eq!(values.len(), 0);
8755 }
8756
8757 #[test]
8758 fn test_drop_type_all_options() {
8759 let builder = PostgresQueryBuilder::new();
8760 let mut stmt = Query::drop_type();
8761 stmt.name("my_type").if_exists().cascade();
8762
8763 let (sql, values) = builder.build_drop_type(&stmt);
8764 assert_eq!(sql, r#"DROP TYPE IF EXISTS "my_type" CASCADE"#);
8765 assert_eq!(values.len(), 0);
8766 }
8767
8768 #[test]
8770 #[should_panic(expected = "PostgreSQL users should use VACUUM ANALYZE")]
8771 fn test_optimize_table_panics() {
8772 let builder = PostgresQueryBuilder::new();
8773 let mut stmt = Query::optimize_table();
8774 stmt.table("users");
8775
8776 let _ = builder.build_optimize_table(&stmt);
8777 }
8778
8779 #[test]
8780 #[should_panic(expected = "not supported in PostgreSQL")]
8781 fn test_repair_table_panics() {
8782 let builder = PostgresQueryBuilder::new();
8783 let mut stmt = Query::repair_table();
8784 stmt.table("users");
8785
8786 let _ = builder.build_repair_table(&stmt);
8787 }
8788
8789 #[test]
8790 #[should_panic(expected = "not supported in PostgreSQL")]
8791 fn test_check_table_panics() {
8792 let builder = PostgresQueryBuilder::new();
8793 let mut stmt = Query::check_table();
8794 stmt.table("users");
8795
8796 let _ = builder.build_check_table(&stmt);
8797 }
8798
8799 #[test]
8802 fn test_grant_single_privilege_on_table() {
8803 use crate::dcl::{GrantStatement, Privilege};
8804
8805 let builder = PostgresQueryBuilder::new();
8806 let stmt = GrantStatement::new()
8807 .privilege(Privilege::Select)
8808 .on_table("users")
8809 .to("app_user");
8810
8811 let (sql, values) = builder.build_grant(&stmt);
8812 assert_eq!(sql, r#"GRANT SELECT ON TABLE "users" TO "app_user""#);
8813 assert!(values.is_empty());
8814 }
8815
8816 #[test]
8817 fn test_grant_multiple_privileges() {
8818 use crate::dcl::{GrantStatement, Privilege};
8819
8820 let builder = PostgresQueryBuilder::new();
8821 let stmt = GrantStatement::new()
8822 .privileges(vec![
8823 Privilege::Select,
8824 Privilege::Insert,
8825 Privilege::Update,
8826 ])
8827 .on_table("users")
8828 .to("app_user");
8829
8830 let (sql, values) = builder.build_grant(&stmt);
8831 assert_eq!(
8832 sql,
8833 r#"GRANT SELECT, INSERT, UPDATE ON TABLE "users" TO "app_user""#
8834 );
8835 assert!(values.is_empty());
8836 }
8837
8838 #[test]
8839 fn test_grant_multiple_objects() {
8840 use crate::dcl::{GrantStatement, ObjectType, Privilege};
8841
8842 let builder = PostgresQueryBuilder::new();
8843 let stmt = GrantStatement::new()
8844 .privilege(Privilege::Select)
8845 .object_type(ObjectType::Table)
8846 .object("users")
8847 .object("posts")
8848 .to("app_user");
8849
8850 let (sql, values) = builder.build_grant(&stmt);
8851 assert_eq!(
8852 sql,
8853 r#"GRANT SELECT ON TABLE "users", "posts" TO "app_user""#
8854 );
8855 assert!(values.is_empty());
8856 }
8857
8858 #[test]
8859 fn test_grant_multiple_grantees() {
8860 use crate::dcl::{GrantStatement, Grantee, Privilege};
8861
8862 let builder = PostgresQueryBuilder::new();
8863 let stmt = GrantStatement::new()
8864 .privilege(Privilege::Select)
8865 .on_table("users")
8866 .grantee(Grantee::role("app_user"))
8867 .grantee(Grantee::role("readonly_user"));
8868
8869 let (sql, values) = builder.build_grant(&stmt);
8870 assert_eq!(
8871 sql,
8872 r#"GRANT SELECT ON TABLE "users" TO "app_user", "readonly_user""#
8873 );
8874 assert!(values.is_empty());
8875 }
8876
8877 #[test]
8878 fn test_grant_with_grant_option() {
8879 use crate::dcl::{GrantStatement, Privilege};
8880
8881 let builder = PostgresQueryBuilder::new();
8882 let stmt = GrantStatement::new()
8883 .privilege(Privilege::Select)
8884 .on_table("users")
8885 .to("app_user")
8886 .with_grant_option(true);
8887
8888 let (sql, values) = builder.build_grant(&stmt);
8889 assert_eq!(
8890 sql,
8891 r#"GRANT SELECT ON TABLE "users" TO "app_user" WITH GRANT OPTION"#
8892 );
8893 assert!(values.is_empty());
8894 }
8895
8896 #[test]
8897 fn test_grant_with_granted_by() {
8898 use crate::dcl::{GrantStatement, Grantee, Privilege};
8899
8900 let builder = PostgresQueryBuilder::new();
8901 let stmt = GrantStatement::new()
8902 .privilege(Privilege::Select)
8903 .on_table("users")
8904 .to("app_user")
8905 .granted_by(Grantee::role("admin"));
8906
8907 let (sql, values) = builder.build_grant(&stmt);
8908 assert_eq!(
8909 sql,
8910 r#"GRANT SELECT ON TABLE "users" TO "app_user" GRANTED BY "admin""#
8911 );
8912 assert!(values.is_empty());
8913 }
8914
8915 #[test]
8916 fn test_grant_on_database() {
8917 use crate::dcl::{GrantStatement, Privilege};
8918
8919 let builder = PostgresQueryBuilder::new();
8920 let stmt = GrantStatement::new()
8921 .privilege(Privilege::Create)
8922 .on_database("mydb")
8923 .to("app_user");
8924
8925 let (sql, values) = builder.build_grant(&stmt);
8926 assert_eq!(sql, r#"GRANT CREATE ON DATABASE "mydb" TO "app_user""#);
8927 assert!(values.is_empty());
8928 }
8929
8930 #[test]
8931 fn test_grant_on_schema() {
8932 use crate::dcl::{GrantStatement, Privilege};
8933
8934 let builder = PostgresQueryBuilder::new();
8935 let stmt = GrantStatement::new()
8936 .privilege(Privilege::Usage)
8937 .on_schema("public")
8938 .to("app_user");
8939
8940 let (sql, values) = builder.build_grant(&stmt);
8941 assert_eq!(sql, r#"GRANT USAGE ON SCHEMA "public" TO "app_user""#);
8942 assert!(values.is_empty());
8943 }
8944
8945 #[test]
8946 fn test_grant_on_sequence() {
8947 use crate::dcl::{GrantStatement, Privilege};
8948
8949 let builder = PostgresQueryBuilder::new();
8950 let stmt = GrantStatement::new()
8951 .privilege(Privilege::Usage)
8952 .on_sequence("user_id_seq")
8953 .to("app_user");
8954
8955 let (sql, values) = builder.build_grant(&stmt);
8956 assert_eq!(
8957 sql,
8958 r#"GRANT USAGE ON SEQUENCE "user_id_seq" TO "app_user""#
8959 );
8960 assert!(values.is_empty());
8961 }
8962
8963 #[test]
8964 fn test_grant_all_privileges() {
8965 use crate::dcl::{GrantStatement, Privilege};
8966
8967 let builder = PostgresQueryBuilder::new();
8968 let stmt = GrantStatement::new()
8969 .privilege(Privilege::All)
8970 .on_table("users")
8971 .to("admin");
8972
8973 let (sql, values) = builder.build_grant(&stmt);
8974 assert_eq!(sql, r#"GRANT ALL PRIVILEGES ON TABLE "users" TO "admin""#);
8975 assert!(values.is_empty());
8976 }
8977
8978 #[test]
8979 fn test_grant_to_public() {
8980 use crate::dcl::{GrantStatement, Grantee, Privilege};
8981
8982 let builder = PostgresQueryBuilder::new();
8983 let stmt = GrantStatement::new()
8984 .privilege(Privilege::Select)
8985 .on_table("public_data")
8986 .grantee(Grantee::Public);
8987
8988 let (sql, values) = builder.build_grant(&stmt);
8989 assert_eq!(sql, r#"GRANT SELECT ON TABLE "public_data" TO PUBLIC"#);
8990 assert!(values.is_empty());
8991 }
8992
8993 #[test]
8994 fn test_grant_to_current_user() {
8995 use crate::dcl::{GrantStatement, Grantee, Privilege};
8996
8997 let builder = PostgresQueryBuilder::new();
8998 let stmt = GrantStatement::new()
8999 .privilege(Privilege::Select)
9000 .on_table("users")
9001 .grantee(Grantee::CurrentUser);
9002
9003 let (sql, values) = builder.build_grant(&stmt);
9004 assert_eq!(sql, r#"GRANT SELECT ON TABLE "users" TO CURRENT_USER"#);
9005 assert!(values.is_empty());
9006 }
9007
9008 #[test]
9009 fn test_grant_complex() {
9010 use crate::dcl::{GrantStatement, Grantee, Privilege};
9011
9012 let builder = PostgresQueryBuilder::new();
9013 let stmt = GrantStatement::new()
9014 .privileges(vec![
9015 Privilege::Select,
9016 Privilege::Insert,
9017 Privilege::Update,
9018 ])
9019 .on_table("users")
9020 .on_table("posts")
9021 .grantee(Grantee::role("app_user"))
9022 .grantee(Grantee::role("readonly_user"))
9023 .with_grant_option(true)
9024 .granted_by(Grantee::role("admin"));
9025
9026 let (sql, values) = builder.build_grant(&stmt);
9027 assert!(sql.starts_with("GRANT SELECT, INSERT, UPDATE ON TABLE"));
9028 assert!(sql.contains(r#""users", "posts""#));
9029 assert!(sql.contains(r#"TO "app_user", "readonly_user""#));
9030 assert!(sql.contains("WITH GRANT OPTION"));
9031 assert!(sql.contains(r#"GRANTED BY "admin""#));
9032 assert!(values.is_empty());
9033 }
9034
9035 #[test]
9038 fn test_revoke_single_privilege() {
9039 use crate::dcl::{Privilege, RevokeStatement};
9040
9041 let builder = PostgresQueryBuilder::new();
9042 let stmt = RevokeStatement::new()
9043 .privilege(Privilege::Insert)
9044 .from_table("users")
9045 .from("app_user");
9046
9047 let (sql, values) = builder.build_revoke(&stmt);
9048 assert_eq!(sql, r#"REVOKE INSERT ON TABLE "users" FROM "app_user""#);
9049 assert!(values.is_empty());
9050 }
9051
9052 #[test]
9053 fn test_revoke_multiple_privileges() {
9054 use crate::dcl::{Privilege, RevokeStatement};
9055
9056 let builder = PostgresQueryBuilder::new();
9057 let stmt = RevokeStatement::new()
9058 .privileges(vec![
9059 Privilege::Select,
9060 Privilege::Insert,
9061 Privilege::Update,
9062 ])
9063 .from_table("users")
9064 .from("app_user");
9065
9066 let (sql, values) = builder.build_revoke(&stmt);
9067 assert_eq!(
9068 sql,
9069 r#"REVOKE SELECT, INSERT, UPDATE ON TABLE "users" FROM "app_user""#
9070 );
9071 assert!(values.is_empty());
9072 }
9073
9074 #[test]
9075 fn test_revoke_with_cascade() {
9076 use crate::dcl::{Privilege, RevokeStatement};
9077
9078 let builder = PostgresQueryBuilder::new();
9079 let stmt = RevokeStatement::new()
9080 .privilege(Privilege::All)
9081 .from_table("users")
9082 .from("app_user")
9083 .cascade(true);
9084
9085 let (sql, values) = builder.build_revoke(&stmt);
9086 assert_eq!(
9087 sql,
9088 r#"REVOKE ALL PRIVILEGES ON TABLE "users" FROM "app_user" CASCADE"#
9089 );
9090 assert!(values.is_empty());
9091 }
9092
9093 #[test]
9094 fn test_revoke_grant_option_for() {
9095 use crate::dcl::{Privilege, RevokeStatement};
9096
9097 let builder = PostgresQueryBuilder::new();
9098 let stmt = RevokeStatement::new()
9099 .privilege(Privilege::Select)
9100 .from_table("users")
9101 .from("app_user")
9102 .grant_option_for(true);
9103
9104 let (sql, values) = builder.build_revoke(&stmt);
9105 assert_eq!(
9106 sql,
9107 r#"REVOKE GRANT OPTION FOR SELECT ON TABLE "users" FROM "app_user""#
9108 );
9109 assert!(values.is_empty());
9110 }
9111
9112 #[test]
9113 fn test_revoke_from_database() {
9114 use crate::dcl::{Privilege, RevokeStatement};
9115
9116 let builder = PostgresQueryBuilder::new();
9117 let stmt = RevokeStatement::new()
9118 .privilege(Privilege::Create)
9119 .from_database("mydb")
9120 .from("app_user");
9121
9122 let (sql, values) = builder.build_revoke(&stmt);
9123 assert_eq!(sql, r#"REVOKE CREATE ON DATABASE "mydb" FROM "app_user""#);
9124 assert!(values.is_empty());
9125 }
9126
9127 #[test]
9128 fn test_revoke_from_schema() {
9129 use crate::dcl::{Privilege, RevokeStatement};
9130
9131 let builder = PostgresQueryBuilder::new();
9132 let stmt = RevokeStatement::new()
9133 .privilege(Privilege::Usage)
9134 .from_schema("public")
9135 .from("app_user");
9136
9137 let (sql, values) = builder.build_revoke(&stmt);
9138 assert_eq!(sql, r#"REVOKE USAGE ON SCHEMA "public" FROM "app_user""#);
9139 assert!(values.is_empty());
9140 }
9141
9142 #[test]
9143 fn test_revoke_from_sequence() {
9144 use crate::dcl::{Privilege, RevokeStatement};
9145
9146 let builder = PostgresQueryBuilder::new();
9147 let stmt = RevokeStatement::new()
9148 .privilege(Privilege::Usage)
9149 .from_sequence("user_id_seq")
9150 .from("app_user");
9151
9152 let (sql, values) = builder.build_revoke(&stmt);
9153 assert_eq!(
9154 sql,
9155 r#"REVOKE USAGE ON SEQUENCE "user_id_seq" FROM "app_user""#
9156 );
9157 assert!(values.is_empty());
9158 }
9159
9160 #[test]
9161 fn test_revoke_from_public() {
9162 use crate::dcl::{Grantee, Privilege, RevokeStatement};
9163
9164 let builder = PostgresQueryBuilder::new();
9165 let stmt = RevokeStatement::new()
9166 .privilege(Privilege::Select)
9167 .from_table("public_data")
9168 .grantee(Grantee::Public);
9169
9170 let (sql, values) = builder.build_revoke(&stmt);
9171 assert_eq!(sql, r#"REVOKE SELECT ON TABLE "public_data" FROM PUBLIC"#);
9172 assert!(values.is_empty());
9173 }
9174
9175 #[test]
9176 fn test_revoke_from_current_user() {
9177 use crate::dcl::{Grantee, Privilege, RevokeStatement};
9178
9179 let builder = PostgresQueryBuilder::new();
9180 let stmt = RevokeStatement::new()
9181 .privilege(Privilege::Select)
9182 .from_table("users")
9183 .grantee(Grantee::CurrentUser);
9184
9185 let (sql, values) = builder.build_revoke(&stmt);
9186 assert_eq!(sql, r#"REVOKE SELECT ON TABLE "users" FROM CURRENT_USER"#);
9187 assert!(values.is_empty());
9188 }
9189
9190 #[test]
9191 fn test_revoke_complex() {
9192 use crate::dcl::{Grantee, Privilege, RevokeStatement};
9193
9194 let builder = PostgresQueryBuilder::new();
9195 let stmt = RevokeStatement::new()
9196 .privileges(vec![Privilege::Select, Privilege::Insert])
9197 .from_table("users")
9198 .from_table("posts")
9199 .grantee(Grantee::role("app_user"))
9200 .grantee(Grantee::role("readonly_user"))
9201 .cascade(true);
9202
9203 let (sql, values) = builder.build_revoke(&stmt);
9204 assert!(sql.starts_with("REVOKE SELECT, INSERT ON TABLE"));
9205 assert!(sql.contains(r#""users", "posts""#));
9206 assert!(sql.contains(r#"FROM "app_user", "readonly_user""#));
9207 assert!(sql.contains("CASCADE"));
9208 assert!(values.is_empty());
9209 }
9210
9211 #[test]
9212 fn test_create_role_simple() {
9213 use crate::dcl::CreateRoleStatement;
9214
9215 let builder = PostgresQueryBuilder::new();
9216 let stmt = CreateRoleStatement::new().role("developer");
9217
9218 let (sql, values) = builder.build_create_role(&stmt);
9219 assert_eq!(sql, r#"CREATE ROLE "developer""#);
9220 assert!(values.is_empty());
9221 }
9222
9223 #[test]
9224 fn test_create_role_with_login() {
9225 use crate::dcl::{CreateRoleStatement, RoleAttribute};
9226 use crate::value::Value;
9227
9228 let builder = PostgresQueryBuilder::new();
9229 let stmt = CreateRoleStatement::new()
9230 .role("app_user")
9231 .attribute(RoleAttribute::Login)
9232 .attribute(RoleAttribute::Password("secret".to_string()));
9233
9234 let (sql, values) = builder.build_create_role(&stmt);
9235 assert_eq!(sql, r#"CREATE ROLE "app_user" WITH LOGIN PASSWORD $1"#);
9236 assert_eq!(values.len(), 1);
9237 assert_eq!(
9238 values[0],
9239 Value::String(Some(Box::new("secret".to_string())))
9240 );
9241 }
9242
9243 #[test]
9244 fn test_create_role_with_multiple_attributes() {
9245 use crate::dcl::{CreateRoleStatement, RoleAttribute};
9246
9247 let builder = PostgresQueryBuilder::new();
9248 let stmt = CreateRoleStatement::new()
9249 .role("superuser")
9250 .attribute(RoleAttribute::SuperUser)
9251 .attribute(RoleAttribute::CreateDb)
9252 .attribute(RoleAttribute::CreateRole)
9253 .attribute(RoleAttribute::ConnectionLimit(10));
9254
9255 let (sql, values) = builder.build_create_role(&stmt);
9256 assert_eq!(
9257 sql,
9258 r#"CREATE ROLE "superuser" WITH SUPERUSER CREATEDB CREATEROLE CONNECTION LIMIT 10"#
9259 );
9260 assert!(values.is_empty());
9261 }
9262
9263 #[test]
9264 fn test_drop_role_simple() {
9265 use crate::dcl::DropRoleStatement;
9266
9267 let builder = PostgresQueryBuilder::new();
9268 let stmt = DropRoleStatement::new().role("old_role");
9269
9270 let (sql, values) = builder.build_drop_role(&stmt);
9271 assert_eq!(sql, r#"DROP ROLE "old_role""#);
9272 assert!(values.is_empty());
9273 }
9274
9275 #[test]
9276 fn test_drop_role_if_exists() {
9277 use crate::dcl::DropRoleStatement;
9278
9279 let builder = PostgresQueryBuilder::new();
9280 let stmt = DropRoleStatement::new().role("old_role").if_exists(true);
9281
9282 let (sql, values) = builder.build_drop_role(&stmt);
9283 assert_eq!(sql, r#"DROP ROLE IF EXISTS "old_role""#);
9284 assert!(values.is_empty());
9285 }
9286
9287 #[test]
9288 fn test_drop_role_multiple() {
9289 use crate::dcl::DropRoleStatement;
9290
9291 let builder = PostgresQueryBuilder::new();
9292 let stmt = DropRoleStatement::new()
9293 .role("role1")
9294 .role("role2")
9295 .role("role3");
9296
9297 let (sql, values) = builder.build_drop_role(&stmt);
9298 assert_eq!(sql, r#"DROP ROLE "role1", "role2", "role3""#);
9299 assert!(values.is_empty());
9300 }
9301
9302 #[test]
9303 fn test_alter_role_with_attributes() {
9304 use crate::dcl::{AlterRoleStatement, RoleAttribute};
9305
9306 let builder = PostgresQueryBuilder::new();
9307 let stmt = AlterRoleStatement::new()
9308 .role("developer")
9309 .attribute(RoleAttribute::NoLogin)
9310 .attribute(RoleAttribute::ConnectionLimit(5));
9311
9312 let (sql, values) = builder.build_alter_role(&stmt);
9313 assert_eq!(
9314 sql,
9315 r#"ALTER ROLE "developer" WITH NOLOGIN CONNECTION LIMIT 5"#
9316 );
9317 assert!(values.is_empty());
9318 }
9319
9320 #[test]
9321 fn test_alter_role_rename_to() {
9322 use crate::dcl::AlterRoleStatement;
9323
9324 let builder = PostgresQueryBuilder::new();
9325 let stmt = AlterRoleStatement::new()
9326 .role("old_name")
9327 .rename_to("new_name");
9328
9329 let (sql, values) = builder.build_alter_role(&stmt);
9330 assert_eq!(sql, r#"ALTER ROLE "old_name" RENAME TO "new_name""#);
9331 assert!(values.is_empty());
9332 }
9333
9334 #[test]
9336 fn test_create_user_basic() {
9337 use crate::dcl::CreateUserStatement;
9338
9339 let builder = PostgresQueryBuilder::new();
9340 let stmt = CreateUserStatement::new().user("app_user");
9341
9342 let (sql, values) = builder.build_create_user(&stmt);
9343 assert_eq!(sql, r#"CREATE ROLE "app_user" WITH LOGIN"#);
9344 assert!(values.is_empty());
9345 }
9346
9347 #[test]
9348 fn test_create_user_with_password() {
9349 use crate::dcl::{CreateUserStatement, RoleAttribute};
9350 use crate::value::Value;
9351
9352 let builder = PostgresQueryBuilder::new();
9353 let stmt = CreateUserStatement::new()
9354 .user("app_user")
9355 .attribute(RoleAttribute::Password("secret".to_string()));
9356
9357 let (sql, values) = builder.build_create_user(&stmt);
9358 assert_eq!(sql, r#"CREATE ROLE "app_user" WITH LOGIN PASSWORD $1"#);
9359 assert_eq!(values.len(), 1);
9360 assert_eq!(
9361 values[0],
9362 Value::String(Some(Box::new("secret".to_string())))
9363 );
9364 }
9365
9366 #[test]
9368 fn test_drop_user_basic() {
9369 use crate::dcl::DropUserStatement;
9370
9371 let builder = PostgresQueryBuilder::new();
9372 let stmt = DropUserStatement::new().user("app_user");
9373
9374 let (sql, values) = builder.build_drop_user(&stmt);
9375 assert_eq!(sql, r#"DROP ROLE "app_user""#);
9376 assert!(values.is_empty());
9377 }
9378
9379 #[test]
9380 fn test_drop_user_if_exists() {
9381 use crate::dcl::DropUserStatement;
9382
9383 let builder = PostgresQueryBuilder::new();
9384 let stmt = DropUserStatement::new().user("app_user").if_exists(true);
9385
9386 let (sql, values) = builder.build_drop_user(&stmt);
9387 assert_eq!(sql, r#"DROP ROLE IF EXISTS "app_user""#);
9388 assert!(values.is_empty());
9389 }
9390
9391 #[test]
9393 fn test_alter_user_basic() {
9394 use crate::dcl::{AlterUserStatement, RoleAttribute};
9395 use crate::value::Value;
9396
9397 let builder = PostgresQueryBuilder::new();
9398 let stmt = AlterUserStatement::new()
9399 .user("app_user")
9400 .attribute(RoleAttribute::Password("new_secret".to_string()));
9401
9402 let (sql, values) = builder.build_alter_user(&stmt);
9403 assert_eq!(sql, r#"ALTER ROLE "app_user" WITH PASSWORD $1"#);
9404 assert_eq!(values.len(), 1);
9405 assert_eq!(
9406 values[0],
9407 Value::String(Some(Box::new("new_secret".to_string())))
9408 );
9409 }
9410
9411 #[test]
9413 #[should_panic(expected = "RENAME USER is not supported by PostgreSQL")]
9414 fn test_rename_user_panics() {
9415 use crate::dcl::RenameUserStatement;
9416
9417 let builder = PostgresQueryBuilder::new();
9418 let stmt = RenameUserStatement::new().rename("old", "new");
9419
9420 builder.build_rename_user(&stmt);
9421 }
9422
9423 #[test]
9425 fn test_set_role_named() {
9426 use crate::dcl::{RoleTarget, SetRoleStatement};
9427
9428 let builder = PostgresQueryBuilder::new();
9429 let stmt = SetRoleStatement::new().role(RoleTarget::Named("admin".to_string()));
9430
9431 let (sql, values) = builder.build_set_role(&stmt);
9432 assert_eq!(sql, r#"SET ROLE "admin""#);
9433 assert!(values.is_empty());
9434 }
9435
9436 #[test]
9437 fn test_set_role_none() {
9438 use crate::dcl::{RoleTarget, SetRoleStatement};
9439
9440 let builder = PostgresQueryBuilder::new();
9441 let stmt = SetRoleStatement::new().role(RoleTarget::None);
9442
9443 let (sql, values) = builder.build_set_role(&stmt);
9444 assert_eq!(sql, "SET ROLE NONE");
9445 assert!(values.is_empty());
9446 }
9447
9448 #[test]
9449 #[should_panic(expected = "SET ROLE ALL is not supported by PostgreSQL")]
9450 fn test_set_role_all_panics() {
9451 use crate::dcl::{RoleTarget, SetRoleStatement};
9452
9453 let builder = PostgresQueryBuilder::new();
9454 let stmt = SetRoleStatement::new().role(RoleTarget::All);
9455
9456 builder.build_set_role(&stmt);
9457 }
9458
9459 #[test]
9461 fn test_reset_role() {
9462 use crate::dcl::ResetRoleStatement;
9463
9464 let builder = PostgresQueryBuilder::new();
9465 let stmt = ResetRoleStatement::new();
9466
9467 let (sql, values) = builder.build_reset_role(&stmt);
9468 assert_eq!(sql, "RESET ROLE");
9469 assert!(values.is_empty());
9470 }
9471
9472 #[test]
9474 #[should_panic(expected = "SET DEFAULT ROLE is not supported by PostgreSQL")]
9475 fn test_set_default_role_panics() {
9476 use crate::dcl::{DefaultRoleSpec, SetDefaultRoleStatement};
9477
9478 let builder = PostgresQueryBuilder::new();
9479 let stmt = SetDefaultRoleStatement::new()
9480 .roles(DefaultRoleSpec::All)
9481 .user("app_user");
9482
9483 builder.build_set_default_role(&stmt);
9484 }
9485
9486 #[rstest]
9489 fn test_as_enum_escapes_type_name_with_special_characters() {
9490 let builder = PostgresQueryBuilder::new();
9492 let mut stmt = Query::select();
9493 stmt.expr(Expr::val("active").as_enum(Alias::new("user\"status")))
9494 .from("users");
9495
9496 let (sql, _) = builder.build_select(&stmt);
9498
9499 assert!(sql.contains("::\"user\"\"status\""));
9501 }
9502
9503 #[rstest]
9504 fn test_cast_escapes_type_name_with_special_characters() {
9505 let builder = PostgresQueryBuilder::new();
9507 let mut stmt = Query::select();
9508 stmt.expr(Expr::col("age").cast_as(Alias::new("my\"type")))
9509 .from("users");
9510
9511 let (sql, _) = builder.build_select(&stmt);
9513
9514 assert!(sql.contains("CAST(\"age\" AS \"my\"\"type\")"));
9516 }
9517
9518 #[rstest]
9519 fn test_trigger_function_name_is_escaped() {
9520 let builder = PostgresQueryBuilder::new();
9522 let mut stmt = Query::create_trigger();
9523 stmt.name("test_trigger")
9524 .timing(crate::types::TriggerTiming::After)
9525 .event(crate::types::TriggerEvent::Insert)
9526 .on_table("users")
9527 .for_each(crate::types::TriggerScope::Row)
9528 .execute_function("my\"func");
9529
9530 let (sql, _) = builder.build_create_trigger(&stmt);
9532
9533 assert!(sql.contains("EXECUTE FUNCTION \"my\"\"func\"()"));
9535 }
9536
9537 #[rstest]
9538 fn test_as_enum_normal_type_name_is_quoted() {
9539 let builder = PostgresQueryBuilder::new();
9541 let mut stmt = Query::select();
9542 stmt.expr(Expr::val("active").as_enum(Alias::new("status")))
9543 .from("users");
9544
9545 let (sql, _) = builder.build_select(&stmt);
9547
9548 assert!(sql.contains("::\"status\""));
9550 }
9551
9552 #[rstest]
9553 fn test_cast_normal_type_name_is_quoted() {
9554 let builder = PostgresQueryBuilder::new();
9556 let mut stmt = Query::select();
9557 stmt.expr(Expr::col("age").cast_as(Alias::new("INTEGER")))
9558 .from("users");
9559
9560 let (sql, _) = builder.build_select(&stmt);
9562
9563 assert!(sql.contains("CAST(\"age\" AS \"INTEGER\")"));
9565 }
9566
9567 #[rstest]
9570 fn test_safe_delimiter_default_when_body_has_no_dollar_quotes() {
9571 let body = "BEGIN RETURN 1; END;";
9573
9574 let delimiter = generate_safe_dollar_quote_delimiter(body);
9576
9577 assert_eq!(delimiter, "$$");
9579 }
9580
9581 #[rstest]
9582 fn test_safe_delimiter_avoids_collision_with_dollar_dollar() {
9583 let body = "BEGIN $$ nested $$ END;";
9585
9586 let delimiter = generate_safe_dollar_quote_delimiter(body);
9588
9589 assert_ne!(
9591 delimiter, "$$",
9592 "Delimiter must not be $$ when body contains $$"
9593 );
9594 assert_eq!(delimiter, "$body_0$");
9595 }
9596
9597 #[rstest]
9598 fn test_safe_delimiter_injection_attempt_with_dollar_quotes() {
9599 let body = "$$ ; DROP TABLE users; --";
9601
9602 let delimiter = generate_safe_dollar_quote_delimiter(body);
9604
9605 assert_ne!(delimiter, "$$");
9607 let delimiters = collect_dollar_quote_delimiters(body);
9608 assert!(
9609 !delimiters.contains(&delimiter),
9610 "Generated delimiter must not conflict with any delimiter in body"
9611 );
9612 }
9613
9614 #[rstest]
9615 fn test_safe_delimiter_skips_collision_with_body_0() {
9616 let body = "BEGIN $$ test $body_0$ END;";
9618
9619 let delimiter = generate_safe_dollar_quote_delimiter(body);
9621
9622 assert_eq!(delimiter, "$body_1$");
9624 }
9625
9626 #[rstest]
9627 fn test_safe_delimiter_multiple_collisions() {
9628 let body = "$$ $body_0$ $body_1$";
9630
9631 let delimiter = generate_safe_dollar_quote_delimiter(body);
9633
9634 assert_eq!(delimiter, "$body_2$");
9636 }
9637
9638 #[rstest]
9639 fn test_safe_delimiter_ignores_dollar_amount_not_delimiter() {
9640 let body = "SELECT $100 + $200";
9642
9643 let delimiter = generate_safe_dollar_quote_delimiter(body);
9645
9646 assert_eq!(delimiter, "$$");
9648 }
9649
9650 #[rstest]
9651 fn test_safe_delimiter_empty_body() {
9652 let body = "";
9654
9655 let delimiter = generate_safe_dollar_quote_delimiter(body);
9657
9658 assert_eq!(delimiter, "$$");
9660 }
9661
9662 #[rstest]
9663 fn test_safe_delimiter_whitespace_only_body() {
9664 let body = " \t\n ";
9666
9667 let delimiter = generate_safe_dollar_quote_delimiter(body);
9669
9670 assert_eq!(delimiter, "$$");
9672 }
9673
9674 #[rstest]
9675 fn test_safe_delimiter_nested_dollar_quotes() {
9676 let body = "$inner$ SELECT 1 $inner$ $$ nested $$";
9678
9679 let delimiter = generate_safe_dollar_quote_delimiter(body);
9681
9682 assert_ne!(delimiter, "$$");
9684 assert_ne!(delimiter, "$inner$");
9685 assert_eq!(delimiter, "$body_0$");
9686 }
9687
9688 #[rstest]
9689 fn test_safe_delimiter_tag_style_delimiters() {
9690 let body = "$func$ BEGIN RETURN 1; END; $func$";
9692
9693 let delimiter = generate_safe_dollar_quote_delimiter(body);
9695
9696 assert_eq!(delimiter, "$$");
9698 }
9699
9700 #[rstest]
9703 fn test_collect_delimiters_empty_body() {
9704 let body = "";
9706
9707 let delimiters = collect_dollar_quote_delimiters(body);
9709
9710 assert!(delimiters.is_empty());
9712 }
9713
9714 #[rstest]
9715 fn test_collect_delimiters_no_dollar_signs() {
9716 let body = "SELECT 1 + 2";
9718
9719 let delimiters = collect_dollar_quote_delimiters(body);
9721
9722 assert!(delimiters.is_empty());
9724 }
9725
9726 #[rstest]
9727 fn test_collect_delimiters_dollar_amounts_are_not_delimiters() {
9728 let body = "SELECT $1 + $2";
9730
9731 let delimiters = collect_dollar_quote_delimiters(body);
9733
9734 assert!(delimiters.is_empty());
9736 }
9737
9738 #[rstest]
9739 fn test_collect_delimiters_finds_empty_tag() {
9740 let body = "$$ body content $$";
9742
9743 let delimiters = collect_dollar_quote_delimiters(body);
9745
9746 assert_eq!(delimiters.len(), 1);
9748 assert!(delimiters.contains("$$"));
9749 }
9750
9751 #[rstest]
9752 fn test_collect_delimiters_finds_named_tag() {
9753 let body = "$func$ body $func$";
9755
9756 let delimiters = collect_dollar_quote_delimiters(body);
9758
9759 assert_eq!(delimiters.len(), 1);
9761 assert!(delimiters.contains("$func$"));
9762 }
9763
9764 #[rstest]
9765 fn test_collect_delimiters_finds_multiple_tags() {
9766 let body = "$$ outer $inner$ nested $inner$ outer $$";
9768
9769 let delimiters = collect_dollar_quote_delimiters(body);
9771
9772 assert_eq!(delimiters.len(), 2);
9774 assert!(delimiters.contains("$$"));
9775 assert!(delimiters.contains("$inner$"));
9776 }
9777
9778 #[rstest]
9779 fn test_collect_delimiters_underscore_in_tag() {
9780 let body = "$my_tag$ content $my_tag$";
9782
9783 let delimiters = collect_dollar_quote_delimiters(body);
9785
9786 assert!(delimiters.contains("$my_tag$"));
9788 }
9789
9790 #[rstest]
9791 fn test_collect_delimiters_rejects_digit_start_tag() {
9792 let body = "$1tag$ content";
9794
9795 let delimiters = collect_dollar_quote_delimiters(body);
9797
9798 assert!(!delimiters.contains("$1tag$"));
9800 }
9801
9802 #[rstest]
9807 fn test_adjust_placeholder_offsets_basic() {
9808 let sql = "SELECT * FROM t WHERE a = $1 AND b = $2";
9810
9811 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 2, 3);
9813
9814 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $4 AND b = $5");
9816 }
9817
9818 #[rstest]
9819 fn test_adjust_placeholder_offsets_zero_offset() {
9820 let sql = "SELECT * FROM t WHERE a = $1";
9822
9823 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 1, 0);
9825
9826 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $1");
9828 }
9829
9830 #[rstest]
9831 fn test_adjust_placeholder_offsets_skips_quoted_identifiers() {
9832 let sql = r#"SELECT "col$1" FROM t WHERE a = $1"#;
9834
9835 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 1, 5);
9837
9838 assert_eq!(adjusted, r#"SELECT "col$1" FROM t WHERE a = $6"#);
9840 }
9841
9842 #[rstest]
9843 fn test_adjust_placeholder_offsets_skips_string_literals() {
9844 let sql = "SELECT * FROM t WHERE a = $1 AND b = 'price$1'";
9846
9847 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 1, 2);
9849
9850 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $3 AND b = 'price$1'");
9852 }
9853
9854 #[rstest]
9855 fn test_adjust_placeholder_offsets_no_collision() {
9856 let sql = "SELECT * FROM t WHERE a = $1 AND b = $2";
9858
9859 let adjusted = PostgresQueryBuilder::adjust_placeholder_offsets(sql, 2, 1);
9861
9862 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $2 AND b = $3");
9864 }
9865
9866 #[rstest]
9871 fn test_expr_as_renders_as_alias() {
9872 let builder = PostgresQueryBuilder::new();
9874 let mut stmt = Query::select();
9875 stmt.expr(Expr::col("name").expr_as("display_name"))
9876 .from("users");
9877
9878 let (sql, _) = builder.build_select(&stmt);
9880
9881 assert!(
9883 sql.contains("AS \"display_name\""),
9884 "Expected AS alias in SQL, got: {}",
9885 sql
9886 );
9887 assert!(
9888 !sql.contains("::\"display_name\""),
9889 "Should NOT contain type cast syntax, got: {}",
9890 sql
9891 );
9892 }
9893}