@@ -29,31 +29,57 @@ internal class RoutineQuery()
2929 else '' end volatility_option,
3030 proc.proretset as returns_set,
3131 (r.type_udt_schema || '.' || r.type_udt_name)::regtype::text as return_type,
32+
3233 coalesce(
3334 array_agg(p.parameter_name::text order by p.ordinal_position) filter(where p.parameter_mode = 'IN' or p.parameter_mode = 'INOUT'),
3435 '{}'::text[]
3536 ) as in_params,
37+
3638 coalesce(
37- array_agg(p.parameter_name::text order by p.ordinal_position) filter(where p.parameter_mode = 'INOUT' or p.parameter_mode = 'OUT'),
39+ case when r.data_type = 'USER-DEFINED' then
40+ (
41+ select array_agg(column_name order by col.ordinal_position)
42+ from information_schema.columns col
43+ where table_schema = r.type_udt_schema and table_name = r.type_udt_name
44+ )
45+ else
46+ array_agg(p.parameter_name::text order by p.ordinal_position) filter(where p.parameter_mode = 'INOUT' or p.parameter_mode = 'OUT')
47+ end,
48+
3849 '{}'::text[]
3950 ) as out_params,
51+
4052 coalesce(
4153 array_agg(
4254 case when p.data_type = 'bit' then 'varbit' else (p.udt_schema || '.' || p.udt_name)::regtype::text end
4355 order by p.ordinal_position
4456 ) filter(where p.parameter_mode = 'IN' or p.parameter_mode = 'INOUT'),
4557 '{}'::text[]
4658 ) as in_param_types,
59+
4760 coalesce(
48- array_agg(
49- case when p.data_type = 'bit' then 'varbit' else (p.udt_schema || '.' || p.udt_name)::regtype::text end
50- order by p.ordinal_position) filter(where p.parameter_mode = 'INOUT' or p.parameter_mode = 'OUT'),
51- '{}'::text[]
61+ case when r.data_type = 'USER-DEFINED' then
62+ (
63+ select array_agg(
64+ case when col.data_type = 'bit' then 'varbit' else (col.udt_schema || '.' || col.udt_name)::regtype::text end
65+ order by col.ordinal_position
66+ )
67+ from information_schema.columns col
68+ where table_schema = r.type_udt_schema and table_name = r.type_udt_name
69+ )
70+ else
71+ array_agg(
72+ case when p.data_type = 'bit' then 'varbit' else (p.udt_schema || '.' || p.udt_name)::regtype::text end
73+ order by p.ordinal_position) filter(where p.parameter_mode = 'INOUT' or p.parameter_mode = 'OUT')
74+ end, '{}'::text[]
5275 ) as out_param_types,
76+
5377 coalesce(
5478 array_agg(p.parameter_default order by p.ordinal_position) filter(where p.parameter_mode = 'IN' or p.parameter_mode = 'INOUT'),
5579 '{}'::text[]
56- ) as in_param_defaults
80+ ) as in_param_defaults,
81+
82+ case when proc.provariadic <> 0 then true else false end as has_variadic
5783 from
5884 information_schema.routines r
5985 join pg_catalog.pg_proc proc on r.specific_name = proc.proname || '_' || proc.oid
@@ -84,8 +110,9 @@ and r.type_udt_name <> 'trigger'
84110 group by
85111 r.routine_type, r.specific_schema, r.routine_name,
86112 proc.oid, r.specific_name, r.external_language, des.description,
87- r.security_type, r.type_udt_schema, r.type_udt_name,
88- proc.proisstrict, proc.procost, proc.prorows, proc.proparallel, proc.provolatile, proc.proretset
113+ r.security_type, r.data_type, r.type_udt_schema, r.type_udt_name,
114+ proc.proisstrict, proc.procost, proc.prorows, proc.proparallel, proc.provolatile,
115+ proc.proretset, proc.provariadic
89116)
90117select
91118 type,
@@ -106,6 +133,7 @@ group by
106133 when returns_set is true and return_type <> 'record' then 'record'
107134 else return_type
108135 end as return_type,
136+
109137 case
110138 when case when returns_set is true or return_type = 'record' then true else false end then
111139 case when array_length(out_params, 1) is null then 1 else array_length(out_params, 1) end
@@ -121,11 +149,15 @@ when returns_set is true and return_type <> 'record' then 'record'
121149 case when array_length(out_params, 1) is null then array[return_type]::text[] else out_param_types end
122150 else array[]::text[]
123151 end as return_record_types,
152+
124153 returns_set is true and return_type <> 'record' and array_length(out_params, 1) is null as returns_unnamed_set,
154+ returns_set is true and return_type = 'record' and array_length(out_params, 1) is null as is_unnamed_record,
155+
125156 array_length(in_params, 1) as param_count,
126157 in_params as param_names,
127158 in_param_types as param_types,
128159 in_param_defaults as param_defaults,
160+ has_variadic,
129161 pg_get_functiondef(oid) as definition
130162from cte" ;
131163
@@ -191,9 +223,13 @@ void AddParameter(object? value, bool isArray = false) => command?
191223 . Select ( ( x , i ) => new TypeDescriptor ( x , hasDefault : paramDefaults [ i ] is not null ) )
192224 . ToArray ( ) ;
193225
226+ bool hasVariadic = reader . Get < bool > ( "has_variadic" ) ;
227+ bool isUnnamedRecord = reader . Get < bool > ( "is_unnamed_record" ) ;
228+
194229 Dictionary < int , string > expressions = [ ] ;
230+
195231 var expression = string . Concat (
196- ( isVoid || ! returnsRecord || ( returnsRecord && returnsUnnamedSet ) ) ?
232+ ( isVoid || ! returnsRecord || ( returnsRecord && returnsUnnamedSet ) || isUnnamedRecord ) ?
197233 "select " :
198234 string . Concat ( "select " , string . Join ( ", " , returnRecordNames ) , " from " ) ,
199235 schema ,
@@ -210,11 +246,12 @@ void AddParameter(object? value, bool isArray = false) => command?
210246 . Select ( i =>
211247 {
212248 var descriptor = paramTypeDescriptor [ i - 1 ] ;
249+ string prefix = hasVariadic && i == paramCount ? "variadic " : "" ;
213250 if ( descriptor . IsCastToText ( ) )
214251 {
215- return $ "${ i } ::{ descriptor . OriginalType } ";
252+ return $ "{ prefix } ${ i } ::{ descriptor . OriginalType } ";
216253 }
217- return $ "${ i } ";
254+ return $ "{ prefix } ${ i } ";
218255 } ) ) ,
219256 ")" ) ;
220257 }
@@ -240,7 +277,7 @@ void AddParameter(object? value, bool isArray = false) => command?
240277 returnRecordCount : reader . Get < int > ( "return_record_count" ) ,
241278 returnRecordNames : returnRecordNames ,
242279 returnRecordTypes : returnRecordTypes ,
243- returnsUnnamedSet : returnsUnnamedSet ,
280+ returnsUnnamedSet : returnsUnnamedSet || isUnnamedRecord ,
244281 paramCount : paramCount ,
245282 paramNames : paramNames ,
246283 paramTypes : paramTypes ,
0 commit comments