feat: extend InfluxQL rewriter for SELECT and EXPLAIN (#24726)
Extended the InfluxQL rewriter to handle SELECT statements with nested sub-queries, as well as EXPLAIN statements. Tests were added to check all the rewrite cases for happy path and failure modes.pull/24737/head
parent
160ac34edd
commit
423308dcd4
|
@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"const-random",
|
||||
|
@ -132,9 +132,9 @@ checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
|||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
||||
checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
|
@ -523,11 +523,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "atomic-write-file"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436"
|
||||
checksum = "a8204db279bf648d64fe845bd8840f78b39c8132ed4d6a4194c3b10d4b4cfb0b"
|
||||
dependencies = [
|
||||
"nix 0.27.1",
|
||||
"nix 0.28.0",
|
||||
"rand",
|
||||
]
|
||||
|
||||
|
@ -739,7 +739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata 0.4.5",
|
||||
"regex-automata 0.4.6",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -846,10 +846,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.88"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
|
||||
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -1065,9 +1066,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
|||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.17"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a"
|
||||
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
]
|
||||
|
@ -2253,9 +2254,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
|
||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
|
@ -2687,9 +2688,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.35.1"
|
||||
version = "1.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c985c1bef99cf13c58fade470483d81a2bfe846ebde60ed28cc2dddec2df9e2"
|
||||
checksum = "0a7c22c4d34ef4788c351e971c52bfdfe7ea2766f8c5466bc175dd46e52ac22e"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
|
@ -2938,10 +2939,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.68"
|
||||
name = "jobserver"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -3117,7 +3127,7 @@ version = "0.20.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553"
|
||||
dependencies = [
|
||||
"regex-automata 0.4.5",
|
||||
"regex-automata 0.4.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3360,9 +3370,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.10"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
|
@ -3999,9 +4009,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
|||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.7"
|
||||
version = "2.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546"
|
||||
checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
|
@ -4010,9 +4020,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.7.7"
|
||||
version = "2.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809"
|
||||
checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
|
@ -4020,9 +4030,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.7.7"
|
||||
version = "2.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e"
|
||||
checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
|
@ -4033,9 +4043,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.7.7"
|
||||
version = "2.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a"
|
||||
checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
|
@ -4552,7 +4562,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.5",
|
||||
"regex-automata 0.4.6",
|
||||
"regex-syntax 0.8.2",
|
||||
]
|
||||
|
||||
|
@ -4567,9 +4577,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -5609,9 +5619,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
|||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.30.5"
|
||||
version = "0.30.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
|
||||
checksum = "6746919caf9f2a85bff759535664c060109f21975c5ac2e8652e60102bd4d196"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
|
@ -6429,9 +6439,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
|
@ -6453,10 +6463,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.91"
|
||||
name = "wasite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -6464,9 +6480,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
@ -6479,9 +6495,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.41"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
|
||||
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -6491,9 +6507,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -6501,9 +6517,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -6514,9 +6530,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
|
@ -6533,9 +6549,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.68"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
|
||||
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -6561,9 +6577,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50"
|
||||
checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
"wasite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
@ -6823,7 +6843,7 @@ dependencies = [
|
|||
"rand",
|
||||
"rand_core",
|
||||
"regex",
|
||||
"regex-automata 0.4.5",
|
||||
"regex-automata 0.4.6",
|
||||
"regex-syntax 0.8.2",
|
||||
"reqwest",
|
||||
"ring",
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use influxdb_influxql_parser::{
|
||||
common::ParseError, identifier::Identifier, parse_statements as parse_internal,
|
||||
select::MeasurementSelection, show_measurements::ExtendedOnClause, statement::Statement,
|
||||
common::ParseError,
|
||||
explain::ExplainStatement,
|
||||
identifier::Identifier,
|
||||
parse_statements as parse_internal,
|
||||
select::{MeasurementSelection, SelectStatement},
|
||||
show_measurements::ExtendedOnClause,
|
||||
statement::Statement,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RewrittenStatement {
|
||||
pub struct Rewritten<S> {
|
||||
database: Option<Identifier>,
|
||||
retention_policy: Option<Identifier>,
|
||||
statement: Statement,
|
||||
statement: S,
|
||||
}
|
||||
|
||||
impl RewrittenStatement {
|
||||
fn new(statement: Statement) -> Self {
|
||||
impl<S> Rewritten<S> {
|
||||
fn new(statement: S) -> Self {
|
||||
Self {
|
||||
database: None,
|
||||
retention_policy: None,
|
||||
|
@ -39,11 +44,11 @@ impl RewrittenStatement {
|
|||
self.retention_policy.as_ref()
|
||||
}
|
||||
|
||||
pub fn statement(&self) -> &Statement {
|
||||
pub fn statement(&self) -> &S {
|
||||
&self.statement
|
||||
}
|
||||
|
||||
pub fn to_statement(self) -> Statement {
|
||||
pub fn to_statement(self) -> S {
|
||||
self.statement
|
||||
}
|
||||
|
||||
|
@ -62,13 +67,13 @@ impl RewrittenStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<RewrittenStatement> for Statement {
|
||||
fn from(r: RewrittenStatement) -> Self {
|
||||
impl From<Rewritten<Statement>> for Statement {
|
||||
fn from(r: Rewritten<Statement>) -> Self {
|
||||
r.to_statement()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Statement> for RewrittenStatement {
|
||||
impl TryFrom<Statement> for Rewritten<Statement> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(statement: Statement) -> Result<Self, Self::Error> {
|
||||
|
@ -106,42 +111,83 @@ impl TryFrom<Statement> for RewrittenStatement {
|
|||
let identifier = s.database.take().map(Into::into);
|
||||
Ok(Self::new(Statement::ShowFieldKeys(s)).with_database(identifier))
|
||||
}
|
||||
Statement::Select(mut s) => {
|
||||
let mut db_rp_set = HashSet::new();
|
||||
let from_clause = s
|
||||
.from
|
||||
.take()
|
||||
.into_iter()
|
||||
.map(|ms| match ms {
|
||||
MeasurementSelection::Name(mut qn) => {
|
||||
let db = qn.database.take();
|
||||
let rp = qn.retention_policy.take();
|
||||
if db_rp_set.insert((db, rp)) && db_rp_set.len() > 1 {
|
||||
return Err(Error::MultiDatabase);
|
||||
}
|
||||
Ok(MeasurementSelection::Name(qn))
|
||||
}
|
||||
// TODO - handle sub-queries?
|
||||
MeasurementSelection::Subquery(_) => Ok(ms),
|
||||
})
|
||||
.collect::<Result<Vec<MeasurementSelection>, Error>>()?;
|
||||
s.from.replace(from_clause);
|
||||
let mut result = Self::new(Statement::Select(s));
|
||||
if let Some((db, rp)) = db_rp_set.into_iter().next() {
|
||||
result = result.with_database(db).with_retention_policy(rp);
|
||||
}
|
||||
Ok(result)
|
||||
Statement::Select(s) => {
|
||||
let ss = Rewritten::<SelectStatement>::try_from(*s)?;
|
||||
let db = ss.database.to_owned();
|
||||
let rp = ss.retention_policy.to_owned();
|
||||
Ok(Self::new(Statement::Select(Box::new(ss.to_statement())))
|
||||
.with_database(db)
|
||||
.with_retention_policy(rp))
|
||||
}
|
||||
Statement::Explain(mut s) => {
|
||||
let options = s.options.take();
|
||||
let s = Self::try_from(*s.statement)?;
|
||||
let db = s.database.to_owned();
|
||||
let rp = s.retention_policy.to_owned();
|
||||
Ok(Self::new(Statement::Explain(Box::new(ExplainStatement {
|
||||
options,
|
||||
statement: Box::new(s.to_statement()),
|
||||
})))
|
||||
.with_database(db)
|
||||
.with_retention_policy(rp))
|
||||
}
|
||||
// For all other statements, we just pass them through. Explicitly
|
||||
// do not use a catch-all match arm here in the event that new variants
|
||||
// are added to the Statement enum, we want the compiler to direct us
|
||||
// here to handle, if relevant.
|
||||
Statement::CreateDatabase(_)
|
||||
| Statement::Delete(_)
|
||||
| Statement::DropMeasurement(_)
|
||||
| Statement::Explain(_)
|
||||
| Statement::ShowDatabases(_) => Ok(Self::new(statement)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, Clone)]
|
||||
impl TryFrom<SelectStatement> for Rewritten<SelectStatement> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(mut select_statement: SelectStatement) -> Result<Self, Self::Error> {
|
||||
let mut db_rp_set = HashSet::new();
|
||||
let from_clause = select_statement
|
||||
.from
|
||||
.take()
|
||||
.into_iter()
|
||||
.map(|ms| {
|
||||
let (db, rp, ms) = match ms {
|
||||
MeasurementSelection::Name(mut qn) => {
|
||||
let db = qn.database.take();
|
||||
let rp = qn.retention_policy.take();
|
||||
(db, rp, MeasurementSelection::Name(qn))
|
||||
}
|
||||
// Recursively call try_from on nested sub-queries, and compare their
|
||||
// resulting db/rp to the same at this level. Sub-queries that have
|
||||
// multiple db/rp in them will throw the MultiDatabase error.
|
||||
MeasurementSelection::Subquery(s) => {
|
||||
let ss = Self::try_from(*s)?;
|
||||
(
|
||||
ss.database.to_owned(),
|
||||
ss.retention_policy.to_owned(),
|
||||
MeasurementSelection::Subquery(Box::new(ss.to_statement())),
|
||||
)
|
||||
}
|
||||
};
|
||||
if db_rp_set.insert((db, rp)) && db_rp_set.len() > 1 {
|
||||
Err(Error::MultiDatabase)
|
||||
} else {
|
||||
Ok(ms)
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<MeasurementSelection>, Error>>()?;
|
||||
select_statement.from.replace(from_clause);
|
||||
let mut result = Self::new(select_statement);
|
||||
if let Some((db, rp)) = db_rp_set.into_iter().next() {
|
||||
result = result.with_database(db).with_retention_policy(rp);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("can only perform queries on a single database")]
|
||||
MultiDatabase,
|
||||
|
@ -149,10 +195,361 @@ pub enum Error {
|
|||
Parse(ParseError),
|
||||
}
|
||||
|
||||
pub fn parse_statements(input: &str) -> Result<Vec<RewrittenStatement>, Error> {
|
||||
pub fn parse_statements(input: &str) -> Result<Vec<Rewritten<Statement>>, Error> {
|
||||
parse_internal(input)
|
||||
.map_err(Error::Parse)?
|
||||
.into_iter()
|
||||
.map(RewrittenStatement::try_from)
|
||||
.collect::<Result<Vec<RewrittenStatement>, Error>>()
|
||||
.map(Rewritten::<Statement>::try_from)
|
||||
.collect::<Result<Vec<Rewritten<Statement>>, Error>>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use influxdb_influxql_parser::statement::Statement;
|
||||
|
||||
use crate::{parse_statements, Error, Rewritten};
|
||||
|
||||
fn parse_single(input: &str) -> Rewritten<Statement> {
|
||||
parse_statements(input).unwrap().pop().unwrap()
|
||||
}
|
||||
|
||||
fn parse_single_failure(input: &str) -> Error {
|
||||
parse_statements(input).unwrap_err()
|
||||
}
|
||||
|
||||
struct TestCase {
|
||||
input: &'static str,
|
||||
expected: &'static str,
|
||||
db: Option<&'static str>,
|
||||
rp: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl TestCase {
|
||||
fn assert(&self) {
|
||||
let s = parse_single(self.input);
|
||||
assert_eq!(s.database().map(|db| db.as_str()), self.db);
|
||||
assert_eq!(s.retention_policy().map(|rp| rp.as_str()), self.rp);
|
||||
assert_eq!(self.expected, s.to_statement().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
struct TestFailure {
|
||||
input: &'static str,
|
||||
expected: Error,
|
||||
}
|
||||
|
||||
impl TestFailure {
|
||||
fn assert(&self) {
|
||||
let e = parse_single_failure(self.input);
|
||||
assert_eq!(self.expected, e, "input: {}", self.input);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_measurements() {
|
||||
TestCase {
|
||||
input: "SHOW MEASUREMENTS",
|
||||
expected: "SHOW MEASUREMENTS",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW MEASUREMENTS ON foo",
|
||||
expected: "SHOW MEASUREMENTS",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW MEASUREMENTS ON foo.bar",
|
||||
expected: "SHOW MEASUREMENTS",
|
||||
db: Some("foo"),
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_measurements_failure_modes() {
|
||||
TestFailure {
|
||||
input: "SHOW MEASUREMENTS ON *.*",
|
||||
expected: Error::MultiDatabase,
|
||||
}
|
||||
.assert();
|
||||
TestFailure {
|
||||
input: r#"SHOW MEASUREMENTS ON *"#,
|
||||
expected: Error::MultiDatabase,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_retention_policies() {
|
||||
TestCase {
|
||||
input: "SHOW RETENTION POLICIES",
|
||||
expected: "SHOW RETENTION POLICIES",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW RETENTION POLICIES ON foo",
|
||||
expected: "SHOW RETENTION POLICIES",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_tag_keys() {
|
||||
TestCase {
|
||||
input: "SHOW TAG KEYS",
|
||||
expected: "SHOW TAG KEYS",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW TAG KEYS FROM cpu",
|
||||
expected: "SHOW TAG KEYS FROM cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW TAG KEYS ON foo",
|
||||
expected: "SHOW TAG KEYS",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW TAG KEYS ON foo FROM cpu",
|
||||
expected: "SHOW TAG KEYS FROM cpu",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_tag_values() {
|
||||
TestCase {
|
||||
input: "SHOW TAG VALUES WITH KEY = host",
|
||||
expected: "SHOW TAG VALUES WITH KEY = host",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW TAG VALUES FROM cpu WITH KEY = host",
|
||||
expected: "SHOW TAG VALUES FROM cpu WITH KEY = host",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW TAG VALUES ON foo WITH KEY = host",
|
||||
expected: "SHOW TAG VALUES WITH KEY = host",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW TAG VALUES ON foo FROM cpu WITH KEY = host",
|
||||
expected: "SHOW TAG VALUES FROM cpu WITH KEY = host",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_field_keys() {
|
||||
TestCase {
|
||||
input: "SHOW FIELD KEYS",
|
||||
expected: "SHOW FIELD KEYS",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW FIELD KEYS FROM cpu",
|
||||
expected: "SHOW FIELD KEYS FROM cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW FIELD KEYS ON foo",
|
||||
expected: "SHOW FIELD KEYS",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW FIELD KEYS ON foo FROM cpu",
|
||||
expected: "SHOW FIELD KEYS FROM cpu",
|
||||
db: Some("foo"),
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select() {
|
||||
TestCase {
|
||||
input: "SELECT * FROM cpu",
|
||||
expected: "SELECT * FROM cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SELECT * FROM bar.cpu",
|
||||
expected: "SELECT * FROM cpu",
|
||||
db: None,
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SELECT * FROM foo.bar.cpu",
|
||||
expected: "SELECT * FROM cpu",
|
||||
db: Some("foo"),
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: r#"SELECT * FROM (SELECT * FROM cpu)"#,
|
||||
expected: r#"SELECT * FROM (SELECT * FROM cpu)"#,
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: r#"SELECT * FROM (SELECT * FROM bar.cpu), bar.mem"#,
|
||||
expected: r#"SELECT * FROM (SELECT * FROM cpu), mem"#,
|
||||
db: None,
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: r#"SELECT * FROM (SELECT * FROM foo.bar.cpu), foo.bar.mem"#,
|
||||
expected: r#"SELECT * FROM (SELECT * FROM cpu), mem"#,
|
||||
db: Some("foo"),
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_failure_modes() {
|
||||
TestFailure {
|
||||
input: r#"SELECT * FROM foo.bar.cpu, baz.bop.cpu"#,
|
||||
expected: Error::MultiDatabase,
|
||||
}
|
||||
.assert();
|
||||
TestFailure {
|
||||
input: r#"SELECT * FROM cpu, baz.bop.cpu"#,
|
||||
expected: Error::MultiDatabase,
|
||||
}
|
||||
.assert();
|
||||
TestFailure {
|
||||
input: r#"SELECT * FROM bar.cpu, baz.bop.cpu"#,
|
||||
expected: Error::MultiDatabase,
|
||||
}
|
||||
.assert();
|
||||
TestFailure {
|
||||
input: r#"SELECT * FROM foo.bar.cpu, (SELECT * FROM mem)"#,
|
||||
expected: Error::MultiDatabase,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explain() {
|
||||
TestCase {
|
||||
input: "EXPLAIN SELECT * FROM cpu",
|
||||
expected: "EXPLAIN SELECT * FROM cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "EXPLAIN SELECT * FROM bar.cpu",
|
||||
expected: "EXPLAIN SELECT * FROM cpu",
|
||||
db: None,
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "EXPLAIN SELECT * FROM foo.bar.cpu",
|
||||
expected: "EXPLAIN SELECT * FROM cpu",
|
||||
db: Some("foo"),
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: r#"EXPLAIN SELECT * FROM (SELECT * FROM cpu)"#,
|
||||
expected: r#"EXPLAIN SELECT * FROM (SELECT * FROM cpu)"#,
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: r#"EXPLAIN SELECT * FROM (SELECT * FROM bar.cpu), bar.mem"#,
|
||||
expected: r#"EXPLAIN SELECT * FROM (SELECT * FROM cpu), mem"#,
|
||||
db: None,
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: r#"EXPLAIN SELECT * FROM (SELECT * FROM foo.bar.cpu), foo.bar.mem"#,
|
||||
expected: r#"EXPLAIN SELECT * FROM (SELECT * FROM cpu), mem"#,
|
||||
db: Some("foo"),
|
||||
rp: Some("bar"),
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noop_rewrites() {
|
||||
TestCase {
|
||||
input: "CREATE DATABASE foo",
|
||||
expected: "CREATE DATABASE foo",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "DELETE FROM cpu",
|
||||
expected: "DELETE FROM cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "DROP MEASUREMENT cpu",
|
||||
expected: "DROP MEASUREMENT cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "EXPLAIN SELECT * FROM cpu",
|
||||
expected: "EXPLAIN SELECT * FROM cpu",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
TestCase {
|
||||
input: "SHOW DATABASES",
|
||||
expected: "SHOW DATABASES",
|
||||
db: None,
|
||||
rp: None,
|
||||
}
|
||||
.assert();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue