Skip to main content

postgres_static_analyzer_reflect/
lib.rs

1//! Functions to actually read the catalog tables from a running postgres instance, and translate them into `ddl-catalog-structs`.
2//!
3//! Uses [`tokio-postgres`](https://docs.rs/tokio-postgres/latest/tokio_postgres/) through [`clorinde`](https://crates.io/crates/clorinde).
4//!
5//! Most likely you'll want to use [`reflect_db_state`].
6
7pub(crate) use postgres_static_analyzer_reflect_queries as queries_crate;
8pub(crate) use queries_crate::tokio_postgres as postgres;
9pub use queries_crate::tokio_postgres::Client as PgClient;
10
11#[cfg(test)]
12mod reflect_test;
13
14mod reflect_gen;
15
16pub(crate) fn maybe_str(s: Option<&str>) -> Option<Str> {
17	s.map(Str::new)
18}
19
20macro_rules! aclitems {
21	($acl: ident, $item_type: ident, $grant_type: ident) => {
22		$item_type {
23			grantee: if $acl.grantee == "public" {None} else {Some($acl.grantee.into())},
24			grantor: $acl.grantor.into(),
25			grants: $acl.grants.map(|g| $grant_type { privilege: g.privilege, with_grant_option: g.with_grant_option }).collect(),
26		}
27	};
28}
29pub(crate) use aclitems;
30
31pub use postgres_static_analyzer_ddl_catalog_structs::*;
32use futures::TryStreamExt;
33
34/// A large wrapper function that calls all the other reflection functions.
35///
36/// All reflection functions narrow their scope to the single [database](https://www.postgresql.org/docs/17/sql-createdatabase.html) `client` is configured to connect to. This includes all catalog tables that point to a database.
37///
38/// Any object created in the `pg_temp` namespace or any `pg_toast` namespaces are also ignored.
39pub async fn reflect_db_state(
40	client: &PgClient
41) -> Result<DbState, postgres::Error> {
42	client.batch_execute(include_str!("../reflect_fns.sql")).await?;
43
44	let (
45		pg_aggregate,
46		pg_am,
47		pg_amop,
48		pg_amproc,
49		pg_attrdef,
50		pg_attribute,
51		pg_roles,
52		pg_auth_members,
53		pg_cast,
54		pg_class,
55		pg_collation,
56		pg_constraint,
57		pg_conversion,
58		pg_database,
59		pg_db_role_setting,
60		pg_default_acl,
61		// pg_depend,
62		// pg_description,
63		pg_enum,
64		pg_event_trigger,
65		pg_extension,
66		pg_foreign_data_wrapper,
67		pg_foreign_server,
68		pg_foreign_table,
69		pg_index,
70		pg_inherits,
71		// pg_init_privs,
72		pg_language,
73		pg_namespace,
74		pg_opclass,
75		pg_operator,
76		pg_opfamily,
77		pg_parameter_acl,
78		pg_partitioned_table,
79		pg_policy,
80		pg_proc,
81		pg_publication,
82		pg_publication_namespace,
83		pg_publication_rel,
84		pg_range,
85		// pg_replication_origin,
86		// pg_rewrite,
87		pg_rules,
88		pg_views,
89		pg_matviews,
90		// pg_seclabel,
91		pg_sequence,
92		// pg_shdepend,
93		// pg_shdescription,
94		// pg_shseclabel,
95		pg_statistic_ext,
96		pg_subscription,
97		pg_transform,
98		pg_trigger,
99		pg_ts_config,
100		pg_ts_config_map,
101		pg_ts_dict,
102		pg_ts_parser,
103		pg_ts_template,
104		pg_type,
105		pg_user_mappings,
106	) = tokio::try_join!(
107		reflect_pg_aggregate(client),
108		reflect_pg_am(client),
109		reflect_pg_amop(client),
110		reflect_pg_amproc(client),
111		reflect_pg_attrdef(client),
112		reflect_pg_attribute(client),
113		reflect_pg_roles(client),
114		reflect_pg_auth_members(client),
115		reflect_pg_cast(client),
116		reflect_pg_class(client),
117		reflect_pg_collation(client),
118		reflect_pg_constraint(client),
119		reflect_pg_conversion(client),
120		reflect_pg_database(client),
121		reflect_pg_db_role_setting(client),
122		reflect_pg_default_acl(client),
123		// reflect_pg_depend(client),
124		// reflect_pg_description(client),
125		reflect_pg_enum(client),
126		reflect_pg_event_trigger(client),
127		reflect_pg_extension(client),
128		reflect_pg_foreign_data_wrapper(client),
129		reflect_pg_foreign_server(client),
130		reflect_pg_foreign_table(client),
131		reflect_pg_index(client),
132		reflect_pg_inherits(client),
133		// reflect_pg_init_privs(client),
134		reflect_pg_language(client),
135		reflect_pg_namespace(client),
136		reflect_pg_opclass(client),
137		reflect_pg_operator(client),
138		reflect_pg_opfamily(client),
139		reflect_pg_parameter_acl(client),
140		reflect_pg_partitioned_table(client),
141		reflect_pg_policy(client),
142		reflect_pg_proc(client),
143		reflect_pg_publication(client),
144		reflect_pg_publication_namespace(client),
145		reflect_pg_publication_rel(client),
146		reflect_pg_range(client),
147		// reflect_pg_replication_origin(client),
148		// reflect_pg_rewrite(client),
149		reflect_pg_rules(client),
150		reflect_pg_views(client),
151		reflect_pg_matviews(client),
152		// reflect_pg_seclabel(client),
153		reflect_pg_sequence(client),
154		// reflect_pg_shdepend(client),
155		// reflect_pg_shdescription(client),
156		// reflect_pg_shseclabel(client),
157		reflect_pg_statistic_ext(client),
158		reflect_pg_subscription(client),
159		reflect_pg_transform(client),
160		reflect_pg_trigger(client),
161		reflect_pg_ts_config(client),
162		reflect_pg_ts_config_map(client),
163		reflect_pg_ts_dict(client),
164		reflect_pg_ts_parser(client),
165		reflect_pg_ts_template(client),
166		reflect_pg_type(client),
167		reflect_pg_user_mappings(client),
168	)?;
169
170	Ok(DbState {
171		pg_aggregate,
172		pg_am,
173		pg_amop,
174		pg_amproc,
175		pg_attrdef,
176		pg_attribute,
177		pg_roles,
178		pg_auth_members,
179		pg_cast,
180		pg_class,
181		pg_collation,
182		pg_constraint,
183		pg_conversion,
184		pg_database,
185		pg_db_role_setting,
186		pg_default_acl,
187		// pg_depend,
188		// pg_description,
189		pg_enum,
190		pg_event_trigger,
191		pg_extension,
192		pg_foreign_data_wrapper,
193		pg_foreign_server,
194		pg_foreign_table,
195		pg_index,
196		pg_inherits,
197		// pg_init_privs,
198		pg_language,
199		pg_namespace,
200		pg_opclass,
201		pg_operator,
202		pg_opfamily,
203		pg_parameter_acl,
204		pg_partitioned_table,
205		pg_policy,
206		pg_proc,
207		pg_publication,
208		pg_publication_namespace,
209		pg_publication_rel,
210		pg_range,
211		// pg_replication_origin,
212		// pg_rewrite,
213		pg_rules,
214		pg_views,
215		pg_matviews,
216		// pg_seclabel,
217		pg_sequence,
218		// pg_shdepend,
219		// pg_shdescription,
220		// pg_shseclabel,
221		pg_statistic_ext,
222		pg_subscription,
223		pg_transform,
224		pg_trigger,
225		pg_ts_config,
226		pg_ts_config_map,
227		pg_ts_dict,
228		pg_ts_parser,
229		pg_ts_template,
230		pg_type,
231		pg_user_mappings,
232	})
233}
234
235
236pub use reflect_gen::reflect_pg_aggregate;
237
238pub use reflect_gen::reflect_pg_am;
239
240pub use reflect_gen::reflect_pg_amop;
241
242pub use reflect_gen::reflect_pg_amproc;
243
244pub use reflect_gen::reflect_pg_attrdef;
245
246pub use reflect_gen::reflect_pg_attribute;
247
248// `pg_authid`: https://www.postgresql.org/docs/17/catalog-pg-authid.html
249pub use reflect_gen::reflect_pg_roles;
250
251pub use reflect_gen::reflect_pg_auth_members;
252
253pub use reflect_gen::reflect_pg_cast;
254
255pub use reflect_gen::reflect_pg_class;
256
257pub use reflect_gen::reflect_pg_collation;
258
259pub use reflect_gen::reflect_pg_constraint;
260
261pub use reflect_gen::reflect_pg_conversion;
262
263pub use reflect_gen::reflect_pg_database;
264
265/// Asynchronously pull the contents of [`pg_db_role_setting`](https://www.postgresql.org/docs/17/catalog-pg-db-role-setting.html)
266pub async fn reflect_pg_db_role_setting(
267	client: &PgClient
268) -> Result<Vec<PgDbRoleSetting>, postgres::Error> {
269	let pg_db_role_setting_coll = queries_crate::queries::manual::reflect_pg_db_role_setting().bind(client)
270		.map(|pg_db_role_setting| {
271			PgDbRoleSetting {
272				setdatabase: if pg_db_role_setting.setdatabase { Some(()) } else { None },
273				setrole: pg_db_role_setting.setrole.map(Into::into),
274				setconfig: pg_db_role_setting.setconfig.map(|items| items.map(Into::into).collect()),
275			}
276		})
277		.iter()
278		.await?
279		.try_collect()
280		.await?;
281
282	Ok(pg_db_role_setting_coll)
283}
284
285pub use reflect_gen::reflect_pg_default_acl;
286
287// `pg_depend`: https://www.postgresql.org/docs/17/catalog-pg-depend.html
288
289// `pg_description`: https://www.postgresql.org/docs/17/catalog-pg-description.html
290
291/// Asynchronously pull the contents of [`pg_enum`](https://www.postgresql.org/docs/17/catalog-pg-enum.html)
292pub async fn reflect_pg_enum(
293	client: &PgClient
294) -> Result<Set<PgEnum>, postgres::Error> {
295	let pg_enum_coll = queries_crate::queries::manual::reflect_pg_enum().bind(client)
296		.map(|pg_enum| {
297			PgEnum {
298				enumtypid: pg_enum.enumtypid.into(),
299				enumlabels: pg_enum.enumlabels.map(Into::into).collect(),
300			}
301		})
302		.iter()
303		.await?
304		.try_collect()
305		.await?;
306
307	Ok(pg_enum_coll)
308}
309
310pub use reflect_gen::reflect_pg_event_trigger;
311
312pub use reflect_gen::reflect_pg_extension;
313
314pub use reflect_gen::reflect_pg_foreign_data_wrapper;
315
316pub use reflect_gen::reflect_pg_foreign_server;
317
318pub use reflect_gen::reflect_pg_foreign_table;
319
320pub use reflect_gen::reflect_pg_index;
321
322pub use reflect_gen::reflect_pg_inherits;
323
324// `pg_init_privs`: https://www.postgresql.org/docs/17/catalog-pg-init-privs.html
325
326pub use reflect_gen::reflect_pg_language;
327
328pub use reflect_gen::reflect_pg_namespace;
329
330pub use reflect_gen::reflect_pg_opclass;
331
332pub use reflect_gen::reflect_pg_operator;
333
334pub use reflect_gen::reflect_pg_opfamily;
335
336pub use reflect_gen::reflect_pg_parameter_acl;
337
338pub use reflect_gen::reflect_pg_partitioned_table;
339
340pub use reflect_gen::reflect_pg_policy;
341
342/// Asynchronously pull the contents of [`pg_proc`](https://www.postgresql.org/docs/17/catalog-pg-proc.html)
343pub async fn reflect_pg_proc(
344	client: &PgClient
345) -> Result<Set<PgProc>, postgres::Error> {
346	let pg_proc_coll = queries_crate::queries::manual::reflect_pg_proc().bind(client)
347		.map(|pg_proc| {
348			PgProc {
349				oid: pg_proc.oid.into(),
350				proname: pg_proc.proname.into(),
351				pronamespace: pg_proc.pronamespace.into(),
352				proowner: pg_proc.proowner.into(),
353				prolang: pg_proc.prolang.into(),
354				procost: pg_proc.procost.map(|n| ordered_float::NotNan::new(n).unwrap()),
355				prorows: pg_proc.prorows.map(|n| ordered_float::NotNan::new(n).unwrap()),
356				provariadic: pg_proc.provariadic.map(Str::new),
357				prosupport: pg_proc.prosupport.map(Str::new),
358				prokind: PgProcProkind::pg_from_char(pg_proc.prokind),
359				prosecdef: pg_proc.prosecdef,
360				proleakproof: pg_proc.proleakproof,
361				proisstrict: pg_proc.proisstrict,
362				proretset: pg_proc.proretset,
363				provolatile: PgProcProvolatile::pg_from_char(pg_proc.provolatile),
364				proparallel: PgProcProparallel::pg_from_char(pg_proc.proparallel),
365				pronargs: pg_proc.pronargs.unsigned_abs(),
366				pronargdefaults: pg_proc.pronargdefaults.unsigned_abs(),
367				prorettype: pg_proc.prorettype.into(),
368		    proargtypes: pg_proc.proargtypes.map(Str::new).collect(),
369				proallargtypes: pg_proc.proallargtypes.map(|items| items.map(Str::new).collect()),
370		    proargmodes: pg_proc.proargmodes.map(|items| items.map(PgProcProargmodes::pg_from_char).collect()),
371				proargnames: pg_proc.proargnames.map(|items| items.map(Into::into).collect()),
372		    proargdefaults: pg_proc.proargdefaults.map(|items| items.map(|item| item.map(Into::into)).collect()),
373				protrftypes: pg_proc.protrftypes.map(|items| items.map(Str::new).collect()),
374				prosrc: pg_proc.prosrc.map(Into::into),
375				probin: pg_proc.probin.map(Into::into),
376		    prosqlbody: pg_proc.prosqlbody.map(Into::into),
377				proconfig: pg_proc.proconfig.map(|items| items.map(Into::into).collect()),
378				proacl: pg_proc.proacl.map(|proacl| proacl.map(|acl| aclitems!(acl, FunctionAclItem, FunctionGrant)).collect()),
379				description: pg_proc.description.map(Into::into),
380			}
381		})
382		.iter()
383		.await?
384		.try_collect()
385		.await?;
386
387	Ok(pg_proc_coll)
388}
389
390pub use reflect_gen::reflect_pg_publication;
391
392pub use reflect_gen::reflect_pg_publication_namespace;
393
394pub use reflect_gen::reflect_pg_publication_rel;
395
396pub use reflect_gen::reflect_pg_range;
397
398// `pg_replication_origin`: https://www.postgresql.org/docs/17/catalog-pg-replication-origin.html
399
400// `pg_rewrite`: https://www.postgresql.org/docs/17/catalog-pg-rewrite.html
401pub use reflect_gen::reflect_pg_rules;
402pub use reflect_gen::reflect_pg_views;
403pub use reflect_gen::reflect_pg_matviews;
404
405// `pg_seclabel`: https://www.postgresql.org/docs/17/catalog-pg-seclabel.html
406
407pub use reflect_gen::reflect_pg_sequence;
408
409// `pg_shdepend`: https://www.postgresql.org/docs/17/catalog-pg-shdepend.html
410
411// `pg_shdescription`: https://www.postgresql.org/docs/17/catalog-pg-shdescription.html
412
413// `pg_shseclabel`: https://www.postgresql.org/docs/17/catalog-pg-shseclabel.html
414
415pub use reflect_gen::reflect_pg_statistic_ext;
416
417pub use reflect_gen::reflect_pg_subscription;
418
419pub use reflect_gen::reflect_pg_transform;
420
421pub use reflect_gen::reflect_pg_trigger;
422
423pub use reflect_gen::reflect_pg_ts_config;
424
425pub use reflect_gen::reflect_pg_ts_config_map;
426
427pub use reflect_gen::reflect_pg_ts_dict;
428
429pub use reflect_gen::reflect_pg_ts_parser;
430
431pub use reflect_gen::reflect_pg_ts_template;
432
433pub use reflect_gen::reflect_pg_type;
434
435pub use reflect_gen::reflect_pg_user_mappings;