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