feat: Add `DELETE` and `DROP MEASUREMENT` statements (#5656)
* chore: Refactor `FROM` clause parser to be generic This will allow us to use it for `DELETE` statements, which has a more restrictive requirement, only allowing regular expressions or single part identifiers. * feat: Add `DELETE` series statement * chore: Add test case for insignificant whitespace between operators NOTE: Added a skipped test until #5663 is implemented * feat: Add `DROP MEASUREMENT` statement * chore: Add DropMeasurementStatement struct * chore: `Statement` enum contains only `Box`ed types Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>pull/24376/head
parent
5090d66eaf
commit
56df74802c
|
|
@ -0,0 +1,96 @@
|
|||
use crate::common::where_clause;
|
||||
use crate::expression::Expr;
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use crate::simple_from_clause::{delete_from_clause, DeleteFromClause};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::{multispace0, multispace1};
|
||||
use nom::combinator::{map, opt};
|
||||
use nom::sequence::{pair, preceded};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DeleteStatement {
|
||||
/// A DELETE with a measurement or measurements and an optional conditional expression
|
||||
/// to restrict which series are deleted.
|
||||
FromWhere {
|
||||
from: DeleteFromClause,
|
||||
condition: Option<Expr>,
|
||||
},
|
||||
|
||||
/// A `DELETE` with a conditional expression to restrict which series are deleted.
|
||||
Where(Expr),
|
||||
}
|
||||
|
||||
impl Display for DeleteStatement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "DELETE")?;
|
||||
|
||||
match self {
|
||||
Self::FromWhere { from, condition } => {
|
||||
write!(f, " FROM {}", from)?;
|
||||
if let Some(condition) = condition {
|
||||
write!(f, " WHERE {}", condition)?;
|
||||
}
|
||||
}
|
||||
Self::Where(condition) => write!(f, " WHERE {}", condition)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a `DELETE` statement.
|
||||
pub fn delete_statement(i: &str) -> ParseResult<&str, DeleteStatement> {
|
||||
// delete ::= "DELETE" ( from_clause where_clause? | where_clause )
|
||||
preceded(
|
||||
tag_no_case("DELETE"),
|
||||
expect(
|
||||
"invalid DELETE statement, must be followed by FROM or WHERE",
|
||||
preceded(
|
||||
multispace1,
|
||||
alt((
|
||||
// delete ::= from_clause where_clause?
|
||||
map(
|
||||
pair(delete_from_clause, opt(preceded(multispace0, where_clause))),
|
||||
|(from, condition)| DeleteStatement::FromWhere { from, condition },
|
||||
),
|
||||
// delete ::= where_clause
|
||||
map(where_clause, DeleteStatement::Where),
|
||||
)),
|
||||
),
|
||||
),
|
||||
)(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::assert_expect_error;
|
||||
use crate::delete::delete_statement;
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
// Validate via the Display trait, as we don't need to validate the contents of the
|
||||
// FROM and / or WHERE clauses, given they are tested in their on modules.
|
||||
|
||||
let (_, got) = delete_statement("DELETE FROM foo").unwrap();
|
||||
assert_eq!(format!("{}", got), "DELETE FROM foo");
|
||||
|
||||
let (_, got) = delete_statement("DELETE FROM foo WHERE time > 10").unwrap();
|
||||
assert_eq!(format!("{}", got), "DELETE FROM foo WHERE time > 10");
|
||||
|
||||
let (_, got) = delete_statement("DELETE WHERE time > 10").unwrap();
|
||||
assert_eq!(format!("{}", got), "DELETE WHERE time > 10");
|
||||
|
||||
// Fallible cases
|
||||
assert_expect_error!(
|
||||
delete_statement("DELETE"),
|
||||
"invalid DELETE statement, must be followed by FROM or WHERE"
|
||||
);
|
||||
|
||||
assert_expect_error!(
|
||||
delete_statement("DELETE FOO"),
|
||||
"invalid DELETE statement, must be followed by FROM or WHERE"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
use crate::identifier::{identifier, Identifier};
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::map;
|
||||
use nom::sequence::{pair, preceded};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Represents a `DROP MEASUREMENT` statement.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DropMeasurementStatement {
|
||||
/// The name of the measurement to delete.
|
||||
name: Identifier,
|
||||
}
|
||||
|
||||
impl Display for DropMeasurementStatement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "DROP MEASUREMENT {}", self.name)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drop_statement(i: &str) -> ParseResult<&str, DropMeasurementStatement> {
|
||||
preceded(
|
||||
pair(tag_no_case("DROP"), multispace1),
|
||||
expect(
|
||||
"invalid DROP statement, must be followed by MEASUREMENT",
|
||||
drop_measurement,
|
||||
),
|
||||
)(i)
|
||||
}
|
||||
|
||||
fn drop_measurement(i: &str) -> ParseResult<&str, DropMeasurementStatement> {
|
||||
preceded(
|
||||
pair(tag_no_case("MEASUREMENT"), multispace1),
|
||||
map(
|
||||
expect(
|
||||
"invalid DROP MEASUREMENT statement, expected identifier",
|
||||
identifier,
|
||||
),
|
||||
|name| DropMeasurementStatement { name },
|
||||
),
|
||||
)(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::assert_expect_error;
|
||||
|
||||
#[test]
|
||||
fn test_drop_statement() {
|
||||
drop_statement("DROP MEASUREMENT foo").unwrap();
|
||||
|
||||
// Fallible cases
|
||||
assert_expect_error!(
|
||||
drop_statement("DROP foo"),
|
||||
"invalid DROP statement, must be followed by MEASUREMENT"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_measurement() {
|
||||
let (_, got) = drop_measurement("MEASUREMENT \"foo\"").unwrap();
|
||||
assert_eq!(got, DropMeasurementStatement { name: "foo".into() });
|
||||
// validate Display
|
||||
assert_eq!(format!("{}", got), "DROP MEASUREMENT foo");
|
||||
|
||||
// Fallible cases
|
||||
assert_expect_error!(
|
||||
drop_measurement("MEASUREMENT 'foo'"),
|
||||
"invalid DROP MEASUREMENT statement, expected identifier"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -468,16 +468,25 @@ mod test {
|
|||
let (_, got) = conditional_expression("5+$foo").unwrap();
|
||||
assert_eq!(got, *binary_op!(5, Add, param!("foo")));
|
||||
|
||||
let (_, got) = conditional_expression("5--(3|2)").unwrap();
|
||||
let (_, got) = conditional_expression("5- -(3|2)").unwrap();
|
||||
assert_eq!(
|
||||
got,
|
||||
*binary_op!(5, Sub, unary!(-nested!(binary_op!(3, BitwiseOr, 2))))
|
||||
);
|
||||
|
||||
// whitespace is not significant between unary operators
|
||||
let (_, got) = conditional_expression("5+-(3|2)").unwrap();
|
||||
assert_eq!(
|
||||
got,
|
||||
*binary_op!(5, Add, unary!(-nested!(binary_op!(3, BitwiseOr, 2))))
|
||||
);
|
||||
|
||||
// Fallible cases
|
||||
|
||||
// invalid operator / incomplete expression
|
||||
assert_failure!(conditional_expression("5 || 3"));
|
||||
// TODO: skip until https://github.com/influxdata/influxdb_iox/issues/5663 is implemented
|
||||
// assert_failure!(conditional_expression("5+--(3|2)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ use std::fmt::{Debug, Display, Formatter};
|
|||
mod test_util;
|
||||
|
||||
mod common;
|
||||
mod delete;
|
||||
mod drop;
|
||||
mod expression;
|
||||
mod identifier;
|
||||
mod internal;
|
||||
|
|
@ -37,6 +39,7 @@ mod show_measurements;
|
|||
mod show_retention_policies;
|
||||
mod show_tag_keys;
|
||||
mod show_tag_values;
|
||||
mod simple_from_clause;
|
||||
mod statement;
|
||||
mod string;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::common::{measurement_name_expression, MeasurementNameExpression};
|
||||
use crate::identifier::{identifier, Identifier};
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use crate::show_field_keys::show_field_keys;
|
||||
|
|
@ -6,35 +5,33 @@ use crate::show_measurements::show_measurements;
|
|||
use crate::show_retention_policies::show_retention_policies;
|
||||
use crate::show_tag_keys::show_tag_keys;
|
||||
use crate::show_tag_values::show_tag_values;
|
||||
use crate::string::{regex, Regex};
|
||||
use crate::Statement;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::{char, multispace0, multispace1};
|
||||
use nom::combinator::{map, opt, value};
|
||||
use nom::multi::separated_list1;
|
||||
use nom::sequence::{pair, preceded, tuple};
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::{map, value};
|
||||
use nom::sequence::{pair, preceded};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Parse a SHOW statement.
|
||||
pub fn show_statement(i: &str) -> ParseResult<&str, Statement> {
|
||||
preceded(
|
||||
pair(tag_no_case("SHOW"), multispace1),
|
||||
expect(
|
||||
"invalid SHOW statement, expected MEASUREMENTS, TAG following SHOW",
|
||||
// NOTE: This will become an alt(()) once more statements are added
|
||||
"invalid SHOW statement, expected DATABASES, FIELD, MEASUREMENTS, TAG, or RETENTION following SHOW",
|
||||
alt((
|
||||
// SHOW DATABASES
|
||||
show_databases,
|
||||
map(show_databases, |s| Statement::ShowDatabases(Box::new(s))),
|
||||
// SHOW FIELD KEYS
|
||||
map(show_field_keys, |v| Statement::ShowFieldKeys(Box::new(v))),
|
||||
map(show_field_keys, |s| Statement::ShowFieldKeys(Box::new(s))),
|
||||
// SHOW MEASUREMENTS
|
||||
map(show_measurements, |v| {
|
||||
Statement::ShowMeasurements(Box::new(v))
|
||||
map(show_measurements, |s| {
|
||||
Statement::ShowMeasurements(Box::new(s))
|
||||
}),
|
||||
// SHOW RETENTION POLICIES
|
||||
show_retention_policies,
|
||||
map(show_retention_policies, |s| {
|
||||
Statement::ShowRetentionPolicies(Box::new(s))
|
||||
}),
|
||||
// SHOW TAG
|
||||
show_tag,
|
||||
)),
|
||||
|
|
@ -42,113 +39,20 @@ pub fn show_statement(i: &str) -> ParseResult<&str, Statement> {
|
|||
)(i)
|
||||
}
|
||||
|
||||
/// Represents a `SHOW DATABASES` statement.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ShowDatabasesStatement;
|
||||
|
||||
impl Display for ShowDatabasesStatement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("SHOW DATABASES")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a `SHOW DATABASES` statement.
|
||||
fn show_databases(i: &str) -> ParseResult<&str, Statement> {
|
||||
value(Statement::ShowDatabases, tag_no_case("DATABASES"))(i)
|
||||
}
|
||||
|
||||
/// Represents a single measurement selection found in a `FROM` measurement clause.
|
||||
///
|
||||
/// A `FROM` measurement clause is used by various `SHOW` statements.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum MeasurementSelection {
|
||||
Name(MeasurementNameExpression),
|
||||
Regex(Regex),
|
||||
}
|
||||
|
||||
impl fmt::Display for MeasurementSelection {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Name(ref name) => fmt::Display::fmt(name, f)?,
|
||||
Self::Regex(ref re) => fmt::Display::fmt(re, f)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a `FROM` clause used by various `SHOW` statements.
|
||||
///
|
||||
/// A `FROM` clause for a `SHOW` statements differs from a `FROM` in a
|
||||
/// `SELECT`, as it can only contain measurement name or regular expressions.
|
||||
///
|
||||
/// It is defined by the following EBNF notation:
|
||||
///
|
||||
/// ```text
|
||||
/// from_clause ::= "FROM" measurement_selection ("," measurement_selection)*
|
||||
/// measurement_selection ::= measurement
|
||||
///
|
||||
/// measurement ::= measurement_name |
|
||||
/// ( policy_name "." measurement_name ) |
|
||||
/// ( db_name "." policy_name? "." measurement_name )
|
||||
///
|
||||
/// db_name ::= identifier
|
||||
/// measurement_name ::= identifier | regex_lit
|
||||
/// policy_name ::= identifier
|
||||
/// ```
|
||||
///
|
||||
/// A minimal `FROM` clause would be a single identifier
|
||||
///
|
||||
/// ```text
|
||||
/// FROM foo
|
||||
/// ```
|
||||
///
|
||||
/// A more complicated example may include a variety of fully-qualified
|
||||
/// identifiers and regular expressions
|
||||
///
|
||||
/// ```text
|
||||
/// FROM foo, /bar/, some_database..foo, some_retention_policy.foobar
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct FromMeasurementClause {
|
||||
first: MeasurementSelection,
|
||||
rest: Option<Vec<MeasurementSelection>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for FromMeasurementClause {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.first, f)?;
|
||||
if let Some(ref rest) = self.rest {
|
||||
for arg in rest {
|
||||
write!(f, ", {}", arg)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn measurement_selection(i: &str) -> ParseResult<&str, MeasurementSelection> {
|
||||
alt((
|
||||
map(measurement_name_expression, MeasurementSelection::Name),
|
||||
map(regex, MeasurementSelection::Regex),
|
||||
))(i)
|
||||
}
|
||||
|
||||
pub fn from_clause(i: &str) -> ParseResult<&str, FromMeasurementClause> {
|
||||
// NOTE: This combinator is optimised to parse
|
||||
map(
|
||||
preceded(
|
||||
pair(tag_no_case("FROM"), multispace1),
|
||||
expect(
|
||||
"invalid FROM clause, expected one or more identifiers or regexes",
|
||||
tuple((
|
||||
measurement_selection,
|
||||
opt(preceded(
|
||||
pair(multispace0, char(',')),
|
||||
expect(
|
||||
"invalid FROM clause, expected identifier after ,",
|
||||
separated_list1(
|
||||
preceded(multispace0, char(',')),
|
||||
preceded(multispace0, measurement_selection),
|
||||
),
|
||||
),
|
||||
)),
|
||||
)),
|
||||
),
|
||||
),
|
||||
|(first, rest)| FromMeasurementClause { first, rest },
|
||||
)(i)
|
||||
fn show_databases(i: &str) -> ParseResult<&str, ShowDatabasesStatement> {
|
||||
value(ShowDatabasesStatement, tag_no_case("DATABASES"))(i)
|
||||
}
|
||||
|
||||
/// Parse an `ON` clause for `SHOW TAG KEYS`, `SHOW TAG VALUES` and `SHOW FIELD KEYS`
|
||||
|
|
@ -167,8 +71,8 @@ fn show_tag(i: &str) -> ParseResult<&str, Statement> {
|
|||
expect(
|
||||
"invalid SHOW TAG statement, expected KEYS or VALUES following TAG",
|
||||
alt((
|
||||
map(show_tag_keys, |v| Statement::ShowTagKeys(Box::new(v))),
|
||||
map(show_tag_values, |v| Statement::ShowTagValues(Box::new(v))),
|
||||
map(show_tag_keys, |s| Statement::ShowTagKeys(Box::new(s))),
|
||||
map(show_tag_values, |s| Statement::ShowTagValues(Box::new(s))),
|
||||
)),
|
||||
),
|
||||
)(i)
|
||||
|
|
@ -211,7 +115,7 @@ mod test {
|
|||
// Unsupported SHOW
|
||||
assert_expect_error!(
|
||||
show_statement("SHOW FOO"),
|
||||
"invalid SHOW statement, expected MEASUREMENTS, TAG following SHOW"
|
||||
"invalid SHOW statement, expected DATABASES, FIELD, MEASUREMENTS, TAG, or RETENTION following SHOW"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::common::{limit_clause, offset_clause};
|
||||
use crate::identifier::Identifier;
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use crate::show::{from_clause, on_clause, FromMeasurementClause};
|
||||
use crate::show::on_clause;
|
||||
use crate::simple_from_clause::{show_from_clause, ShowFromClause};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::opt;
|
||||
|
|
@ -18,7 +19,7 @@ pub struct ShowFieldKeysStatement {
|
|||
|
||||
/// The measurement or measurements to restrict which field keys
|
||||
/// are retrieved.
|
||||
pub from: Option<FromMeasurementClause>,
|
||||
pub from: Option<ShowFromClause>,
|
||||
|
||||
/// A value to restrict the number of field keys returned.
|
||||
pub limit: Option<u64>,
|
||||
|
|
@ -72,7 +73,7 @@ pub fn show_field_keys(i: &str) -> ParseResult<&str, ShowFieldKeysStatement> {
|
|||
tag_no_case("KEYS"),
|
||||
),
|
||||
opt(preceded(multispace1, on_clause)),
|
||||
opt(preceded(multispace1, from_clause)),
|
||||
opt(preceded(multispace1, show_from_clause)),
|
||||
opt(preceded(multispace1, limit_clause)),
|
||||
opt(preceded(multispace1, offset_clause)),
|
||||
))(i)?;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,27 @@
|
|||
use crate::identifier::{identifier, Identifier};
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use crate::Statement;
|
||||
use crate::Statement::ShowRetentionPolicies;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::{pair, preceded, tuple};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Represents a `SHOW RETENTION POLICIES` statement.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ShowRetentionPoliciesStatement {
|
||||
/// Name of the database to list the retention policies, or all if this is `None`.
|
||||
database: Option<Identifier>,
|
||||
}
|
||||
|
||||
impl Display for ShowRetentionPoliciesStatement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "SHOW RETENTION POLICIES")?;
|
||||
if let Some(ref database) = self.database {
|
||||
write!(f, " ON {}", database)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn on_clause(i: &str) -> ParseResult<&str, Identifier> {
|
||||
preceded(
|
||||
|
|
@ -14,7 +30,7 @@ fn on_clause(i: &str) -> ParseResult<&str, Identifier> {
|
|||
)(i)
|
||||
}
|
||||
|
||||
pub fn show_retention_policies(i: &str) -> ParseResult<&str, Statement> {
|
||||
pub fn show_retention_policies(i: &str) -> ParseResult<&str, ShowRetentionPoliciesStatement> {
|
||||
let (remaining, (_, _, _, database)) = tuple((
|
||||
tag_no_case("RETENTION"),
|
||||
multispace1,
|
||||
|
|
@ -25,7 +41,7 @@ pub fn show_retention_policies(i: &str) -> ParseResult<&str, Statement> {
|
|||
opt(preceded(multispace1, on_clause)),
|
||||
))(i)?;
|
||||
|
||||
Ok((remaining, ShowRetentionPolicies { database }))
|
||||
Ok((remaining, ShowRetentionPoliciesStatement { database }))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use crate::common::{limit_clause, offset_clause, where_clause};
|
|||
use crate::expression::Expr;
|
||||
use crate::identifier::Identifier;
|
||||
use crate::internal::ParseResult;
|
||||
use crate::show::{from_clause, on_clause, FromMeasurementClause};
|
||||
use crate::show::on_clause;
|
||||
use crate::simple_from_clause::{show_from_clause, ShowFromClause};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::combinator::opt;
|
||||
|
|
@ -19,7 +20,7 @@ pub struct ShowTagKeysStatement {
|
|||
|
||||
/// The measurement or measurements to restrict which tag keys
|
||||
/// are retrieved.
|
||||
pub from: Option<FromMeasurementClause>,
|
||||
pub from: Option<ShowFromClause>,
|
||||
|
||||
/// A conditional expression to filter the tag keys.
|
||||
pub condition: Option<Expr>,
|
||||
|
|
@ -74,7 +75,7 @@ pub fn show_tag_keys(i: &str) -> ParseResult<&str, ShowTagKeysStatement> {
|
|||
) = tuple((
|
||||
tag_no_case("KEYS"),
|
||||
opt(preceded(multispace1, on_clause)),
|
||||
opt(preceded(multispace1, from_clause)),
|
||||
opt(preceded(multispace1, show_from_clause)),
|
||||
opt(preceded(multispace1, where_clause)),
|
||||
opt(preceded(multispace1, limit_clause)),
|
||||
opt(preceded(multispace1, offset_clause)),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use crate::common::{limit_clause, offset_clause, where_clause};
|
|||
use crate::expression::Expr;
|
||||
use crate::identifier::{identifier, Identifier};
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use crate::show::{from_clause, on_clause, FromMeasurementClause};
|
||||
use crate::show::on_clause;
|
||||
use crate::simple_from_clause::{show_from_clause, ShowFromClause};
|
||||
use crate::string::{regex, Regex};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{tag, tag_no_case};
|
||||
|
|
@ -22,7 +23,7 @@ pub struct ShowTagValuesStatement {
|
|||
|
||||
/// The measurement or measurements to restrict which tag keys
|
||||
/// are retrieved.
|
||||
pub from: Option<FromMeasurementClause>,
|
||||
pub from: Option<ShowFromClause>,
|
||||
|
||||
/// `WITH KEY` expression, to limit the values retrieved to
|
||||
/// the matching tag keys.
|
||||
|
|
@ -84,7 +85,7 @@ pub fn show_tag_values(i: &str) -> ParseResult<&str, ShowTagValuesStatement> {
|
|||
) = tuple((
|
||||
tag_no_case("VALUES"),
|
||||
opt(preceded(multispace1, on_clause)),
|
||||
opt(preceded(multispace1, from_clause)),
|
||||
opt(preceded(multispace1, show_from_clause)),
|
||||
expect(
|
||||
"invalid SHOW TAG VALUES statement, expect WITH KEY clause",
|
||||
preceded(multispace1, with_key_clause),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,267 @@
|
|||
use crate::common::{measurement_name_expression, MeasurementNameExpression};
|
||||
use crate::identifier::{identifier, Identifier};
|
||||
use crate::internal::{expect, ParseResult};
|
||||
use crate::string::{regex, Regex};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::{char, multispace0, multispace1};
|
||||
use nom::combinator::{map, opt};
|
||||
use nom::multi::separated_list1;
|
||||
use nom::sequence::{pair, preceded, tuple};
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
pub trait Parser: Sized {
|
||||
fn parse(i: &str) -> ParseResult<&str, Self>;
|
||||
}
|
||||
|
||||
/// Represents a single measurement selection found in a `FROM` measurement clause.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum MeasurementSelection<T: Parser> {
|
||||
Name(T),
|
||||
Regex(Regex),
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + Parser> fmt::Display for MeasurementSelection<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Name(ref name) => fmt::Display::fmt(name, f)?,
|
||||
Self::Regex(ref re) => fmt::Display::fmt(re, f)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a `FROM` clause of a `DELETE` or `SHOW` statement.
|
||||
///
|
||||
/// A `FROM` clause for a `DELETE` can only accept [`Identifier`] or regular expressions
|
||||
/// for measurements names.
|
||||
///
|
||||
/// A `FROM` clause for a number of `SHOW` statements can accept a 3-part measurement name or
|
||||
/// regular expression.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct FromMeasurementClause<T: Parser> {
|
||||
pub first: MeasurementSelection<T>,
|
||||
pub rest: Option<Vec<MeasurementSelection<T>>>,
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + Parser> fmt::Display for FromMeasurementClause<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.first, f)?;
|
||||
if let Some(ref rest) = self.rest {
|
||||
for arg in rest {
|
||||
write!(f, ", {}", arg)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn measurement_selection<T: Parser>(i: &str) -> ParseResult<&str, MeasurementSelection<T>> {
|
||||
alt((
|
||||
map(T::parse, MeasurementSelection::Name),
|
||||
map(regex, MeasurementSelection::Regex),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn from_clause<T: Parser>(i: &str) -> ParseResult<&str, FromMeasurementClause<T>> {
|
||||
// NOTE: This combinator is optimised to parse
|
||||
map(
|
||||
preceded(
|
||||
pair(tag_no_case("FROM"), multispace1),
|
||||
expect(
|
||||
"invalid FROM clause, expected one or more identifiers or regexes",
|
||||
tuple((
|
||||
measurement_selection,
|
||||
opt(preceded(
|
||||
pair(multispace0, char(',')),
|
||||
expect(
|
||||
"invalid FROM clause, expected identifier after ,",
|
||||
separated_list1(
|
||||
preceded(multispace0, char(',')),
|
||||
preceded(multispace0, measurement_selection),
|
||||
),
|
||||
),
|
||||
)),
|
||||
)),
|
||||
),
|
||||
),
|
||||
|(first, rest)| FromMeasurementClause { first, rest },
|
||||
)(i)
|
||||
}
|
||||
|
||||
impl Parser for MeasurementNameExpression {
|
||||
fn parse(i: &str) -> ParseResult<&str, Self> {
|
||||
measurement_name_expression(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a `FROM` clause used by various `SHOW` statements.
|
||||
///
|
||||
/// A `FROM` clause for a `SHOW` statements differs from a `FROM` in a
|
||||
/// `SELECT`, as it can only contain measurement name or regular expressions.
|
||||
///
|
||||
/// It is defined by the following EBNF notation:
|
||||
///
|
||||
/// ```text
|
||||
/// from_clause ::= "FROM" measurement_selection ("," measurement_selection)*
|
||||
/// measurement_selection ::= measurement
|
||||
///
|
||||
/// measurement ::= measurement_name |
|
||||
/// ( policy_name "." measurement_name ) |
|
||||
/// ( db_name "." policy_name? "." measurement_name )
|
||||
///
|
||||
/// db_name ::= identifier
|
||||
/// measurement_name ::= identifier | regex_lit
|
||||
/// policy_name ::= identifier
|
||||
/// ```
|
||||
///
|
||||
/// A minimal `FROM` clause would be a single identifier
|
||||
///
|
||||
/// ```text
|
||||
/// FROM foo
|
||||
/// ```
|
||||
///
|
||||
/// A more complicated example may include a variety of fully-qualified
|
||||
/// identifiers and regular expressions
|
||||
///
|
||||
/// ```text
|
||||
/// FROM foo, /bar/, some_database..foo, some_retention_policy.foobar
|
||||
/// ```
|
||||
pub type ShowFromClause = FromMeasurementClause<MeasurementNameExpression>;
|
||||
|
||||
/// Parse a `FROM` clause for various `SHOW` statements.
|
||||
pub fn show_from_clause(i: &str) -> ParseResult<&str, ShowFromClause> {
|
||||
from_clause(i)
|
||||
}
|
||||
|
||||
impl Parser for Identifier {
|
||||
fn parse(i: &str) -> ParseResult<&str, Self> {
|
||||
identifier(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a `FROM` clause for a `DELETE` statement.
|
||||
pub type DeleteFromClause = FromMeasurementClause<Identifier>;
|
||||
|
||||
/// Parse a `FROM` clause for a `DELETE` statement.
|
||||
pub fn delete_from_clause(i: &str) -> ParseResult<&str, DeleteFromClause> {
|
||||
from_clause(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_show_from_clause() {
|
||||
use crate::simple_from_clause::MeasurementSelection::*;
|
||||
|
||||
let (_, from) = show_from_clause("FROM c").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
ShowFromClause {
|
||||
first: Name(MeasurementNameExpression {
|
||||
database: None,
|
||||
retention_policy: None,
|
||||
name: "c".into()
|
||||
}),
|
||||
rest: None
|
||||
}
|
||||
);
|
||||
|
||||
let (_, from) = show_from_clause("FROM a..c").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
ShowFromClause {
|
||||
first: Name(MeasurementNameExpression {
|
||||
database: Some("a".into()),
|
||||
retention_policy: None,
|
||||
name: "c".into()
|
||||
}),
|
||||
rest: None
|
||||
}
|
||||
);
|
||||
|
||||
let (_, from) = show_from_clause("FROM a.b.c").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
ShowFromClause {
|
||||
first: Name(MeasurementNameExpression {
|
||||
database: Some("a".into()),
|
||||
retention_policy: Some("b".into()),
|
||||
name: "c".into()
|
||||
}),
|
||||
rest: None
|
||||
}
|
||||
);
|
||||
|
||||
let (_, from) = show_from_clause("FROM /reg/").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
ShowFromClause {
|
||||
first: Regex("reg".into()),
|
||||
rest: None
|
||||
}
|
||||
);
|
||||
|
||||
let (_, from) = show_from_clause("FROM c, /reg/").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
ShowFromClause {
|
||||
first: Name(MeasurementNameExpression {
|
||||
database: None,
|
||||
retention_policy: None,
|
||||
name: "c".into()
|
||||
}),
|
||||
rest: Some(vec![Regex("reg".into())]),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_from_clause() {
|
||||
use crate::simple_from_clause::MeasurementSelection::*;
|
||||
|
||||
let (_, from) = delete_from_clause("FROM c").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
DeleteFromClause {
|
||||
first: Name("c".into()),
|
||||
rest: None
|
||||
}
|
||||
);
|
||||
|
||||
let (_, from) = delete_from_clause("FROM /reg/").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
DeleteFromClause {
|
||||
first: Regex("reg".into()),
|
||||
rest: None
|
||||
}
|
||||
);
|
||||
|
||||
let (_, from) = delete_from_clause("FROM c, /reg/").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
DeleteFromClause {
|
||||
first: Name("c".into()),
|
||||
rest: Some(vec![Regex("reg".into())]),
|
||||
}
|
||||
);
|
||||
|
||||
// Demonstrate that the 3-part name is not parsed
|
||||
let (i, from) = delete_from_clause("FROM a.b.c").unwrap();
|
||||
assert_eq!(
|
||||
from,
|
||||
DeleteFromClause {
|
||||
first: Name("a".into()),
|
||||
rest: None,
|
||||
}
|
||||
);
|
||||
// The remaining input will fail in a later parser
|
||||
assert_eq!(i, ".b.c");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,29 @@
|
|||
use crate::identifier::Identifier;
|
||||
use crate::delete::{delete_statement, DeleteStatement};
|
||||
use crate::drop::{drop_statement, DropMeasurementStatement};
|
||||
use crate::internal::ParseResult;
|
||||
use crate::show::show_statement;
|
||||
use crate::show::{show_statement, ShowDatabasesStatement};
|
||||
use crate::show_field_keys::ShowFieldKeysStatement;
|
||||
use crate::show_measurements::ShowMeasurementsStatement;
|
||||
use crate::show_retention_policies::ShowRetentionPoliciesStatement;
|
||||
use crate::show_tag_keys::ShowTagKeysStatement;
|
||||
use crate::show_tag_values::ShowTagValuesStatement;
|
||||
use nom::branch::alt;
|
||||
use nom::combinator::map;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// An InfluxQL statement.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Statement {
|
||||
/// Represents a `DELETE` statement.
|
||||
Delete(Box<DeleteStatement>),
|
||||
/// Represents a `DROP MEASUREMENT` statement.
|
||||
DropMeasurement(Box<DropMeasurementStatement>),
|
||||
/// Represents a `SHOW DATABASES` statement.
|
||||
ShowDatabases,
|
||||
ShowDatabases(Box<ShowDatabasesStatement>),
|
||||
/// Represents a `SHOW MEASUREMENTS` statement.
|
||||
ShowMeasurements(Box<ShowMeasurementsStatement>),
|
||||
/// Represents a `SHOW RETENTION POLICIES` statement.
|
||||
ShowRetentionPolicies {
|
||||
/// Name of the database to list the retention policies, or all if this is `None`.
|
||||
database: Option<Identifier>,
|
||||
},
|
||||
ShowRetentionPolicies(Box<ShowRetentionPoliciesStatement>),
|
||||
/// Represents a `SHOW TAG KEYS` statement.
|
||||
ShowTagKeys(Box<ShowTagKeysStatement>),
|
||||
/// Represents a `SHOW TAG VALUES` statement.
|
||||
|
|
@ -30,17 +35,14 @@ pub enum Statement {
|
|||
impl Display for Statement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ShowDatabases => f.write_str("SHOW DATABASES")?,
|
||||
Self::ShowMeasurements(s) => write!(f, "{}", s)?,
|
||||
Self::ShowRetentionPolicies { database } => {
|
||||
write!(f, "SHOW RETENTION POLICIES")?;
|
||||
if let Some(database) = database {
|
||||
write!(f, " ON {}", database)?;
|
||||
}
|
||||
}
|
||||
Self::ShowTagKeys(s) => write!(f, "{}", s)?,
|
||||
Self::ShowTagValues(s) => write!(f, "{}", s)?,
|
||||
Self::ShowFieldKeys(s) => write!(f, "{}", s)?,
|
||||
Self::Delete(s) => Display::fmt(s, f)?,
|
||||
Self::DropMeasurement(s) => Display::fmt(s, f)?,
|
||||
Self::ShowDatabases(s) => Display::fmt(s, f)?,
|
||||
Self::ShowMeasurements(s) => Display::fmt(s, f)?,
|
||||
Self::ShowRetentionPolicies(s) => Display::fmt(s, f)?,
|
||||
Self::ShowTagKeys(s) => Display::fmt(s, f)?,
|
||||
Self::ShowTagValues(s) => Display::fmt(s, f)?,
|
||||
Self::ShowFieldKeys(s) => Display::fmt(s, f)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
|
@ -49,6 +51,28 @@ impl Display for Statement {
|
|||
|
||||
/// Parse a single InfluxQL statement.
|
||||
pub fn statement(i: &str) -> ParseResult<&str, Statement> {
|
||||
// NOTE: This will become an alt(()) once more statements are added
|
||||
show_statement(i)
|
||||
alt((
|
||||
map(delete_statement, |s| Statement::Delete(Box::new(s))),
|
||||
map(drop_statement, |s| Statement::DropMeasurement(Box::new(s))),
|
||||
show_statement,
|
||||
))(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::statement;
|
||||
|
||||
#[test]
|
||||
fn test_statement() {
|
||||
// validate one of each statement parser is accepted
|
||||
|
||||
// delete_statement combinator
|
||||
statement("DELETE FROM foo").unwrap();
|
||||
|
||||
// drop_statement combinator
|
||||
statement("DROP MEASUREMENT foo").unwrap();
|
||||
|
||||
// show_statement combinator
|
||||
statement("SHOW TAG KEYS").unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue