Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions crates/rupg-analyze/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,13 @@ fn write_value(value: &Value, out: &mut String) {
hex(&bytes, out);
out.push(')');
}
Value::JsonPath(path) => {
let mut bytes = Vec::new();
rupg_types::jsonpath_send(path, &mut bytes);
out.push_str("(jsonpath ");
hex(&bytes, out);
out.push(')');
}
Value::Range(range) => write_range(range, out),
Value::Multirange(ranges) => {
out.push_str("(multirange");
Expand Down Expand Up @@ -807,6 +814,13 @@ fn value_of(item: &Item) -> Result<Value> {
Value::TsQuery(value)
}
}
"jsonpath" => {
let bytes = bytes_of(part(0)?)?;
// The input checked the patterns of `like_regex` when it made the value.
let value = rupg_types::jsonpath_recv(&mut Recv::new(&bytes), &|_, _| Ok(()))
.map_err(|_| bad("the bytes of a jsonpath"))?;
Value::JsonPath(Box::new(value))
}
"oid" => Value::Oid(number(part(0)?)?),
"char" => Value::Char(number(part(0)?)?),
"text" => match parts.first() {
Expand Down Expand Up @@ -956,6 +970,17 @@ mod tests {
),
oid::TSQUERY,
),
(
Value::JsonPath(Box::new(
rupg_types::jsonpath_parse(
"strict $.a[1 to last] ? (@ like_regex \"^b\" flag \"i\").**{2}",
false,
&|_, _| Ok(()),
)
.expect("jsonpath"),
)),
oid::JSONPATH,
),
(
Value::Multirange(vec![
Range {
Expand Down
23 changes: 23 additions & 0 deletions crates/rupg-func/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ pub fn output_supported(ty: u32) -> bool {
| oid::TXID_SNAPSHOT
| oid::TSVECTOR
| oid::TSQUERY
| oid::JSONPATH
) || RegKind::from_oid(ty).is_some()
|| is_row_type(ty)
}
Expand Down Expand Up @@ -195,6 +196,10 @@ pub fn output(ty: u32, value: &Value, session: &dyn Session, out: &mut Vec<u8>)
types::tsquery_deparse(items, out);
return Ok(());
}
if let Value::JsonPath(path) = value {
types::jsonpath_deparse(path, out);
return Ok(());
}
if let (Some(kind), Value::Bytea(v)) = (types::GeoKind::from_oid(ty), value) {
types::geo_out(kind, v, session.extra_float_digits(), out);
return Ok(());
Expand Down Expand Up @@ -498,6 +503,19 @@ fn tsquery_in(
Ok(items)
}

/// `jsonpath_in`. A number that is too large and a path that is too deep are errors that `pg_input_is_valid` does not catch. The error for a code point that is too large is not the same in the soft input, as in PostgreSQL.
fn jsonpath_in(text: &str, noisy: bool) -> std::result::Result<types::JsonPath, Failed> {
types::jsonpath_parse(text, !noisy, &crate::regex::validate).map_err(|error| {
if error.sqlstate == SqlState::NUMERIC_VALUE_OUT_OF_RANGE
|| error.sqlstate == SqlState::STATEMENT_TOO_COMPLEX
{
Failed::Hard(type_error(error))
} else {
Failed::from(error)
}
})
}

/// The text input function of the type. `noisy` is false for the soft input of `pg_input_is_valid`, which gives no notices, as `escontext` does in PostgreSQL.
fn input_value(
ty: u32,
Expand Down Expand Up @@ -555,6 +573,7 @@ fn input_value(
oid::INT4 => Value::Int4(types::int4_in(text)?),
oid::TSVECTOR => Value::TsVector(types::tsvector_parse(text)?),
oid::TSQUERY => Value::TsQuery(tsquery_in(text, session, noisy)?),
oid::JSONPATH => Value::JsonPath(Box::new(jsonpath_in(text, noisy)?)),
oid::INT8 => Value::Int8(types::int8_in(text)?),
oid::OID => Value::Oid(types::oid_in(text)?),
oid::MONEY => Value::Int8(types::cash_in(text)?),
Expand Down Expand Up @@ -815,6 +834,9 @@ fn receive_from(ty: u32, recv: &mut Recv<'_>, typmod: i32) -> std::result::Resul
oid::OID => Value::Oid(recv.u32()?),
oid::TSVECTOR => Value::TsVector(types::tsvector_recv(recv)?),
oid::TSQUERY => Value::TsQuery(types::tsquery_recv(recv)?),
oid::JSONPATH => {
Value::JsonPath(Box::new(types::jsonpath_recv(recv, &crate::regex::validate)?))
}
ty if RegKind::from_oid(ty).is_some() => Value::Oid(recv.u32()?),
oid::JSON => Value::text(types::json_in(recv.text()?)?),
oid::JSONB => Value::Text(types::jsonb_recv(recv.rest())?),
Expand Down Expand Up @@ -926,6 +948,7 @@ pub fn send(ty: u32, value: &Value, out: &mut Vec<u8>) -> Result<()> {
(_, Value::Multirange(ranges)) => multirange_send(ty, ranges, out)?,
(_, Value::TsVector(lexemes)) => types::tsvector_send(lexemes, out),
(_, Value::TsQuery(items)) => types::tsquery_send(items, out),
(_, Value::JsonPath(path)) => types::jsonpath_send(path, out),
(_, Value::Null) => return Err(Error::internal("the send function got a null value")),
_ => return Err(not_yet(format!("binary output of type {}", type_name(ty)))),
}
Expand Down
5 changes: 5 additions & 0 deletions crates/rupg-func/src/regex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ pub fn auth_compile(pattern: &str) -> std::result::Result<(), &'static str> {
Regex::compile(pattern, flags::ADVANCED).map(drop).map_err(Code::message)
}

/// The check of the pattern of `like_regex` in a `jsonpath`: the message of `pg_regerror` when `pattern` is not valid with the `cflags`.
pub(crate) fn validate(pattern: &str, cflags: u32) -> std::result::Result<(), String> {
Regex::compile(pattern, cflags).map(drop).map_err(|code| code.message().to_string())
}

/// `regexec_auth_token`: find the regular expression `pattern` of [`auth_compile`] in `s`. The result is `None` when the pattern does not match.
///
/// # Errors
Expand Down
6 changes: 6 additions & 0 deletions crates/rupg-session/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1874,6 +1874,12 @@ mod tests {
big_stack(|| script(include_str!("connection/tsquery.test")));
}

/// The type `jsonpath`: the text input with its keys, variables, literals, operators, filters, subscripts and methods, the escapes and the comments, the errors of the scanner and of the parser at the same token as PostgreSQL, the checks of `@` and `last`, the output, arrays of `jsonpath`, `pg_input_is_valid`, `pg_input_error_info`, the text and binary forms of `COPY` and a query with parameters. `connection/jsonpath.test` is the output of PostgreSQL 19 for the same script in a new database.
#[test]
fn jsonpath() {
big_stack(|| script(include_str!("connection/jsonpath.test")));
}

/// The operators `@@` and `@@@` of `tsvector` and `tsquery`: the operators `&`, `|` and `!`, weights, prefixes, a vector with no positions, `<->` and `<N>` with `!`, `&` and `|` under them, the largest distance, the bits of a position that PostgreSQL keeps, `GROUP BY` and a query with parameters. `connection/ts_match.test` is the output of PostgreSQL 19 for the same script in a new database.
#[test]
fn ts_match() {
Expand Down
Loading
Loading