1pub mod check_violation_strategy;
2pub mod column;
3pub mod constraint;
4pub mod fk_orphan_strategy;
5pub mod foreign_key;
6pub mod index;
7pub mod names;
8pub mod pk_addition_strategy;
9pub mod primary_key;
10pub mod reference;
11pub mod str_or_bool;
12pub mod table;
13pub mod unique_strategy;
14
15pub use check_violation_strategy::CheckViolationStrategy;
16pub use column::{
17 ColumnDef, ColumnType, ComplexColumnType, EnumValues, NumValue, SimpleColumnType,
18};
19pub use constraint::{ConstraintKind, TableConstraint};
20pub use fk_orphan_strategy::ForeignKeyOrphanStrategy;
21pub use index::IndexDef;
22pub use names::{ColumnName, IndexName, TableName};
23pub use pk_addition_strategy::PrimaryKeyAdditionStrategy;
24pub use primary_key::PrimaryKeyDef;
25pub use reference::ReferenceAction;
26pub use str_or_bool::{DefaultValue, StrOrBoolOrArray, StringOrBool};
27pub use table::{TableDef, TableValidationError};
28pub use unique_strategy::{KeepPolicy, UniqueConstraintStrategy};
29
30#[cfg(test)]
31mod tests {
32 mod column {
33 use crate::schema::column::*;
34 use crate::schema::primary_key::PrimaryKeySyntax;
35 use crate::schema::str_or_bool::StrOrBoolOrArray;
36 use rstest::rstest;
37
38 #[test]
39 fn new_creates_minimal_column() {
40 let c = ColumnDef::new("id", ColumnType::Simple(SimpleColumnType::Integer), false);
41 assert_eq!(c.name.as_str(), "id");
42 assert!(!c.nullable);
43 assert!(c.primary_key.is_none());
44 assert!(c.unique.is_none());
45 assert!(c.index.is_none());
46 assert!(c.foreign_key.is_none());
47 assert!(c.default.is_none());
48 assert!(c.comment.is_none());
49 }
50
51 #[test]
52 fn builder_chain_sets_fields() {
53 let c = ColumnDef::new("email", ColumnType::Simple(SimpleColumnType::Text), false)
54 .unique(StrOrBoolOrArray::Bool(true))
55 .index(StrOrBoolOrArray::Bool(true))
56 .comment("user email");
57 assert!(c.unique.is_some());
58 assert!(c.index.is_some());
59 assert_eq!(c.comment.as_deref(), Some("user email"));
60 }
61
62 #[test]
63 fn builder_preserves_round_trip_with_existing_literal() {
64 let from_builder =
66 ColumnDef::new("name", ColumnType::Simple(SimpleColumnType::Text), false);
67 let from_literal = ColumnDef {
68 name: "name".into(),
69 r#type: ColumnType::Simple(SimpleColumnType::Text),
70 nullable: false,
71 default: None,
72 comment: None,
73 primary_key: None,
74 unique: None,
75 index: None,
76 foreign_key: None,
77 };
78 assert_eq!(from_builder, from_literal);
79 }
80
81 #[test]
82 fn primary_key_builder_accepts_existing_syntax_type() {
83 let c = ColumnDef::new("id", ColumnType::Simple(SimpleColumnType::Integer), false)
84 .primary_key(PrimaryKeySyntax::Bool(true));
85 assert_eq!(c.primary_key, Some(PrimaryKeySyntax::Bool(true)));
86 }
87
88 #[rstest]
89 #[case(SimpleColumnType::SmallInt, "i16")]
90 #[case(SimpleColumnType::Integer, "i32")]
91 #[case(SimpleColumnType::BigInt, "i64")]
92 #[case(SimpleColumnType::Real, "f32")]
93 #[case(SimpleColumnType::DoublePrecision, "f64")]
94 #[case(SimpleColumnType::Text, "String")]
95 #[case(SimpleColumnType::Boolean, "bool")]
96 #[case(SimpleColumnType::Date, "Date")]
97 #[case(SimpleColumnType::Time, "Time")]
98 #[case(SimpleColumnType::Timestamp, "DateTime")]
99 #[case(SimpleColumnType::Timestamptz, "DateTimeWithTimeZone")]
100 #[case(SimpleColumnType::Interval, "String")]
101 #[case(SimpleColumnType::Bytea, "Vec<u8>")]
102 #[case(SimpleColumnType::Uuid, "Uuid")]
103 #[case(SimpleColumnType::Json, "Json")]
104 #[case(SimpleColumnType::Inet, "String")]
106 #[case(SimpleColumnType::Cidr, "String")]
107 #[case(SimpleColumnType::Macaddr, "String")]
108 #[case(SimpleColumnType::Xml, "String")]
109 fn test_simple_column_type_to_rust_type_not_nullable(
110 #[case] column_type: SimpleColumnType,
111 #[case] expected: &str,
112 ) {
113 assert_eq!(
114 ColumnType::Simple(column_type).to_rust_type(false),
115 expected
116 );
117 }
118
119 #[rstest]
120 #[case(SimpleColumnType::SmallInt, "Option<i16>")]
121 #[case(SimpleColumnType::Integer, "Option<i32>")]
122 #[case(SimpleColumnType::BigInt, "Option<i64>")]
123 #[case(SimpleColumnType::Real, "Option<f32>")]
124 #[case(SimpleColumnType::DoublePrecision, "Option<f64>")]
125 #[case(SimpleColumnType::Text, "Option<String>")]
126 #[case(SimpleColumnType::Boolean, "Option<bool>")]
127 #[case(SimpleColumnType::Date, "Option<Date>")]
128 #[case(SimpleColumnType::Time, "Option<Time>")]
129 #[case(SimpleColumnType::Timestamp, "Option<DateTime>")]
130 #[case(SimpleColumnType::Timestamptz, "Option<DateTimeWithTimeZone>")]
131 #[case(SimpleColumnType::Interval, "Option<String>")]
132 #[case(SimpleColumnType::Bytea, "Option<Vec<u8>>")]
133 #[case(SimpleColumnType::Uuid, "Option<Uuid>")]
134 #[case(SimpleColumnType::Json, "Option<Json>")]
135 #[case(SimpleColumnType::Inet, "Option<String>")]
137 #[case(SimpleColumnType::Cidr, "Option<String>")]
138 #[case(SimpleColumnType::Macaddr, "Option<String>")]
139 #[case(SimpleColumnType::Xml, "Option<String>")]
140 fn test_simple_column_type_to_rust_type_nullable(
141 #[case] column_type: SimpleColumnType,
142 #[case] expected: &str,
143 ) {
144 assert_eq!(ColumnType::Simple(column_type).to_rust_type(true), expected);
145 }
146
147 #[rstest]
148 #[case(ComplexColumnType::Varchar { length: 255 }, false, "String")]
149 #[case(ComplexColumnType::Varchar { length: 50 }, false, "String")]
150 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, false, "Decimal")]
151 #[case(ComplexColumnType::Numeric { precision: 5, scale: 0 }, false, "Decimal")]
152 #[case(ComplexColumnType::Char { length: 10 }, false, "String")]
153 #[case(ComplexColumnType::Char { length: 1 }, false, "String")]
154 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, false, "String")]
155 #[case(ComplexColumnType::Custom { custom_type: "JSONB".into() }, false, "String")]
156 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec!["active".into(), "inactive".into()]) }, false, "String")]
157 fn test_complex_column_type_to_rust_type_not_nullable(
158 #[case] column_type: ComplexColumnType,
159 #[case] nullable: bool,
160 #[case] expected: &str,
161 ) {
162 assert_eq!(
163 ColumnType::Complex(column_type).to_rust_type(nullable),
164 expected
165 );
166 }
167
168 #[rstest]
169 #[case(ComplexColumnType::Varchar { length: 255 }, "Option<String>")]
170 #[case(ComplexColumnType::Varchar { length: 50 }, "Option<String>")]
171 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, "Option<Decimal>")]
172 #[case(ComplexColumnType::Numeric { precision: 5, scale: 0 }, "Option<Decimal>")]
173 #[case(ComplexColumnType::Char { length: 10 }, "Option<String>")]
174 #[case(ComplexColumnType::Char { length: 1 }, "Option<String>")]
175 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, "Option<String>")]
176 #[case(ComplexColumnType::Custom { custom_type: "JSONB".into() }, "Option<String>")]
177 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec!["active".into(), "inactive".into()]) }, "Option<String>")]
178 fn test_complex_column_type_to_rust_type_nullable(
179 #[case] column_type: ComplexColumnType,
180 #[case] expected: &str,
181 ) {
182 assert_eq!(
183 ColumnType::Complex(column_type).to_rust_type(true),
184 expected
185 );
186 }
187
188 #[rstest]
189 #[case(ComplexColumnType::Varchar { length: 255 })]
190 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 })]
191 #[case(ComplexColumnType::Char { length: 1 })]
192 #[case(ComplexColumnType::Custom { custom_type: "SERIAL".into() })]
193 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec![]) })]
194 fn test_complex_column_type_does_not_support_auto_increment(
195 #[case] column_type: ComplexColumnType,
196 ) {
197 assert!(!ColumnType::Complex(column_type).supports_auto_increment());
199 }
200
201 #[test]
202 fn test_enum_values_is_string() {
203 let string_vals = EnumValues::String(vec!["active".into()]);
204 let int_vals = EnumValues::Integer(vec![NumValue {
205 name: "Active".into(),
206 value: 1,
207 }]);
208 assert!(string_vals.is_string());
209 assert!(!int_vals.is_string());
210 }
211
212 #[test]
213 fn test_enum_values_is_integer() {
214 let string_vals = EnumValues::String(vec!["active".into()]);
215 let int_vals = EnumValues::Integer(vec![NumValue {
216 name: "Active".into(),
217 value: 1,
218 }]);
219 assert!(!string_vals.is_integer());
220 assert!(int_vals.is_integer());
221 }
222
223 #[rstest]
224 #[case::string_match(
226 EnumValues::String(vec!["active".into(), "pending".into()]),
227 "active",
228 true,
229 )]
230 #[case::string_miss(
231 EnumValues::String(vec!["active".into(), "pending".into()]),
232 "banned",
233 false,
234 )]
235 #[case::integer_numeric_match(
237 EnumValues::Integer(vec![
238 NumValue { name: "Low".into(), value: 0 },
239 NumValue { name: "High".into(), value: 10 },
240 ]),
241 "10",
242 true,
243 )]
244 #[case::integer_numeric_miss(
245 EnumValues::Integer(vec![
246 NumValue { name: "Low".into(), value: 0 },
247 NumValue { name: "High".into(), value: 10 },
248 ]),
249 "99",
250 false,
251 )]
252 #[case::integer_numeric_beyond_i32(
254 EnumValues::Integer(vec![
255 NumValue { name: "small".into(), value: 1 },
256 NumValue { name: "big".into(), value: 3_000_000_000 },
257 ]),
258 "3000000000",
259 true,
260 )]
261 #[case::integer_name_match(
263 EnumValues::Integer(vec![
264 NumValue { name: "Low".into(), value: 0 },
265 NumValue { name: "High".into(), value: 10 },
266 ]),
267 "High",
268 true,
269 )]
270 #[case::integer_name_miss(
271 EnumValues::Integer(vec![
272 NumValue { name: "Low".into(), value: 0 },
273 NumValue { name: "High".into(), value: 10 },
274 ]),
275 "Unknown",
276 false,
277 )]
278 fn contains_value_matches_variants_per_enum_kind(
279 #[case] values: EnumValues,
280 #[case] candidate: &str,
281 #[case] expected: bool,
282 ) {
283 assert_eq!(values.contains_value(candidate), expected);
284 }
285
286 #[test]
287 fn test_enum_values_len_and_is_empty() {
288 let empty = EnumValues::String(vec![]);
290 let non_empty = EnumValues::String(vec!["a".into()]);
291 assert!(empty.is_empty());
292 assert_eq!(empty.len(), 0);
293 assert!(!non_empty.is_empty());
294 assert_eq!(non_empty.len(), 1);
295
296 let empty_int = EnumValues::Integer(vec![]);
298 let non_empty_int = EnumValues::Integer(vec![
299 NumValue {
300 name: "A".into(),
301 value: 0,
302 },
303 NumValue {
304 name: "B".into(),
305 value: 1,
306 },
307 ]);
308 assert!(empty_int.is_empty());
309 assert_eq!(empty_int.len(), 0);
310 assert!(!non_empty_int.is_empty());
311 assert_eq!(non_empty_int.len(), 2);
312 }
313
314 #[rstest]
315 #[case::string_two_variants(
320 EnumValues::String(vec!["active".into(), "pending".into()]),
321 ", ",
322 "'active', 'pending'",
323 )]
324 #[case::string_single_quote_escape(
325 EnumValues::String(vec!["O'Brien".into(), "'leading".into()]),
326 ", ",
327 "'O''Brien', '''leading'",
328 )]
329 #[case::integer_two_variants(
330 EnumValues::Integer(vec![
331 NumValue { name: "low".into(), value: 0 },
332 NumValue { name: "high".into(), value: 10 },
333 ]),
334 ", ",
335 "0, 10",
336 )]
337 #[case::string_empty(EnumValues::String(vec![]), ", ", "")]
338 #[case::integer_empty(EnumValues::Integer(vec![]), ", ", "")]
339 #[case::string_single_variant(
340 EnumValues::String(vec!["only".into()]),
341 ", ",
342 "'only'",
343 )]
344 #[case::alt_separator(
345 EnumValues::String(vec!["a".into(), "b".into(), "c".into()]),
346 "_",
347 "'a'_'b'_'c'",
348 )]
349 fn sql_values_joined_locks_expected_output(
350 #[case] vals: EnumValues,
351 #[case] separator: &str,
352 #[case] expected: &str,
353 ) {
354 assert_eq!(vals.sql_values_joined(separator), expected);
355 }
356
357 #[test]
358 fn test_enum_values_from_vec_string() {
359 let vals: EnumValues = vec!["a".to_string(), "b".to_string()].into();
360 assert!(matches!(vals, EnumValues::String(_)));
361 }
362
363 #[test]
364 fn test_enum_values_from_vec_str() {
365 let vals: EnumValues = vec!["a", "b"].into();
366 assert!(matches!(vals, EnumValues::String(_)));
367 }
368
369 #[rstest]
370 #[case(SimpleColumnType::SmallInt, true)]
371 #[case(SimpleColumnType::Integer, true)]
372 #[case(SimpleColumnType::BigInt, true)]
373 #[case(SimpleColumnType::Text, false)]
374 #[case(SimpleColumnType::Boolean, false)]
375 fn test_simple_column_type_supports_auto_increment(
376 #[case] ty: SimpleColumnType,
377 #[case] expected: bool,
378 ) {
379 assert_eq!(ty.supports_auto_increment(), expected);
380 }
381
382 #[rstest]
383 #[case(SimpleColumnType::Integer, true)]
384 #[case(SimpleColumnType::Text, false)]
385 fn test_column_type_simple_supports_auto_increment(
386 #[case] ty: SimpleColumnType,
387 #[case] expected: bool,
388 ) {
389 assert_eq!(ColumnType::Simple(ty).supports_auto_increment(), expected);
390 }
391
392 #[test]
393 fn test_requires_migration_integer_enum_values_changed() {
394 let from = ColumnType::Complex(ComplexColumnType::Enum {
396 name: "status".into(),
397 values: EnumValues::Integer(vec![
398 NumValue {
399 name: "Pending".into(),
400 value: 0,
401 },
402 NumValue {
403 name: "Active".into(),
404 value: 1,
405 },
406 ]),
407 });
408 let to = ColumnType::Complex(ComplexColumnType::Enum {
409 name: "status".into(),
410 values: EnumValues::Integer(vec![
411 NumValue {
412 name: "Pending".into(),
413 value: 0,
414 },
415 NumValue {
416 name: "Active".into(),
417 value: 1,
418 },
419 NumValue {
420 name: "Completed".into(),
421 value: 100,
422 },
423 ]),
424 });
425 assert!(!from.requires_migration(&to));
426 }
427
428 #[test]
429 fn requires_migration_integer_enum_name_change_is_false() {
430 let e1 = ColumnType::Complex(ComplexColumnType::Enum {
431 name: "old_status".into(),
432 values: EnumValues::Integer(vec![
433 NumValue {
434 name: "active".into(),
435 value: 0,
436 },
437 NumValue {
438 name: "inactive".into(),
439 value: 1,
440 },
441 ]),
442 });
443 let e2 = ColumnType::Complex(ComplexColumnType::Enum {
444 name: "new_status".into(),
445 values: EnumValues::Integer(vec![
446 NumValue {
447 name: "active".into(),
448 value: 0,
449 },
450 NumValue {
451 name: "inactive".into(),
452 value: 1,
453 },
454 ]),
455 });
456
457 assert!(
458 !e1.requires_migration(&e2),
459 "integer enum name change should NOT require migration (stored as INTEGER, no DB schema change)"
460 );
461 }
462
463 #[test]
464 fn test_requires_migration_integer_enum_name_changed() {
465 let from = ColumnType::Complex(ComplexColumnType::Enum {
467 name: "old_status".into(),
468 values: EnumValues::Integer(vec![NumValue {
469 name: "Pending".into(),
470 value: 0,
471 }]),
472 });
473 let to = ColumnType::Complex(ComplexColumnType::Enum {
474 name: "new_status".into(),
475 values: EnumValues::Integer(vec![NumValue {
476 name: "Pending".into(),
477 value: 0,
478 }]),
479 });
480 assert!(!from.requires_migration(&to));
481 }
482
483 #[test]
484 fn test_requires_migration_string_enum_values_changed() {
485 let from = ColumnType::Complex(ComplexColumnType::Enum {
487 name: "status".into(),
488 values: EnumValues::String(vec!["pending".into(), "active".into()]),
489 });
490 let to = ColumnType::Complex(ComplexColumnType::Enum {
491 name: "status".into(),
492 values: EnumValues::String(vec![
493 "pending".into(),
494 "active".into(),
495 "completed".into(),
496 ]),
497 });
498 assert!(from.requires_migration(&to));
499 }
500
501 #[test]
502 fn test_requires_migration_simple_types() {
503 let int = ColumnType::Simple(SimpleColumnType::Integer);
504 let text = ColumnType::Simple(SimpleColumnType::Text);
505 assert!(int.requires_migration(&text));
506 assert!(!int.requires_migration(&int));
507 }
508
509 #[test]
510 fn test_requires_migration_mixed_enum_types() {
511 let string_enum = ColumnType::Complex(ComplexColumnType::Enum {
513 name: "status".into(),
514 values: EnumValues::String(vec!["pending".into()]),
515 });
516 let int_enum = ColumnType::Complex(ComplexColumnType::Enum {
517 name: "status".into(),
518 values: EnumValues::Integer(vec![NumValue {
519 name: "Pending".into(),
520 value: 0,
521 }]),
522 });
523 assert!(string_enum.requires_migration(&int_enum));
524 }
525
526 #[rstest]
528 #[case(SimpleColumnType::SmallInt, "smallint")]
529 #[case(SimpleColumnType::Integer, "integer")]
530 #[case(SimpleColumnType::BigInt, "bigint")]
531 #[case(SimpleColumnType::Real, "real")]
532 #[case(SimpleColumnType::DoublePrecision, "double precision")]
533 #[case(SimpleColumnType::Text, "text")]
534 #[case(SimpleColumnType::Boolean, "boolean")]
535 #[case(SimpleColumnType::Date, "date")]
536 #[case(SimpleColumnType::Time, "time")]
537 #[case(SimpleColumnType::Timestamp, "timestamp")]
538 #[case(SimpleColumnType::Timestamptz, "timestamptz")]
539 #[case(SimpleColumnType::Interval, "interval")]
540 #[case(SimpleColumnType::Bytea, "bytea")]
541 #[case(SimpleColumnType::Uuid, "uuid")]
542 #[case(SimpleColumnType::Json, "json")]
543 #[case(SimpleColumnType::Inet, "inet")]
544 #[case(SimpleColumnType::Cidr, "cidr")]
545 #[case(SimpleColumnType::Macaddr, "macaddr")]
546 #[case(SimpleColumnType::Xml, "xml")]
547 fn test_simple_column_type_to_display_string(
548 #[case] column_type: SimpleColumnType,
549 #[case] expected: &str,
550 ) {
551 assert_eq!(column_type.to_display_string(), expected);
552 }
553
554 #[rstest]
555 #[case(SimpleColumnType::SmallInt, "SMALLINT")]
556 #[case(SimpleColumnType::Integer, "INTEGER")]
557 #[case(SimpleColumnType::BigInt, "BIGINT")]
558 #[case(SimpleColumnType::Real, "REAL")]
559 #[case(SimpleColumnType::DoublePrecision, "DOUBLE PRECISION")]
560 #[case(SimpleColumnType::Text, "TEXT")]
561 #[case(SimpleColumnType::Boolean, "BOOLEAN")]
562 #[case(SimpleColumnType::Date, "DATE")]
563 #[case(SimpleColumnType::Time, "TIME")]
564 #[case(SimpleColumnType::Timestamp, "TIMESTAMP")]
565 #[case(SimpleColumnType::Timestamptz, "TIMESTAMPTZ")]
566 #[case(SimpleColumnType::Interval, "INTERVAL")]
567 #[case(SimpleColumnType::Bytea, "BYTEA")]
568 #[case(SimpleColumnType::Uuid, "UUID")]
569 #[case(SimpleColumnType::Json, "JSON")]
570 #[case(SimpleColumnType::Inet, "INET")]
571 #[case(SimpleColumnType::Cidr, "CIDR")]
572 #[case(SimpleColumnType::Macaddr, "MACADDR")]
573 #[case(SimpleColumnType::Xml, "XML")]
574 fn test_simple_column_type_sql_type(
575 #[case] column_type: SimpleColumnType,
576 #[case] expected: &str,
577 ) {
578 assert_eq!(column_type.sql_type(), expected);
579 }
580
581 #[rstest]
582 #[case(SimpleColumnType::SmallInt, "small_int")]
583 #[case(SimpleColumnType::Integer, "integer")]
584 #[case(SimpleColumnType::BigInt, "big_int")]
585 #[case(SimpleColumnType::Real, "real")]
586 #[case(SimpleColumnType::DoublePrecision, "double_precision")]
587 #[case(SimpleColumnType::Text, "text")]
588 #[case(SimpleColumnType::Boolean, "boolean")]
589 #[case(SimpleColumnType::Date, "date")]
590 #[case(SimpleColumnType::Time, "time")]
591 #[case(SimpleColumnType::Timestamp, "timestamp")]
592 #[case(SimpleColumnType::Timestamptz, "timestamptz")]
593 #[case(SimpleColumnType::Interval, "interval")]
594 #[case(SimpleColumnType::Bytea, "bytea")]
595 #[case(SimpleColumnType::Uuid, "uuid")]
596 #[case(SimpleColumnType::Json, "json")]
597 #[case(SimpleColumnType::Inet, "inet")]
598 #[case(SimpleColumnType::Cidr, "cidr")]
599 #[case(SimpleColumnType::Macaddr, "macaddr")]
600 #[case(SimpleColumnType::Xml, "xml")]
601 fn test_simple_column_type_model_name_matches_serde_wire_format(
602 #[case] column_type: SimpleColumnType,
603 #[case] expected: &str,
604 ) {
605 assert_eq!(column_type.model_name(), expected);
606 assert_eq!(
608 serde_json::to_value(column_type).unwrap(),
609 serde_json::Value::String(expected.to_string())
610 );
611 }
612
613 #[rstest]
614 #[case(ComplexColumnType::Varchar { length: 255 }, "VARCHAR")]
615 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, "NUMERIC")]
616 #[case(ComplexColumnType::Char { length: 5 }, "CHAR")]
617 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, "CUSTOM")]
618 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec!["active".into()]) }, "ENUM")]
619 fn test_complex_column_type_sql_type(
620 #[case] column_type: ComplexColumnType,
621 #[case] expected: &str,
622 ) {
623 assert_eq!(column_type.sql_type(), expected);
624 }
625
626 #[test]
627 fn test_complex_column_type_to_display_string_varchar() {
628 let ty = ComplexColumnType::Varchar { length: 255 };
629 assert_eq!(ty.to_display_string(), "varchar(255)");
630 }
631
632 #[test]
633 fn test_complex_column_type_to_display_string_numeric() {
634 let ty = ComplexColumnType::Numeric {
635 precision: 10,
636 scale: 2,
637 };
638 assert_eq!(ty.to_display_string(), "numeric(10,2)");
639 }
640
641 #[test]
642 fn test_complex_column_type_to_display_string_char() {
643 let ty = ComplexColumnType::Char { length: 5 };
644 assert_eq!(ty.to_display_string(), "char(5)");
645 }
646
647 #[test]
648 fn test_complex_column_type_to_display_string_custom() {
649 let ty = ComplexColumnType::Custom {
650 custom_type: "TSVECTOR".into(),
651 };
652 assert_eq!(ty.to_display_string(), "tsvector");
653 }
654
655 #[test]
656 fn test_complex_column_type_to_display_string_string_enum() {
657 let ty = ComplexColumnType::Enum {
658 name: "user_status".into(),
659 values: EnumValues::String(vec!["active".into(), "inactive".into()]),
660 };
661 assert_eq!(ty.to_display_string(), "enum<user_status>");
662 }
663
664 #[test]
665 fn test_complex_column_type_to_display_string_integer_enum() {
666 let ty = ComplexColumnType::Enum {
667 name: "priority".into(),
668 values: EnumValues::Integer(vec![
669 NumValue {
670 name: "Low".into(),
671 value: 0,
672 },
673 NumValue {
674 name: "High".into(),
675 value: 10,
676 },
677 ]),
678 };
679 assert_eq!(ty.to_display_string(), "enum<priority> (integer)");
680 }
681
682 #[test]
683 fn test_column_type_to_display_string_simple() {
684 let ty = ColumnType::Simple(SimpleColumnType::Integer);
685 assert_eq!(ty.to_display_string(), "integer");
686 }
687
688 #[test]
689 fn test_column_type_to_display_string_complex() {
690 let ty = ColumnType::Complex(ComplexColumnType::Varchar { length: 100 });
691 assert_eq!(ty.to_display_string(), "varchar(100)");
692 }
693
694 #[rstest]
696 #[case(SimpleColumnType::SmallInt, "0")]
697 #[case(SimpleColumnType::Integer, "0")]
698 #[case(SimpleColumnType::BigInt, "0")]
699 #[case(SimpleColumnType::Real, "0.0")]
700 #[case(SimpleColumnType::DoublePrecision, "0.0")]
701 #[case(SimpleColumnType::Boolean, "false")]
702 #[case(SimpleColumnType::Text, "''")]
703 #[case(SimpleColumnType::Date, "'1970-01-01'")]
704 #[case(SimpleColumnType::Time, "'00:00:00'")]
705 #[case(SimpleColumnType::Timestamp, "CURRENT_TIMESTAMP")]
706 #[case(SimpleColumnType::Timestamptz, "CURRENT_TIMESTAMP")]
707 #[case(SimpleColumnType::Interval, "'0'")]
708 #[case(SimpleColumnType::Bytea, "''")]
709 #[case(SimpleColumnType::Uuid, "'00000000-0000-0000-0000-000000000000'")]
710 #[case(SimpleColumnType::Json, "'{}'")]
711 #[case(SimpleColumnType::Inet, "'0.0.0.0'")]
712 #[case(SimpleColumnType::Cidr, "'0.0.0.0'")]
713 #[case(SimpleColumnType::Macaddr, "'00:00:00:00:00:00'")]
714 #[case(SimpleColumnType::Xml, "'<xml/>'")]
715 fn test_simple_column_type_default_fill_value(
716 #[case] column_type: SimpleColumnType,
717 #[case] expected: &str,
718 ) {
719 assert_eq!(column_type.default_fill_value(), expected);
720 }
721
722 #[test]
723 fn test_complex_column_type_default_fill_value_varchar() {
724 let ty = ComplexColumnType::Varchar { length: 255 };
725 assert_eq!(ty.default_fill_value(), "''");
726 }
727
728 #[test]
729 fn test_complex_column_type_default_fill_value_char() {
730 let ty = ComplexColumnType::Char { length: 1 };
731 assert_eq!(ty.default_fill_value(), "''");
732 }
733
734 #[test]
735 fn test_complex_column_type_default_fill_value_numeric() {
736 let ty = ComplexColumnType::Numeric {
737 precision: 10,
738 scale: 2,
739 };
740 assert_eq!(ty.default_fill_value(), "0");
741 }
742
743 #[test]
744 fn test_complex_column_type_default_fill_value_custom() {
745 let ty = ComplexColumnType::Custom {
746 custom_type: "MONEY".into(),
747 };
748 assert_eq!(ty.default_fill_value(), "''");
749 }
750
751 #[test]
752 fn test_complex_column_type_default_fill_value_enum() {
753 let ty = ComplexColumnType::Enum {
754 name: "status".into(),
755 values: EnumValues::String(vec!["active".into()]),
756 };
757 assert_eq!(ty.default_fill_value(), "''");
758 }
759
760 #[test]
761 fn test_column_type_default_fill_value_simple() {
762 let ty = ColumnType::Simple(SimpleColumnType::Integer);
763 assert_eq!(ty.default_fill_value(), "0");
764 }
765
766 #[test]
767 fn test_column_type_default_fill_value_complex() {
768 let ty = ColumnType::Complex(ComplexColumnType::Varchar { length: 100 });
769 assert_eq!(ty.default_fill_value(), "''");
770 }
771
772 #[test]
774 fn test_enum_variant_names_simple_type_returns_none() {
775 let ty = ColumnType::Simple(SimpleColumnType::Integer);
776 assert_eq!(ty.enum_variant_names(), None);
777 }
778
779 #[test]
780 fn test_enum_variant_names_complex_non_enum_returns_none() {
781 let ty = ColumnType::Complex(ComplexColumnType::Varchar { length: 255 });
782 assert_eq!(ty.enum_variant_names(), None);
783 }
784
785 #[test]
786 fn test_enum_variant_names_complex_numeric_returns_none() {
787 let ty = ColumnType::Complex(ComplexColumnType::Numeric {
788 precision: 10,
789 scale: 2,
790 });
791 assert_eq!(ty.enum_variant_names(), None);
792 }
793
794 #[test]
795 fn test_enum_variant_names_complex_char_returns_none() {
796 let ty = ColumnType::Complex(ComplexColumnType::Char { length: 1 });
797 assert_eq!(ty.enum_variant_names(), None);
798 }
799
800 #[test]
801 fn test_enum_variant_names_complex_custom_returns_none() {
802 let ty = ColumnType::Complex(ComplexColumnType::Custom {
803 custom_type: "TSVECTOR".into(),
804 });
805 assert_eq!(ty.enum_variant_names(), None);
806 }
807
808 #[test]
809 fn test_enum_variant_names_string_enum() {
810 let ty = ColumnType::Complex(ComplexColumnType::Enum {
811 name: "status".into(),
812 values: EnumValues::String(vec![
813 "active".into(),
814 "inactive".into(),
815 "pending".into(),
816 ]),
817 });
818 assert_eq!(
819 ty.enum_variant_names(),
820 Some(vec![
821 "active".to_string(),
822 "inactive".to_string(),
823 "pending".to_string()
824 ])
825 );
826 }
827
828 #[test]
829 fn test_enum_variant_names_integer_enum() {
830 let ty = ColumnType::Complex(ComplexColumnType::Enum {
831 name: "priority".into(),
832 values: EnumValues::Integer(vec![
833 NumValue {
834 name: "Low".into(),
835 value: 0,
836 },
837 NumValue {
838 name: "Medium".into(),
839 value: 5,
840 },
841 NumValue {
842 name: "High".into(),
843 value: 10,
844 },
845 ]),
846 });
847 assert_eq!(
848 ty.enum_variant_names(),
849 Some(vec![
850 "Low".to_string(),
851 "Medium".to_string(),
852 "High".to_string()
853 ])
854 );
855 }
856
857 #[test]
858 fn test_enum_variant_names_empty_string_enum() {
859 let ty = ColumnType::Complex(ComplexColumnType::Enum {
860 name: "empty".into(),
861 values: EnumValues::String(vec![]),
862 });
863 assert_eq!(ty.enum_variant_names(), Some(vec![]));
864 }
865
866 #[test]
867 fn test_enum_variant_names_empty_integer_enum() {
868 let ty = ColumnType::Complex(ComplexColumnType::Enum {
869 name: "empty".into(),
870 values: EnumValues::Integer(vec![]),
871 });
872 assert_eq!(ty.enum_variant_names(), Some(vec![]));
873 }
874 }
875}