Skip to content

Commit a9a10b3

Browse files
authored
Merge pull request #4749 from weiznich/fix/better_generate_migrations_for_postgres
Improve support for various postgres types in generated migrations
2 parents 9d0dec3 + ef8ebb5 commit a9a10b3

File tree

16 files changed

+550
-27
lines changed

16 files changed

+550
-27
lines changed

diesel_cli/src/infer_schema_internals/data_structures.rs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ pub struct ColumnType {
2020
pub is_array: bool,
2121
pub is_nullable: bool,
2222
pub is_unsigned: bool,
23+
pub record: Option<Vec<ColumnType>>,
2324
pub max_length: Option<u64>,
2425
}
2526

2627
impl ColumnType {
2728
pub(crate) fn for_column_def(c: &ColumnDef) -> Result<Self, crate::errors::Error> {
28-
Ok(Self::for_type_path(
29+
Self::for_type_path(
2930
&c.tpe,
3031
c.max_length
3132
.as_ref()
@@ -34,10 +35,13 @@ impl ColumnType {
3435
.map_err(crate::errors::Error::ColumnLiteralParseError)
3536
})
3637
.transpose()?,
37-
))
38+
)
3839
}
3940

40-
fn for_type_path(t: &syn::TypePath, max_length: Option<u64>) -> Self {
41+
fn for_type_path(
42+
t: &syn::TypePath,
43+
max_length: Option<u64>,
44+
) -> Result<Self, crate::errors::Error> {
4145
let last = t
4246
.path
4347
.segments
@@ -51,31 +55,80 @@ impl ColumnType {
5155
is_array: last.ident == "Array",
5256
is_nullable: last.ident == "Nullable",
5357
is_unsigned: last.ident == "Unsigned",
58+
record: None,
5459
max_length,
5560
};
61+
let is_range = last.ident == "Range";
62+
let is_multirange = last.ident == "Multirange";
63+
let is_record = last.ident == "Record";
5664

57-
let sql_name = if !ret.is_nullable && !ret.is_array && !ret.is_unsigned {
65+
let sql_name = if !ret.is_nullable
66+
&& !ret.is_array
67+
&& !ret.is_unsigned
68+
&& !is_range
69+
&& !is_multirange
70+
&& !is_record
71+
{
5872
if last.ident == "PgLsn" {
5973
"pg_lsn".to_string()
6074
} else {
6175
last.ident.to_string()
6276
}
6377
} else if let syn::PathArguments::AngleBracketed(ref args) = last.arguments {
6478
let arg = args.args.first().expect("There is at least one argument");
65-
if let syn::GenericArgument::Type(syn::Type::Path(p)) = arg {
66-
let s = Self::for_type_path(p, max_length);
67-
ret.is_nullable |= s.is_nullable;
68-
ret.is_array |= s.is_array;
69-
ret.is_unsigned |= s.is_unsigned;
70-
s.sql_name
79+
if let syn::GenericArgument::Type(syn::Type::Tuple(t)) = arg {
80+
ret.record = Some(
81+
t.elems
82+
.iter()
83+
.map(|t| {
84+
if let syn::Type::Path(p) = t {
85+
Self::for_type_path(p, None)
86+
} else {
87+
panic!();
88+
}
89+
})
90+
.collect::<Result<_, _>>()?,
91+
);
92+
"record".to_owned()
93+
} else if let syn::GenericArgument::Type(syn::Type::Path(p)) = arg {
94+
let s = Self::for_type_path(p, max_length)?;
95+
if is_range {
96+
match s.sql_name.to_uppercase().as_str() {
97+
"INT4" | "INTEGER" => "int4range".to_owned(),
98+
"INT8" | "BIGINT" => "int8range".into(),
99+
"NUMERIC" => "numrange".into(),
100+
"TIMESTAMP" | "TIMESTAMP WITHOUT TIME ZONE" => "tsrange".into(),
101+
"TIMESTAMPTZ" | "TIMESTAMP WITH TIME ZONE" => "tstzrange".into(),
102+
"DATE" => "daterange".into(),
103+
s => format!("{s}range"),
104+
}
105+
} else if is_multirange {
106+
match s.sql_name.to_uppercase().as_str() {
107+
"INT4" | "INTEGER" => "int4multirange".to_owned(),
108+
"INT8" | "BIGINT" => "int8multirange".into(),
109+
"NUMERIC" => "nummultirange".into(),
110+
"TIMESTAMP" | "TIMESTAMP WITHOUT TIME ZONE" => "tsmultirange".into(),
111+
"TIMESTAMPTZ" | "TIMESTAMP WITH TIME ZONE" => "tstzmultirange".into(),
112+
"DATE" => "datemultirange".into(),
113+
s => format!("{s}multirange"),
114+
}
115+
} else {
116+
if !ret.is_array {
117+
ret.is_nullable |= s.is_nullable;
118+
ret.is_array |= s.is_array;
119+
}
120+
ret.is_unsigned |= s.is_unsigned;
121+
ret.record = ret.record.or(s.record);
122+
s.sql_name
123+
}
71124
} else {
72125
unreachable!("That shouldn't happen")
73126
}
74127
} else {
75128
unreachable!("That shouldn't happen")
76129
};
77130
ret.sql_name = sql_name;
78-
ret
131+
Ok(ret)
79132
}
80133
}
81134

@@ -106,7 +159,7 @@ impl fmt::Display for ColumnType {
106159
}
107160
}
108161

109-
#[derive(Debug)]
162+
#[derive(Debug, Clone)]
110163
pub struct ColumnDefinition {
111164
pub sql_name: String,
112165
pub rust_name: String,

diesel_cli/src/infer_schema_internals/mysql.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ pub fn determine_column_type(attr: &ColumnInformation) -> Result<ColumnType, cra
222222
is_array: false,
223223
is_nullable: attr.nullable,
224224
is_unsigned: unsigned,
225+
record: None,
225226
max_length: attr.max_length,
226227
})
227228
}

diesel_cli/src/infer_schema_internals/pg.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub fn determine_column_type(
6868
is_array,
6969
is_nullable: attr.nullable,
7070
is_unsigned: false,
71+
record: None,
7172
max_length: attr.max_length,
7273
})
7374
}

diesel_cli/src/infer_schema_internals/sqlite.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ pub fn determine_column_type(
374374
is_array: false,
375375
is_nullable: attr.nullable,
376376
is_unsigned: false,
377+
record: None,
377378
max_length: attr.max_length,
378379
})
379380
}

0 commit comments

Comments
 (0)