2022-09-02 00:32:57 +00:00
|
|
|
//! # Parse an InfluxQL [bind parameter]
|
|
|
|
//!
|
|
|
|
//! Bind parameters are parsed where a literal value may appear and are prefixed
|
|
|
|
//! by a `$`. Per the original Go [implementation], the token following the `$` is
|
|
|
|
//! parsed as an identifier, and therefore may appear in double quotes.
|
|
|
|
//!
|
|
|
|
//! [bind parameter]: https://docs.influxdata.com/influxdb/v1.8/tools/api/#bind-parameters
|
|
|
|
//! [implementation]: https://github.com/influxdata/influxql/blob/df51a45762be9c1b578f01718fa92d286a843fe9/scanner.go#L57-L62
|
|
|
|
|
2022-09-15 00:19:03 +00:00
|
|
|
use crate::internal::ParseResult;
|
2022-09-02 00:32:57 +00:00
|
|
|
use crate::string::double_quoted_string;
|
2022-10-13 22:37:49 +00:00
|
|
|
use crate::{impl_tuple_clause, write_quoted_string};
|
2022-09-02 00:32:57 +00:00
|
|
|
use nom::branch::alt;
|
|
|
|
use nom::bytes::complete::tag;
|
|
|
|
use nom::character::complete::{alphanumeric1, char};
|
|
|
|
use nom::combinator::{map, recognize};
|
|
|
|
use nom::multi::many1_count;
|
|
|
|
use nom::sequence::preceded;
|
|
|
|
use std::fmt;
|
|
|
|
use std::fmt::{Display, Formatter, Write};
|
|
|
|
|
|
|
|
/// Parse an unquoted InfluxQL bind parameter.
|
2022-09-15 06:52:31 +00:00
|
|
|
fn unquoted_parameter(i: &str) -> ParseResult<&str, &str> {
|
|
|
|
recognize(many1_count(alt((alphanumeric1, tag("_")))))(i)
|
2022-09-02 00:32:57 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 06:52:31 +00:00
|
|
|
/// A type that represents an InfluxQL bind parameter.
|
2022-09-02 00:32:57 +00:00
|
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
2022-10-13 22:37:49 +00:00
|
|
|
pub struct BindParameter(pub(crate) String);
|
2022-09-02 00:32:57 +00:00
|
|
|
|
2022-10-13 22:37:49 +00:00
|
|
|
impl_tuple_clause!(BindParameter, String);
|
2022-09-15 06:52:31 +00:00
|
|
|
|
|
|
|
impl From<&str> for BindParameter {
|
|
|
|
fn from(s: &str) -> Self {
|
|
|
|
Self(s.to_string())
|
|
|
|
}
|
2022-09-02 00:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for BindParameter {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
2022-09-15 06:52:31 +00:00
|
|
|
f.write_char('$')?;
|
|
|
|
write_quoted_string!(f, '"', self.0.as_str(), unquoted_parameter, '\n' => "\\n", '\\' => "\\\\", '"' => "\\\"");
|
2022-09-02 00:32:57 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses an InfluxQL [BindParameter].
|
2022-10-13 22:37:49 +00:00
|
|
|
pub(crate) fn parameter(i: &str) -> ParseResult<&str, BindParameter> {
|
2022-09-02 00:32:57 +00:00
|
|
|
// See: https://github.com/influxdata/influxql/blob/df51a45762be9c1b578f01718fa92d286a843fe9/scanner.go#L358-L362
|
|
|
|
preceded(
|
|
|
|
char('$'),
|
|
|
|
alt((
|
2022-09-15 06:52:31 +00:00
|
|
|
map(unquoted_parameter, Into::into),
|
|
|
|
map(double_quoted_string, Into::into),
|
2022-09-02 00:32:57 +00:00
|
|
|
)),
|
|
|
|
)(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parameter() {
|
|
|
|
// all ascii
|
|
|
|
let (_, got) = parameter("$cpu").unwrap();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, "cpu".into());
|
2022-09-02 00:32:57 +00:00
|
|
|
|
|
|
|
// digits
|
|
|
|
let (_, got) = parameter("$01").unwrap();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, "01".into());
|
2022-09-02 00:32:57 +00:00
|
|
|
|
|
|
|
// all valid chars
|
|
|
|
let (_, got) = parameter("$cpu_0").unwrap();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, "cpu_0".into());
|
2022-09-02 00:32:57 +00:00
|
|
|
|
|
|
|
// keyword
|
|
|
|
let (_, got) = parameter("$from").unwrap();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, "from".into());
|
2022-09-02 00:32:57 +00:00
|
|
|
|
|
|
|
// quoted
|
|
|
|
let (_, got) = parameter("$\"quick draw\"").unwrap();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, "quick draw".into());
|
2022-09-02 00:32:57 +00:00
|
|
|
|
|
|
|
// ┌─────────────────────────────┐
|
|
|
|
// │ Fallible tests │
|
|
|
|
// └─────────────────────────────┘
|
|
|
|
|
|
|
|
// missing `$` prefix
|
2022-09-15 00:19:03 +00:00
|
|
|
parameter("cpu").unwrap_err();
|
2022-09-02 00:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bind_parameter_display() {
|
2022-09-15 06:52:31 +00:00
|
|
|
// BindParameter displays quoted output
|
2023-01-18 02:27:38 +00:00
|
|
|
let got = BindParameter("from foo".into()).to_string();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, r#"$"from foo""#);
|
|
|
|
|
|
|
|
// BindParameter displays quoted and escaped output
|
2023-01-18 02:27:38 +00:00
|
|
|
let got = BindParameter("from\nfoo".into()).to_string();
|
2022-09-15 06:52:31 +00:00
|
|
|
assert_eq!(got, r#"$"from\nfoo""#);
|
2022-09-02 00:32:57 +00:00
|
|
|
|
2022-09-15 06:52:31 +00:00
|
|
|
// BindParameter displays unquoted output
|
2023-01-18 02:27:38 +00:00
|
|
|
let got = BindParameter("quick_draw".into()).to_string();
|
2022-09-02 00:32:57 +00:00
|
|
|
assert_eq!(got, "$quick_draw");
|
|
|
|
}
|
|
|
|
}
|