Skip to content

Commit 3bb367d

Browse files
committed
chore(routine): Move building of routine signature out of SQL
1 parent 5047c46 commit 3bb367d

File tree

7 files changed

+173
-37
lines changed

7 files changed

+173
-37
lines changed

src/compare/mod.rs

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::db::column_privilege::ColumnPrivilege;
3434
use crate::db::index::Index;
3535
use crate::db::privilege::Privilege;
3636
use crate::db::routine::Routine;
37+
use crate::db::routine_parameters::RoutineParameter;
3738
use crate::db::routine_privilege::RoutinePrivilege;
3839
use crate::db::table_constraint::TableConstraint;
3940
use crate::db::table_privilege::TablePrivilege;
@@ -72,6 +73,8 @@ impl Comparer {
7273
let right_column_privileges = self.right_db.column_privileges(&schemas[..]).await?;
7374
let left_routines = self.left_db.routines(&schemas[..]).await?;
7475
let right_routines = self.right_db.routines(&schemas[..]).await?;
76+
let left_routine_parameters = self.left_db.routine_parameters(&schemas[..]).await?;
77+
let right_routine_parameters = self.right_db.routine_parameters(&schemas[..]).await?;
7578
let left_routine_privileges = self.left_db.routine_privileges(&schemas[..]).await?;
7679
let right_routine_privileges = self.right_db.routine_privileges(&schemas[..]).await?;
7780
let left_schemas = self.left_db.schemas(&schemas[..]).await?;
@@ -107,9 +110,17 @@ impl Comparer {
107110

108111
let left_schema_routines = left_routines.iter().filter(|r| r.routine_schema == schema).collect();
109112
let right_schema_routines = right_routines.iter().filter(|r| r.routine_schema == schema).collect();
113+
let left_schema_routine_parameters = left_routine_parameters.iter().filter(|p| p.specific_schema == schema).collect();
114+
let right_schema_routine_parameters = right_routine_parameters.iter().filter(|p| p.specific_schema == schema).collect();
110115
let left_schema_routine_privileges = left_routine_privileges.iter().filter(|p| p.routine_schema == schema).collect();
111116
let right_schema_routine_privileges = right_routine_privileges.iter().filter(|p| p.routine_schema == schema).collect();
112-
let routines = self.compare_routines(left_schema_routines, right_schema_routines, left_schema_routine_privileges, right_schema_routine_privileges)?;
117+
let routines = self.compare_routines(
118+
left_schema_routines,
119+
right_schema_routines,
120+
left_schema_routine_parameters,
121+
right_schema_routine_parameters,
122+
left_schema_routine_privileges,
123+
right_schema_routine_privileges)?;
113124

114125
let left_schema_sequences = left_sequences.iter().filter(|s| s.sequence_schema == schema).collect();
115126
let right_schema_sequences = right_sequences.iter().filter(|s| s.sequence_schema == schema).collect();
@@ -172,39 +183,42 @@ impl Comparer {
172183
fn compare_routines(&mut self,
173184
left_routines: Vec<&Routine>,
174185
right_routines: Vec<&Routine>,
186+
left_routine_parameters: Vec<&RoutineParameter>,
187+
right_routine_parameters: Vec<&RoutineParameter>,
175188
left_routine_privileges: Vec<&RoutinePrivilege>,
176189
right_routine_privileges: Vec<&RoutinePrivilege>,
177190
) -> Result<Report<RoutineComparison>, Error> {
178-
let mut right_routines_map: HashMap<String, &Routine> = right_routines.into_iter().map(|t| (t.signature.clone(), t)).collect();
191+
let mut right_routines_map: HashMap<String, &Routine> = right_routines.into_iter().map(|r| (routine_signature(r, &right_routine_parameters), r)).collect();
179192
let mut entries = Vec::new();
180193

181194
for left_routine in left_routines {
182-
let right_routine = right_routines_map.get(&left_routine.signature);
195+
let signature = routine_signature(left_routine, &left_routine_parameters);
196+
let right_routine = right_routines_map.get(&signature);
183197

184198
match right_routine {
185199
None => {
186-
entries.push(RoutineRemoved { routine_signature: left_routine.signature.clone() });
200+
entries.push(RoutineRemoved { routine_signature: signature.clone() });
187201
},
188202
Some(rr) => {
189203
let properties = self.compare_routine_properties(&left_routine, rr);
190-
191-
let left_routine_routine_privileges: Vec<&RoutinePrivilege> = left_routine_privileges.iter().filter(|p| p.signature == left_routine.signature).cloned().collect();
192-
let right_routine_routine_privileges: Vec<&RoutinePrivilege> = right_routine_privileges.iter().filter(|p| p.signature == rr.signature).cloned().collect();
204+
205+
let left_routine_routine_privileges: Vec<&RoutinePrivilege> = left_routine_privileges.iter().filter(|p| p.specific_catalog == left_routine.specific_catalog && p.specific_schema == left_routine.specific_schema && p.specific_name == left_routine.specific_name).cloned().collect();
206+
let right_routine_routine_privileges: Vec<&RoutinePrivilege> = right_routine_privileges.iter().filter(|p| p.specific_catalog == rr.specific_catalog && p.specific_schema == rr.specific_schema && p.specific_name == rr.specific_name).cloned().collect();
193207
let privileges = self.compare_routine_privileges(left_routine_routine_privileges, right_routine_routine_privileges)?;
194208

195-
entries.push(RoutineMaintained { routine_signature: left_routine.signature.clone(), properties, privileges });
209+
entries.push(RoutineMaintained { routine_signature: signature.clone(), properties, privileges });
196210

197-
right_routines_map.remove(&rr.signature.clone());
211+
right_routines_map.remove(&signature);
198212
}
199213
}
200214
}
201215

202216
if right_routines_map.len() > 0 {
203-
let mut added_routines: Vec<&&Routine> = right_routines_map.values().collect();
204-
added_routines.sort_unstable_by_key(|r| &r.signature);
217+
let mut added_signatures : Vec<&String> = right_routines_map.keys().collect();
218+
added_signatures.sort_unstable();
205219

206-
for right_routine in added_routines {
207-
entries.push(RoutineAdded { routine_signature: right_routine.signature.clone() });
220+
for added_signature in added_signatures {
221+
entries.push(RoutineAdded { routine_signature: added_signature.clone() });
208222
}
209223
}
210224

@@ -786,4 +800,37 @@ impl Comparer {
786800
PropertyChanged { property_name: String::from(property_name), left_value: left.to_string(), right_value: right.to_string() }
787801
}
788802
}
803+
804+
}
805+
806+
fn routine_signature(routine: &Routine, all_parameters: &Vec<&RoutineParameter>) -> String {
807+
let parameters : Vec<&RoutineParameter> = all_parameters.iter().filter(|p| p.specific_catalog == routine.specific_catalog && p.specific_schema == routine.specific_schema && p.specific_name == routine.specific_name).cloned().collect();
808+
809+
let mut signature = routine.routine_name.clone();
810+
signature.push('(');
811+
812+
for (index, parameter) in parameters.iter().enumerate() {
813+
if index > 0 {
814+
signature.push_str(", ")
815+
}
816+
817+
let parameter_name = match &parameter.parameter_name {
818+
Some(parameter_name) => parameter_name,
819+
None => &format!("${}", parameter.ordinal_position),
820+
};
821+
822+
signature.push_str(parameter_name);
823+
signature.push(' ');
824+
signature.push_str(&parameter.parameter_mode);
825+
signature.push(' ');
826+
signature.push_str(&parameter.udt_catalog);
827+
signature.push('.');
828+
signature.push_str(&parameter.udt_schema);
829+
signature.push('.');
830+
signature.push_str(&parameter.udt_name);
831+
}
832+
833+
signature.push(')');
834+
835+
signature
789836
}

src/db/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod column_privilege;
33
pub mod index;
44
pub mod privilege;
55
pub mod routine;
6+
pub mod routine_parameters;
67
pub mod routine_privilege;
78
pub mod schema;
89
pub mod sequence;
@@ -17,6 +18,7 @@ use crate::db::column::Column;
1718
use crate::db::column_privilege::ColumnPrivilege;
1819
use crate::db::index::Index;
1920
use crate::db::routine::Routine;
21+
use crate::db::routine_parameters::RoutineParameter;
2022
use crate::db::routine_privilege::RoutinePrivilege;
2123
use crate::db::table_privilege::TablePrivilege;
2224
use crate::db::table_trigger::TableTrigger;
@@ -51,6 +53,10 @@ impl Database {
5153
routine::routines(&mut self.connection, schema_names).await
5254
}
5355

56+
pub async fn routine_parameters(&mut self, schema_names: &[String]) -> Result<Vec<RoutineParameter>, Error> {
57+
routine_parameters::routine_parameters(&mut self.connection, schema_names).await
58+
}
59+
5460
pub async fn routine_privileges(&mut self, schema_names: &[String]) -> Result<Vec<RoutinePrivilege>, Error> {
5561
routine_privilege::routine_privileges(&mut self.connection, schema_names).await
5662
}

src/db/routine.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ use sqlx::{Error, FromRow, PgConnection};
22

33
pub const QUERY: &str = r#"
44
SELECT
5+
r.specific_catalog,
6+
r.specific_schema,
7+
r.specific_name,
58
r.routine_catalog,
69
r.routine_schema,
7-
r.routine_name || '(' || COALESCE((
8-
SELECT string_agg(COALESCE(p.parameter_name, '$' || p.ordinal_position) || ' ' || p.parameter_mode || ' ' || p.udt_schema || '.' || p.udt_name, ', ' order by p.ordinal_position)
9-
FROM information_schema.parameters p
10-
WHERE p.specific_name = r.specific_name
11-
GROUP BY p.specific_name
12-
), '') || ')' signature,
10+
r.routine_name,
1311
r.routine_type,
1412
r.module_catalog,
1513
r.module_schema,
@@ -57,7 +55,7 @@ FROM
5755
WHERE
5856
r.routine_schema = ANY($1)
5957
ORDER BY
60-
signature;"#;
58+
r.routine_name;"#;
6159

6260
pub async fn routines(connection: &mut PgConnection, schema_names: &[String]) -> Result<Vec<Routine>, Error> {
6361
sqlx::query_as(QUERY)
@@ -67,9 +65,12 @@ pub async fn routines(connection: &mut PgConnection, schema_names: &[String]) ->
6765

6866
#[derive(Debug, Clone, PartialEq, FromRow)]
6967
pub struct Routine {
68+
pub specific_catalog: String,
69+
pub specific_schema: String,
70+
pub specific_name: String,
7071
pub routine_catalog: String,
7172
pub routine_schema: String,
72-
pub signature: String,
73+
pub routine_name: String,
7374
pub routine_type: Option<String>,
7475
pub module_catalog: Option<String>,
7576
pub module_schema: Option<String>,

src/db/routine_parameters.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use sqlx::{Error, FromRow, PgConnection};
2+
3+
pub const QUERY: &str = r#"
4+
SELECT
5+
p.specific_catalog,
6+
p.specific_schema,
7+
p.specific_name,
8+
p.ordinal_position,
9+
p.parameter_mode,
10+
p.is_result,
11+
p.as_locator,
12+
p.parameter_name,
13+
p.data_type,
14+
p.character_maximum_length,
15+
p.character_octet_length,
16+
p.character_set_catalog,
17+
p.character_set_schema,
18+
p.character_set_name,
19+
p.collation_catalog,
20+
p.collation_schema,
21+
p.collation_name,
22+
p.numeric_precision,
23+
p.numeric_precision_radix,
24+
p.numeric_scale,
25+
p.datetime_precision,
26+
p.interval_type,
27+
p.interval_precision,
28+
p.udt_catalog,
29+
p.udt_schema,
30+
p.udt_name,
31+
p.scope_catalog,
32+
p.scope_schema,
33+
p.scope_name,
34+
p.maximum_cardinality,
35+
p.dtd_identifier,
36+
p.parameter_default
37+
FROM information_schema.parameters p
38+
WHERE
39+
p.specific_schema = ANY($1);"#;
40+
41+
pub async fn routine_parameters(connection: &mut PgConnection, schema_names: &[String]) -> Result<Vec<RoutineParameter>, Error> {
42+
sqlx::query_as(QUERY)
43+
.bind(&schema_names[..])
44+
.fetch_all(connection).await
45+
}
46+
47+
#[derive(Debug, Clone, PartialEq, FromRow)]
48+
pub struct RoutineParameter {
49+
pub specific_catalog: String,
50+
pub specific_schema: String,
51+
pub specific_name: String,
52+
pub ordinal_position: i32,
53+
pub parameter_mode: String,
54+
pub is_result: Option<String>,
55+
pub as_locator: Option<String>,
56+
pub parameter_name: Option<String>,
57+
pub data_type: String,
58+
pub character_maximum_length: Option<i32>,
59+
pub character_octet_length: Option<i32>,
60+
pub character_set_catalog: Option<String>,
61+
pub character_set_schema: Option<String>,
62+
pub character_set_name: Option<String>,
63+
pub collation_catalog: Option<String>,
64+
pub collation_schema: Option<String>,
65+
pub collation_name: Option<String>,
66+
pub numeric_precision: Option<i32>,
67+
pub numeric_precision_radix: Option<i32>,
68+
pub numeric_scale: Option<i32>,
69+
pub datetime_precision: Option<i32>,
70+
pub interval_type: Option<String>,
71+
pub interval_precision: Option<i32>,
72+
pub udt_catalog: String,
73+
pub udt_schema: String,
74+
pub udt_name: String,
75+
pub scope_catalog: Option<String>,
76+
pub scope_schema: Option<String>,
77+
pub scope_name: Option<String>,
78+
pub maximum_cardinality: Option<i32>,
79+
pub dtd_identifier: String,
80+
pub parameter_default: Option<String>,
81+
}
82+

src/db/routine_privilege.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@ const QUERY: &str = r#"
55
SELECT
66
rp.grantor,
77
rp.grantee,
8+
rp.specific_catalog,
9+
rp.specific_schema,
10+
rp.specific_name,
811
rp.routine_catalog,
912
rp.routine_schema,
10-
rp.routine_name || '(' || COALESCE((
11-
SELECT string_agg(COALESCE(p.parameter_name, '$' || p.ordinal_position) || ' ' || p.parameter_mode || ' ' || p.udt_schema || '.' || p.udt_name, ', ' order by p.ordinal_position)
12-
FROM information_schema.parameters p
13-
WHERE p.specific_name = rp.specific_name
14-
GROUP BY p.specific_name
15-
), '') || ')' signature,
13+
rp.routine_name,
1614
rp.privilege_type,
1715
rp.is_grantable
1816
FROM
@@ -21,7 +19,6 @@ WHERE
2119
rp.routine_schema = ANY($1) AND
2220
rp.grantor != rp.grantee
2321
ORDER BY
24-
signature,
2522
privilege_type;"#;
2623

2724
pub async fn routine_privileges(connection: &mut PgConnection, schema_names: &[String]) -> Result<Vec<RoutinePrivilege>, Error> {
@@ -34,9 +31,12 @@ pub async fn routine_privileges(connection: &mut PgConnection, schema_names: &[S
3431
pub struct RoutinePrivilege {
3532
pub grantor: String,
3633
pub grantee: String,
34+
pub specific_catalog: String,
35+
pub specific_schema: String,
36+
pub specific_name: String,
3737
pub routine_catalog: String,
3838
pub routine_schema: String,
39-
pub signature: String,
39+
pub routine_name: String,
4040
pub privilege_type: String,
4141
pub is_grantable: String,
4242
}

tests/identical-schemas-verbose/expected.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Schema 'test': unchanged
33
Property 'default_character_set_catalog': unchanged at '<none>'
44
Property 'default_character_set_schema': unchanged at '<none>'
55
Property 'default_character_set_name': unchanged at '<none>'
6-
Routine 'new_employee(name IN pg_catalog.text, role_id IN pg_catalog.int4)': unchanged
6+
Routine 'new_employee(name IN postgres.pg_catalog.text, role_id IN postgres.pg_catalog.int4)': unchanged
77
Property 'routine_type': unchanged at 'PROCEDURE'
88
Property 'module_catalog': unchanged at '<none>'
99
Property 'module_schema': unchanged at '<none>'

tests/main/expected.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
Schema 'test':
2-
Routine 'routine_changed(a IN pg_catalog.int4, b IN pg_catalog.text)':
2+
Routine 'routine_changed(a IN postgres.pg_catalog.int4, b IN postgres.pg_catalog.text)':
33
Property 'routine_definition': changed from '
44
INSERT INTO routine (a, b)
55
VALUES (a, b);
66
' to '
77
INSERT INTO routine (a, b)
88
VALUES (a + 100, b);
99
'
10-
Routine 'routine_privilege_added(a IN pg_catalog.int4, b IN pg_catalog.text)':
10+
Routine 'routine_privilege_added(a IN postgres.pg_catalog.int4, b IN postgres.pg_catalog.text)':
1111
Privilege 'EXECUTE' (postgres->routine_privilege_added_role): added
12-
Routine 'routine_privilege_removed(a IN pg_catalog.int4, b IN pg_catalog.text)':
12+
Routine 'routine_privilege_removed(a IN postgres.pg_catalog.int4, b IN postgres.pg_catalog.text)':
1313
Privilege 'EXECUTE' (postgres->routine_privilege_removed_role): removed
14-
Routine 'routine_removed(a IN pg_catalog.int4, b IN pg_catalog.text)': removed
15-
Routine 'routine_whitespace_changed(a IN pg_catalog.int4, b IN pg_catalog.text)':
14+
Routine 'routine_removed(a IN postgres.pg_catalog.int4, b IN postgres.pg_catalog.text)': removed
15+
Routine 'routine_whitespace_changed(a IN postgres.pg_catalog.int4, b IN postgres.pg_catalog.text)':
1616
Property 'routine_definition': changed from '
1717
INSERT INTO routine (a, b)
1818
VALUES (a, b);
@@ -22,8 +22,8 @@ VALUES (a, b);
2222
VALUES
2323
(a, b);
2424
'
25-
Routine 'routine_added(a IN pg_catalog.int4, b IN pg_catalog.text)': added
26-
Routine 'routine_added_with_unnamed_parameters($1 IN pg_catalog.int4, $2 IN pg_catalog.text)': added
25+
Routine 'routine_added(a IN postgres.pg_catalog.int4, b IN postgres.pg_catalog.text)': added
26+
Routine 'routine_added_with_unnamed_parameters($1 IN postgres.pg_catalog.int4, $2 IN postgres.pg_catalog.text)': added
2727
Routine 'trigger_exception()': added
2828
Sequence 'sequence_changed':
2929
Property 'data_type': changed from 'integer' to 'bigint'

0 commit comments

Comments
 (0)