Skip to main content

rupg_func/
srf.rs

1//! The set-returning functions that a query can call in `FROM` or in its target list: `generate_series` of the integer types, of the timestamps and of `numeric`, `unnest`, `generate_subscripts`, `string_to_table`, `regexp_matches`, `regexp_split_to_table`, `pg_options_to_table`, `aclexplode`, `range_minus_multi`, `multirange_unnest`, `multirange_minus_multi`, `unnest` of a `tsvector`, `ts_parse`, `ts_token_type`, `ts_stat`, `pg_partition_tree`, `pg_partition_ancestors`, `pg_get_keywords`, `pg_show_all_settings`, `pg_stat_get_activity`, `pg_stat_get_backend_idset`, `pg_stat_get_autovacuum_scores`, `pg_prepared_statement`, `pg_hba_file_rules`, `pg_ident_file_mappings`, the functions of the views of replication, of progress and of the prepared transactions, the functions of the statistics of the whole server that give rows, the functions of the views of asynchronous I/O, shared memory, extended statistics, replication origins, recovery and publications, `pg_cursor`, and the functions of the views of the time zones.
2//!
3//! A set kernel gives all the rows of the call at once, as a function of PostgreSQL in the materialize mode gives a tuplestore. Each row has one value for each column of the result: one value for a function of a scalar type, or one value for each `OUT` parameter.
4
5use 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
12/// The code of a set-returning function. It gets the values of the arguments and gives the rows of the result. A strict function does not get a null value, because the executor gives no rows without the call.
13pub type SetKernel = fn(&Call<'_>, &[Value]) -> Result<Vec<Vec<Value>>>;
14
15/// The set kernel of the function with this `pg_proc` OID, or `None` if the engine does not have it.
16pub 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
101/// The integer value of an argument.
102fn int(args: &[Value], at: usize) -> Result<i64> {
103    args.get(at).and_then(Value::as_i64).ok_or_else(bad_value)
104}
105
106/// `generate_series_step_int8`: the numbers from the start to the stop, with the step or 1. The series stops before a number that overflows `kind`, or after the rows that the caller reads.
107fn 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
132/// `generate_series_int4` and `generate_series_step_int4`.
133fn 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
139/// `generate_series_int8` and `generate_series_step_int8`.
140fn series_int8(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
141    series(call, args, i64::MIN, i64::MAX, Value::Int8)
142}
143
144/// `array_unnest`: the elements of the array in the order of storage, for all the dimensions.
145fn 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
150/// `generate_subscripts`: the subscripts of one dimension of the array, from the lower bound up, or down with the third argument true. A dimension that the array does not have gives no rows.
151fn 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
167/// `text_to_table`: the parts of the string between the separators. The function is not strict: a null string gives no rows, and a null separator gives each character. An empty separator gives the whole string, and an empty string gives no rows. A part equal to the third argument is null.
168fn 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
176/// `pg_options_to_table` with `untransformRelOptions`: each option of the form `name=value` gives its name and its value. An option with no `=` has a null value.
177fn 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
190/// `show_all_settings`: the parameters of the session with their values, which the view `pg_settings` reads.
191fn all_settings(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
192    Ok(call.session.all_settings())
193}
194
195/// `pg_prepared_statement`: the named prepared statements of the session, which the view `pg_prepared_statements` reads.
196fn prepared_statements(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
197    Ok(call.session.prepared_statements())
198}
199
200/// `pg_cursor`: the open cursors of the session, which the view `pg_cursors` reads.
201fn cursors(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
202    Ok(call.session.cursors())
203}
204
205/// `pg_hba_file_rules`: the lines of the host rules, which the view `pg_hba_file_rules` reads.
206fn hba_rules(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
207    call.session.hba_rules()
208}
209
210/// `pg_show_all_file_settings`: the lines of the configuration files, which the view `pg_file_settings` reads.
211fn file_settings(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
212    Ok(call.session.file_settings())
213}
214
215/// `pg_ident_file_mappings`: the lines of the user maps, which the view `pg_ident_file_mappings` reads.
216fn ident_mappings(call: &Call<'_>, _: &[Value]) -> Result<Vec<Vec<Value>>> {
217    call.session.ident_mappings()
218}
219
220/// `pg_get_wait_events`: the wait events of `wait_event_names.txt`. PostgreSQL also gives the events that extensions add, and rupg has no extensions.
221fn 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
230/// `pg_get_keywords`: each keyword of the parser with its category and its description, and whether it can be a column label without `AS`.
231fn 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}