@@ -5,10 +5,20 @@ use std::path::Path;
55use std:: process:: { Command , Stdio } ;
66
77use anyhow:: { Context , Result , bail} ;
8- use serde:: Deserialize ;
8+ use serde:: de:: DeserializeOwned ;
9+ use serde:: { Deserialize , Deserializer } ;
910
1011use crate :: path:: project_root;
1112
13+ fn deserialize_json_string < ' de , D , T > ( deserializer : D ) -> Result < T , D :: Error >
14+ where
15+ D : Deserializer < ' de > ,
16+ T : DeserializeOwned ,
17+ {
18+ let s = String :: deserialize ( deserializer) ?;
19+ serde_json:: from_str ( & s) . map_err ( serde:: de:: Error :: custom)
20+ }
21+
1222const PG_TEMP_SCHEMA_SQL : & str = "create schema pg_temp;\n \n " ;
1323
1424#[ derive( Deserialize ) ]
@@ -129,7 +139,8 @@ struct RelationQuery {
129139 relkind : String ,
130140 description : String ,
131141 extension_name : String ,
132- columns : String ,
142+ #[ serde( deserialize_with = "deserialize_json_string" ) ]
143+ columns : Vec < Column > ,
133144}
134145
135146impl Query for RelationQuery {
@@ -212,6 +223,49 @@ order by n.nspname, p.proname, pg_get_function_arguments(p.oid), pg_get_function
212223" ;
213224}
214225
226+ #[ derive( Deserialize ) ]
227+ struct CompositeTypeQuery {
228+ schema : String ,
229+ name : String ,
230+ description : String ,
231+ extension_name : String ,
232+ #[ serde( deserialize_with = "deserialize_json_string" ) ]
233+ columns : Vec < Column > ,
234+ }
235+
236+ impl Query for CompositeTypeQuery {
237+ const QUERY : & ' static str = r"
238+ select
239+ n.nspname as schema,
240+ t.typname as name,
241+ coalesce(d.description, '') as description,
242+ coalesce(ext.extname, 'builtins') as extension_name,
243+ json_agg(
244+ json_build_object(
245+ 'name', a.attname,
246+ 'data_type', format_type(a.atttypid, a.atttypmod)
247+ )
248+ order by a.attnum
249+ ) as columns
250+ from pg_type t
251+ join pg_namespace n on n.oid = t.typnamespace
252+ join pg_class c on c.oid = t.typrelid
253+ join pg_attribute a on a.attrelid = c.oid
254+ left join pg_description d on d.objoid = t.oid and d.classoid = 'pg_type'::regclass
255+ left join pg_depend dep on dep.classid = 'pg_type'::regclass and dep.objid = t.oid and dep.objsubid = 0 and dep.deptype = 'e'
256+ left join pg_extension ext on ext.oid = dep.refobjid
257+ where n.nspname not like 'pg_temp%'
258+ and n.nspname not like 'pg_toast%'
259+ and (n.nspname != 'public' or ext.extname is not null)
260+ and t.typtype = 'c'
261+ and c.relkind = 'c'
262+ and a.attnum > 0
263+ and not a.attisdropped
264+ group by t.oid, n.nspname, t.typname, d.description, ext.extname
265+ order by n.nspname, t.typname, t.oid;
266+ " ;
267+ }
268+
215269#[ derive( Deserialize ) ]
216270struct OperatorQuery {
217271 schema : String ,
@@ -282,6 +336,7 @@ begin
282336 'plpgsql',
283337 'postgis',
284338 'postgres_fdw',
339+ 'tablefunc',
285340 'vector'
286341 ] loop
287342 execute format('create extension if not exists %I', extension_name);
@@ -363,6 +418,31 @@ impl WriteSql for RangeTypeDef {
363418 }
364419}
365420
421+ struct CompositeTypeDef {
422+ schema : String ,
423+ name : String ,
424+ description : String ,
425+ columns : Vec < Column > ,
426+ }
427+
428+ impl WriteSql for CompositeTypeDef {
429+ fn write_sql < W : Write > ( & self , f : & mut W ) -> io:: Result < ( ) > {
430+ write_description ( f, & self . description ) ?;
431+ writeln ! ( f, "create type {}.{} as (" , self . schema, self . name) ?;
432+ for ( index, column) in self . columns . iter ( ) . enumerate ( ) {
433+ let comma = if index + 1 < self . columns . len ( ) {
434+ ","
435+ } else {
436+ ""
437+ } ;
438+ writeln ! ( f, " {} {}{comma}" , column. name, column. data_type) ?;
439+ }
440+ writeln ! ( f, ");" ) ?;
441+ writeln ! ( f) ?;
442+ Ok ( ( ) )
443+ }
444+ }
445+
366446struct TableDef {
367447 schema : String ,
368448 name : String ,
@@ -552,6 +632,7 @@ struct Module {
552632 schemas : Vec < SchemaDef > ,
553633 types : Vec < TypeDef > ,
554634 range_types : Vec < RangeTypeDef > ,
635+ composite_types : Vec < CompositeTypeDef > ,
555636 tables : Vec < TableDef > ,
556637 views : Vec < ViewDef > ,
557638 functions : Vec < FunctionDef > ,
@@ -572,6 +653,10 @@ impl Module {
572653 range_type. write_sql ( f) ?;
573654 }
574655
656+ for composite_type in & self . composite_types {
657+ composite_type. write_sql ( f) ?;
658+ }
659+
575660 for table in & self . tables {
576661 table. write_sql ( f) ?;
577662 }
@@ -663,21 +748,35 @@ fn query_types(modules: &mut BTreeMap<String, Module>) -> Result<()> {
663748 Ok ( ( ) )
664749}
665750
751+ fn query_composite_types ( modules : & mut BTreeMap < String , Module > ) -> Result < ( ) > {
752+ for row in CompositeTypeQuery :: run ( ) ? {
753+ modules
754+ . entry ( row. extension_name )
755+ . or_default ( )
756+ . composite_types
757+ . push ( CompositeTypeDef {
758+ columns : row. columns ,
759+ description : row. description ,
760+ name : row. name ,
761+ schema : row. schema ,
762+ } ) ;
763+ }
764+
765+ Ok ( ( ) )
766+ }
767+
666768fn query_relations ( modules : & mut BTreeMap < String , Module > ) -> Result < ( ) > {
667769 for row in RelationQuery :: run ( ) ? {
668- let columns: Vec < Column > =
669- serde_json:: from_str ( & row. columns ) . context ( "expected valid column json" ) ?;
670-
671770 let module = modules. entry ( row. extension_name ) . or_default ( ) ;
672771 match row. relkind . as_str ( ) {
673772 "r" => module. tables . push ( TableDef {
674- columns,
773+ columns : row . columns ,
675774 description : row. description ,
676775 name : row. name ,
677776 schema : row. schema ,
678777 } ) ,
679778 "v" => module. views . push ( ViewDef {
680- columns,
779+ columns : row . columns ,
681780 description : row. description ,
682781 name : row. name ,
683782 schema : row. schema ,
@@ -759,6 +858,7 @@ pub(crate) fn sync_builtins() -> Result<()> {
759858
760859 query_schemas ( & mut modules) ?;
761860 query_types ( & mut modules) ?;
861+ query_composite_types ( & mut modules) ?;
762862 query_relations ( & mut modules) ?;
763863 query_functions ( & mut modules) ?;
764864 query_operators ( & mut modules) ?;
0 commit comments