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
Stuart Carnie 2022-09-20 10:51:30 +10:00 committed by GitHub
parent 5090d66eaf
commit 56df74802c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 555 additions and 158 deletions

View File

@ -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"
);
}
}

View File

@ -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"
);
}
}

View File

@ -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]

View File

@ -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;

View File

@ -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"
);
}
}

View File

@ -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)?;

View File

@ -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)]

View File

@ -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)),

View File

@ -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),

View File

@ -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");
}
}

View File

@ -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();
}
}