1use rupg_common::{Error, Result, SqlState};
6use rupg_pgcatalog::builtin;
7use rupg_sql::Category;
8use rupg_types::Value;
9
10use crate::{Call, INTERNAL_LANGUAGE, bad_value};
11
12pub type SetKernel = fn(&Call<'_>, &[Value]) -> Result<Vec<Vec<Value>>>;
14
15pub fn set_kernel(func: u32) -> Option<SetKernel> {
17 let proc = builtin::proc_by_oid(func)?;
18 if proc.lang != INTERNAL_LANGUAGE {
19 return None;
20 }
21 let kernel: SetKernel = match proc.src {
22 "generate_series_int4" | "generate_series_step_int4" => series_int4,
23 "generate_series_int8" | "generate_series_step_int8" => series_int8,
24 "array_unnest" => unnest,
25 "generate_subscripts" | "generate_subscripts_nodir" => subscripts,
26 "text_to_table" | "text_to_table_null" => text_to_table,
27 "regexp_matches" | "regexp_matches_no_flags" => crate::regex::regexp_matches,
28 "regexp_split_to_table" | "regexp_split_to_table_no_flags" => {
29 crate::regex::regexp_split_to_table
30 }
31 "pg_options_to_table" => options_to_table,
32 "aclexplode" => crate::acl::aclexplode,
33 "pg_snapshot_xip" => crate::snapshot::snapshot_xip,
34 "range_minus_multi" => crate::range::range_minus_multi,
35 "multirange_unnest" => crate::multirange::unnest,
36 "multirange_minus_multi" => crate::multirange::minus_multi,
37 "tsvector_unnest" => crate::tsvector::unnest,
38 "ts_parse_byid" | "ts_parse_byname" => crate::tsearch::parse,
39 "ts_token_type_byid" | "ts_token_type_byname" => crate::tsearch::token_types,
40 "ts_stat1" => crate::tsstat::stat1,
41 "ts_stat2" => crate::tsstat::stat2,
42 "json_each" => crate::json::json_each,
43 "json_each_text" => crate::json::json_each_text,
44 "jsonb_each" => crate::json::jsonb_each,
45 "jsonb_each_text" => crate::json::jsonb_each_text,
46 "json_object_keys" => crate::json::json_object_keys,
47 "jsonb_object_keys" => crate::json::jsonb_object_keys,
48 "json_array_elements" => crate::json::json_array_elements,
49 "json_array_elements_text" => crate::json::json_array_elements_text,
50 "jsonb_array_elements" => crate::json::jsonb_array_elements,
51 "jsonb_path_query" => crate::jsonpath::jsonb_path_query,
52 "jsonb_path_query_tz" => crate::jsonpath::jsonb_path_query_tz,
53 "jsonb_array_elements_text" => crate::json::jsonb_array_elements_text,
54 "pg_partition_tree" | "pg_partition_ancestors" => crate::catinfo::partition_members,
55 "pg_get_keywords" => keywords,
56 "pg_get_wait_events" => wait_events,
57 "show_all_settings" => all_settings,
58 "pg_prepared_statement" => prepared_statements,
59 "pg_cursor" => cursors,
60 "pg_hba_file_rules" => hba_rules,
61 "pg_ident_file_mappings" => ident_mappings,
62 "pg_stat_get_activity" => crate::activity::activity,
63 "pg_stat_get_backend_idset" => crate::activity::backend_idset,
64 "pg_stat_get_progress_info" => crate::replication::progress_info,
65 "pg_stat_get_wal_senders"
66 | "pg_stat_get_subscription"
67 | "pg_get_replication_slots"
68 | "pg_prepared_xact" => crate::replication::none,
69 "pg_stat_get_wal_receiver" => crate::replication::wal_receiver,
70 "pg_stat_get_subscription_stats" => crate::replication::subscription_stats,
71 "pg_stat_get_replication_slot" => crate::replication::replication_slot,
72 "pg_get_aios" | "pg_get_dsm_registry_allocations" | "pg_show_replication_origin_status" => {
73 crate::absent::none
74 }
75 "pg_available_extensions" => crate::absent::extensions,
76 "pg_available_extension_versions" => crate::absent::extension_versions,
77 "show_all_file_settings" => file_settings,
78 "pg_config" => crate::absent::config,
79 "pg_stat_get_recovery" => crate::absent::recovery,
80 "pg_get_shmem_allocations_numa" => crate::absent::shmem_numa,
81 "pg_stats_ext_mcvlist_items" => crate::absent::mcv_list_items,
82 "pg_get_publication_tables_a" => crate::absent::publication_tables,
83 "pg_get_publication_tables_b" => crate::absent::publication_tables_of,
84 "pg_get_publication_sequences" => crate::absent::publication_sequences,
85 "pg_stat_get_wal" => crate::shared::wal,
86 "pg_stat_get_io" => crate::shared::io,
87 "pg_stat_get_archiver" => crate::shared::archiver,
88 "pg_stat_get_slru" => crate::shared::slru,
89 "pg_stat_get_recovery_prefetch" => crate::shared::recovery_prefetch,
90 "pg_stat_get_lock" => crate::shared::lock,
91 "pg_stat_get_autovacuum_scores" => crate::autovacuum::scores,
92 "pg_tablespace_databases" => crate::dbsize::tablespace_databases,
93 "pg_timezone_names" => crate::zones::names,
94 "pg_timezone_abbrevs_zone" => crate::zones::abbrevs_zone,
95 "pg_timezone_abbrevs_abbrevs" => crate::zones::abbrevs_abbrevs,
96 src => return crate::calendar::set_kernel(src),
97 };
98 Some(kernel)
99}
100
101fn int(args: &[Value], at: usize) -> Result<i64> {
103 args.get(at).and_then(Value::as_i64).ok_or_else(bad_value)
104}
105
106fn series(
108 call: &Call<'_>,
109 args: &[Value],
110 min: i64,
111 max: i64,
112 kind: fn(i64) -> Value,
113) -> Result<Vec<Vec<Value>>> {
114 let (mut current, finish) = (int(args, 0)?, int(args, 1)?);
115 let step = if args.len() > 2 { int(args, 2)? } else { 1 };
116 if step == 0 {
117 return Err(Error::new(SqlState::INVALID_PARAMETER_VALUE, "step size cannot equal zero"));
118 }
119 let most = call.want.unwrap_or(usize::MAX);
120 let mut rows = Vec::new();
121 while rows.len() < most && ((step > 0 && current <= finish) || (step < 0 && current >= finish))
122 {
123 rows.push(vec![kind(current)]);
124 match current.checked_add(step).filter(|n| (min..=max).contains(n)) {
125 Some(next) => current = next,
126 None => break,
127 }
128 }
129 Ok(rows)
130}
131
132fn series_int4(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
134 series(call, args, i64::from(i32::MIN), i64::from(i32::MAX), |n| {
135 Value::Int4(i32::try_from(n).unwrap_or_default())
136 })
137}
138
139fn series_int8(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
141 series(call, args, i64::MIN, i64::MAX, Value::Int8)
142}
143
144fn unnest(_: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
146 let Some(Value::Array(array)) = args.first() else { return Err(bad_value()) };
147 Ok(array.values.iter().map(|v| vec![v.clone().unwrap_or(Value::Null)]).collect())
148}
149
150fn subscripts(_: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
152 let Some(Value::Array(array)) = args.first() else { return Err(bad_value()) };
153 let reverse = args.get(2).and_then(Value::as_bool).unwrap_or(false);
154 let dim = int(args, 1)?;
155 let Some(dim) = usize::try_from(dim - 1).ok().and_then(|d| array.dims.get(d)) else {
156 return Ok(Vec::new());
157 };
158 let (lower, upper) = (i64::from(dim.lower), i64::from(dim.lower) + i64::from(dim.len) - 1);
159 let mut rows: Vec<Vec<Value>> =
160 (lower..=upper).map(|n| vec![Value::Int4(i32::try_from(n).unwrap_or_default())]).collect();
161 if reverse {
162 rows.reverse();
163 }
164 Ok(rows)
165}
166
167fn text_to_table(_: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
169 let Some(input) = args.first().and_then(Value::as_str) else { return Ok(Vec::new()) };
170 let separator = args.get(1).and_then(Value::as_str);
171 let null = args.get(2).and_then(Value::as_str);
172 let parts = crate::array::split(input, separator, null);
173 Ok(parts.into_iter().map(|v| vec![v.unwrap_or(Value::Null)]).collect())
174}
175
176fn options_to_table(_: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
178 let Some(Value::Array(array)) = args.first() else { return Err(bad_value()) };
179 let mut rows = Vec::with_capacity(array.values.len());
180 for option in array.values.iter().flatten() {
181 let option = option.as_str().ok_or_else(bad_value)?;
182 rows.push(match option.split_once('=') {
183 Some((name, value)) => vec![Value::text(name), Value::text(value)],
184 None => vec![Value::text(option), Value::Null],
185 });
186 }
187 Ok(rows)
188}
189
190fn all_settings(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
192 Ok(call.session.all_settings())
193}
194
195fn prepared_statements(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
197 Ok(call.session.prepared_statements())
198}
199
200fn cursors(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
202 Ok(call.session.cursors())
203}
204
205fn hba_rules(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
207 call.session.hba_rules()
208}
209
210fn file_settings(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
212 Ok(call.session.file_settings())
213}
214
215fn ident_mappings(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
217 call.session.ident_mappings()
218}
219
220fn wait_events(_: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
222 Ok(crate::generated::wait_events::WAIT_EVENTS
223 .iter()
224 .map(|&(kind, name, description)| {
225 vec![Value::text(kind), Value::text(name), Value::text(description)]
226 })
227 .collect())
228}
229
230fn keywords(_: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
232 Ok(rupg_sql::keywords()
233 .iter()
234 .map(|k| {
235 let (code, desc) = match k.category {
236 Category::Unreserved => (b'U', "unreserved"),
237 Category::ColumnName => (b'C', "unreserved (cannot be function or type name)"),
238 Category::TypeFuncName => (b'T', "reserved (can be function or type name)"),
239 Category::Reserved => (b'R', "reserved"),
240 };
241 let bare = if k.bare_label { "can be bare label" } else { "requires AS" };
242 vec![
243 Value::text(k.name),
244 Value::Char(code),
245 Value::Bool(k.bare_label),
246 Value::text(desc),
247 Value::text(bare),
248 ]
249 })
250 .collect())
251}