mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2025-12-25 23:49:58 +00:00
feat: add create alter table expr translator (#3203)
* feat: add create table expr translator * feat: add alter table expr translator * refactor: expose mod * refactor: expr generator * chore: ignore typos check for lorem_words * feat: add string map helper functions * chore: remove unit tests
This commit is contained in:
25
Cargo.lock
generated
25
Cargo.lock
generated
@@ -2875,12 +2875,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "deunicode"
|
|
||||||
version = "1.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff"
|
name = "diff"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -3154,17 +3148,6 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "faker_rand"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "300d2ddbf2245b5b5e723995e0961033121b4fc2be9045fb661af82bd739ffb6"
|
|
||||||
dependencies = [
|
|
||||||
"deunicode",
|
|
||||||
"lazy_static",
|
|
||||||
"rand",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -9551,15 +9534,21 @@ dependencies = [
|
|||||||
"common-error",
|
"common-error",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
"common-query",
|
"common-query",
|
||||||
|
"common-telemetry",
|
||||||
"datatypes",
|
"datatypes",
|
||||||
"derive_builder 0.12.0",
|
"derive_builder 0.12.0",
|
||||||
"faker_rand",
|
"dotenv",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"partition",
|
"partition",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"snafu",
|
"snafu",
|
||||||
"sql",
|
"sql",
|
||||||
|
"sqlparser 0.38.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=6a93567ae38d42be5c8d08b13c8ff4dde26502ef)",
|
||||||
|
"sqlx",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -9,12 +9,25 @@ async-trait = { workspace = true }
|
|||||||
common-error = { workspace = true }
|
common-error = { workspace = true }
|
||||||
common-macro = { workspace = true }
|
common-macro = { workspace = true }
|
||||||
common-query = { workspace = true }
|
common-query = { workspace = true }
|
||||||
|
common-telemetry = { workspace = true }
|
||||||
datatypes = { workspace = true }
|
datatypes = { workspace = true }
|
||||||
derive_builder = { workspace = true }
|
derive_builder = { workspace = true }
|
||||||
faker_rand = "0.1"
|
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
partition = { workspace = true }
|
partition = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
rand_chacha = "0.3.1"
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
snafu = { workspace = true }
|
snafu = { workspace = true }
|
||||||
sql = { workspace = true }
|
sql = { workspace = true }
|
||||||
|
sqlparser.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
dotenv = "0.15"
|
||||||
|
sqlx = { version = "0.6", features = [
|
||||||
|
"runtime-tokio-rustls",
|
||||||
|
"mysql",
|
||||||
|
"postgres",
|
||||||
|
"chrono",
|
||||||
|
] }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub struct TableContext {
|
|||||||
pub columns: Vec<Column>,
|
pub columns: Vec<Column>,
|
||||||
|
|
||||||
// GreptimeDB specific options
|
// GreptimeDB specific options
|
||||||
pub partitions: Vec<PartitionDef>,
|
pub partition: Option<PartitionDef>,
|
||||||
pub primary_keys: Vec<usize>,
|
pub primary_keys: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ impl From<&CreateTableExpr> for TableContext {
|
|||||||
CreateTableExpr {
|
CreateTableExpr {
|
||||||
name,
|
name,
|
||||||
columns,
|
columns,
|
||||||
partitions,
|
partition,
|
||||||
primary_keys,
|
primary_keys,
|
||||||
..
|
..
|
||||||
}: &CreateTableExpr,
|
}: &CreateTableExpr,
|
||||||
@@ -43,7 +43,7 @@ impl From<&CreateTableExpr> for TableContext {
|
|||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
columns: columns.clone(),
|
columns: columns.clone(),
|
||||||
partitions: partitions.clone(),
|
partition: partition.clone(),
|
||||||
primary_keys: primary_keys.clone(),
|
primary_keys: primary_keys.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
249
tests-fuzz/src/data/lorem_words
Normal file
249
tests-fuzz/src/data/lorem_words
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
alias
|
||||||
|
consequatur
|
||||||
|
aut
|
||||||
|
perferendis
|
||||||
|
sit
|
||||||
|
voluptatem
|
||||||
|
accusantium
|
||||||
|
doloremque
|
||||||
|
aperiam
|
||||||
|
eaque
|
||||||
|
ipsa
|
||||||
|
quae
|
||||||
|
ab
|
||||||
|
illo
|
||||||
|
inventore
|
||||||
|
veritatis
|
||||||
|
et
|
||||||
|
quasi
|
||||||
|
architecto
|
||||||
|
beatae
|
||||||
|
vitae
|
||||||
|
dicta
|
||||||
|
sunt
|
||||||
|
explicabo
|
||||||
|
aspernatur
|
||||||
|
aut
|
||||||
|
odit
|
||||||
|
aut
|
||||||
|
fugit
|
||||||
|
sed
|
||||||
|
quia
|
||||||
|
consequuntur
|
||||||
|
magni
|
||||||
|
dolores
|
||||||
|
eos
|
||||||
|
qui
|
||||||
|
ratione
|
||||||
|
voluptatem
|
||||||
|
sequi
|
||||||
|
nesciunt
|
||||||
|
neque
|
||||||
|
dolorem
|
||||||
|
ipsum
|
||||||
|
quia
|
||||||
|
dolor
|
||||||
|
sit
|
||||||
|
amet
|
||||||
|
consectetur
|
||||||
|
adipisci
|
||||||
|
velit
|
||||||
|
sed
|
||||||
|
quia
|
||||||
|
non
|
||||||
|
numquam
|
||||||
|
eius
|
||||||
|
modi
|
||||||
|
tempora
|
||||||
|
incidunt
|
||||||
|
ut
|
||||||
|
labore
|
||||||
|
et
|
||||||
|
dolore
|
||||||
|
magnam
|
||||||
|
aliquam
|
||||||
|
quaerat
|
||||||
|
voluptatem
|
||||||
|
ut
|
||||||
|
enim
|
||||||
|
ad
|
||||||
|
minima
|
||||||
|
veniam
|
||||||
|
quis
|
||||||
|
nostrum
|
||||||
|
exercitationem
|
||||||
|
ullam
|
||||||
|
corporis
|
||||||
|
nemo
|
||||||
|
enim
|
||||||
|
ipsam
|
||||||
|
voluptatem
|
||||||
|
quia
|
||||||
|
voluptas
|
||||||
|
sit
|
||||||
|
suscipit
|
||||||
|
laboriosam
|
||||||
|
nisi
|
||||||
|
ut
|
||||||
|
aliquid
|
||||||
|
ex
|
||||||
|
ea
|
||||||
|
commodi
|
||||||
|
consequatur
|
||||||
|
quis
|
||||||
|
autem
|
||||||
|
vel
|
||||||
|
eum
|
||||||
|
iure
|
||||||
|
reprehenderit
|
||||||
|
qui
|
||||||
|
in
|
||||||
|
ea
|
||||||
|
voluptate
|
||||||
|
velit
|
||||||
|
esse
|
||||||
|
quam
|
||||||
|
nihil
|
||||||
|
molestiae
|
||||||
|
et
|
||||||
|
iusto
|
||||||
|
odio
|
||||||
|
dignissimos
|
||||||
|
ducimus
|
||||||
|
qui
|
||||||
|
blanditiis
|
||||||
|
praesentium
|
||||||
|
laudantium
|
||||||
|
totam
|
||||||
|
rem
|
||||||
|
voluptatum
|
||||||
|
deleniti
|
||||||
|
atque
|
||||||
|
corrupti
|
||||||
|
quos
|
||||||
|
dolores
|
||||||
|
et
|
||||||
|
quas
|
||||||
|
molestias
|
||||||
|
excepturi
|
||||||
|
sint
|
||||||
|
occaecati
|
||||||
|
cupiditate
|
||||||
|
non
|
||||||
|
provident
|
||||||
|
sed
|
||||||
|
ut
|
||||||
|
perspiciatis
|
||||||
|
unde
|
||||||
|
omnis
|
||||||
|
iste
|
||||||
|
natus
|
||||||
|
error
|
||||||
|
similique
|
||||||
|
sunt
|
||||||
|
in
|
||||||
|
culpa
|
||||||
|
qui
|
||||||
|
officia
|
||||||
|
deserunt
|
||||||
|
mollitia
|
||||||
|
animi
|
||||||
|
id
|
||||||
|
est
|
||||||
|
laborum
|
||||||
|
et
|
||||||
|
dolorum
|
||||||
|
fuga
|
||||||
|
et
|
||||||
|
harum
|
||||||
|
quidem
|
||||||
|
rerum
|
||||||
|
facilis
|
||||||
|
est
|
||||||
|
et
|
||||||
|
expedita
|
||||||
|
distinctio
|
||||||
|
nam
|
||||||
|
libero
|
||||||
|
tempore
|
||||||
|
cum
|
||||||
|
soluta
|
||||||
|
nobis
|
||||||
|
est
|
||||||
|
eligendi
|
||||||
|
optio
|
||||||
|
cumque
|
||||||
|
nihil
|
||||||
|
impedit
|
||||||
|
quo
|
||||||
|
porro
|
||||||
|
quisquam
|
||||||
|
est
|
||||||
|
qui
|
||||||
|
minus
|
||||||
|
id
|
||||||
|
quod
|
||||||
|
maxime
|
||||||
|
placeat
|
||||||
|
facere
|
||||||
|
possimus
|
||||||
|
omnis
|
||||||
|
voluptas
|
||||||
|
assumenda
|
||||||
|
est
|
||||||
|
omnis
|
||||||
|
dolor
|
||||||
|
repellendus
|
||||||
|
temporibus
|
||||||
|
autem
|
||||||
|
quibusdam
|
||||||
|
et
|
||||||
|
aut
|
||||||
|
consequatur
|
||||||
|
vel
|
||||||
|
illum
|
||||||
|
qui
|
||||||
|
dolorem
|
||||||
|
eum
|
||||||
|
fugiat
|
||||||
|
quo
|
||||||
|
voluptas
|
||||||
|
nulla
|
||||||
|
pariatur
|
||||||
|
at
|
||||||
|
vero
|
||||||
|
eos
|
||||||
|
et
|
||||||
|
accusamus
|
||||||
|
officiis
|
||||||
|
debitis
|
||||||
|
aut
|
||||||
|
rerum
|
||||||
|
necessitatibus
|
||||||
|
saepe
|
||||||
|
eveniet
|
||||||
|
ut
|
||||||
|
et
|
||||||
|
voluptates
|
||||||
|
repudiandae
|
||||||
|
sint
|
||||||
|
et
|
||||||
|
molestiae
|
||||||
|
non
|
||||||
|
recusandae
|
||||||
|
itaque
|
||||||
|
earum
|
||||||
|
rerum
|
||||||
|
hic
|
||||||
|
tenetur
|
||||||
|
a
|
||||||
|
sapiente
|
||||||
|
delectus
|
||||||
|
ut
|
||||||
|
aut
|
||||||
|
reiciendis
|
||||||
|
voluptatibus
|
||||||
|
maiores
|
||||||
|
doloribus
|
||||||
|
asperiores
|
||||||
|
repellat
|
||||||
174
tests-fuzz/src/fake.rs
Normal file
174
tests-fuzz/src/fake.rs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use rand::seq::{IteratorRandom, SliceRandom};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::generator::Random;
|
||||||
|
use crate::impl_random;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref LOREM_WORDS: Vec<String> = include_str!("data/lorem_words")
|
||||||
|
.lines()
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modified from https://github.com/ucarion/faker_rand/blob/ea70c660e1ecd7320156eddb31d2830a511f8842/src/lib.rs
|
||||||
|
macro_rules! faker_impl_from_values {
|
||||||
|
($name: ident, $values: expr) => {
|
||||||
|
impl rand::distributions::Distribution<$name> for rand::distributions::Standard {
|
||||||
|
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $name {
|
||||||
|
$name($values[rng.gen_range(0..$values.len())].clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Word(String);
|
||||||
|
faker_impl_from_values!(Word, LOREM_WORDS);
|
||||||
|
pub struct WordGenerator;
|
||||||
|
impl_random!(String, WordGenerator, LOREM_WORDS);
|
||||||
|
|
||||||
|
pub type WordMapFn<R> = Box<dyn Fn(&mut R, String) -> String>;
|
||||||
|
|
||||||
|
pub struct MapWordGenerator<R: Rng> {
|
||||||
|
base: WordGenerator,
|
||||||
|
map: WordMapFn<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_capitalize_map<R: Rng + 'static>(rng: &mut R, s: String) -> String {
|
||||||
|
let mut v = s.chars().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let select = rng.gen_range(0..s.len());
|
||||||
|
for idx in (0..s.len()).choose_multiple(rng, select) {
|
||||||
|
v[idx] = v[idx].to_uppercase().next().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
v.into_iter().collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref KEYWORDS_SET: HashSet<&'static str> = sqlparser::keywords::ALL_KEYWORDS
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if it's a keyword.
|
||||||
|
pub fn is_keyword(word: impl AsRef<str>) -> bool {
|
||||||
|
KEYWORDS_SET.contains(word.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if it contains uppercase char.
|
||||||
|
pub fn contain_uppercase_char(s: &str) -> bool {
|
||||||
|
s.chars().any(|c| c.is_uppercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if it's a keyword or contains uppercase char.
|
||||||
|
pub fn is_keyword_or_contain_uppercase(s: &str) -> bool {
|
||||||
|
is_keyword(s.to_uppercase()) || contain_uppercase_char(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_backtick_map<R: Rng + 'static, F: Fn(&str) -> bool>(
|
||||||
|
f: F,
|
||||||
|
) -> impl Fn(&mut R, String) -> String {
|
||||||
|
move |_rng, s| -> String {
|
||||||
|
let need = f(&s);
|
||||||
|
|
||||||
|
if need {
|
||||||
|
format!("`{s}`")
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_quote_map<R: Rng + 'static, F: Fn(&str) -> bool>(
|
||||||
|
f: F,
|
||||||
|
) -> impl Fn(&mut R, String) -> String {
|
||||||
|
move |_rng, s| -> String {
|
||||||
|
let need = f(&s);
|
||||||
|
|
||||||
|
if need {
|
||||||
|
format!("\"{s}\"")
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds backticks if it contains uppercase chars.
|
||||||
|
pub fn auto_backtick_map<R: Rng + 'static>(_rng: &mut R, s: String) -> String {
|
||||||
|
let need = s.chars().any(|c| c.is_uppercase());
|
||||||
|
|
||||||
|
if need {
|
||||||
|
format!("`{s}`")
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds backticks if it contains uppercase chars.
|
||||||
|
pub fn uppercase_and_keyword_backtick_map<R: Rng + 'static>(rng: &mut R, s: String) -> String {
|
||||||
|
make_backtick_map(is_keyword_or_contain_uppercase)(rng, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds quotes if it contains uppercase chars.
|
||||||
|
pub fn auto_quote_map<R: Rng + 'static>(rng: &mut R, s: String) -> String {
|
||||||
|
make_quote_map(contain_uppercase_char)(rng, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds quotes if it contains uppercase chars.
|
||||||
|
pub fn uppercase_and_keyword_quote_map<R: Rng + 'static>(rng: &mut R, s: String) -> String {
|
||||||
|
make_quote_map(is_keyword_or_contain_uppercase)(rng, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_two_word_map_fn<R: Rng>(
|
||||||
|
f1: impl Fn(&mut R, String) -> String,
|
||||||
|
f2: impl Fn(&mut R, String) -> String,
|
||||||
|
) -> impl Fn(&mut R, String) -> String {
|
||||||
|
move |rng, s| -> String {
|
||||||
|
let s = f1(rng, s);
|
||||||
|
f2(rng, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> MapWordGenerator<R> {
|
||||||
|
pub fn new(map: WordMapFn<R>) -> Self {
|
||||||
|
Self {
|
||||||
|
base: WordGenerator,
|
||||||
|
map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> Random<String, R> for MapWordGenerator<R> {
|
||||||
|
fn choose(&self, rng: &mut R, amount: usize) -> Vec<String> {
|
||||||
|
self.base
|
||||||
|
.choose(rng, amount)
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| (self.map)(rng, s))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,16 +17,46 @@ pub mod create_expr;
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use datatypes::data_type::ConcreteDataType;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::ir::create_expr::ColumnOption;
|
||||||
use crate::ir::{AlterTableExpr, CreateTableExpr};
|
use crate::ir::{AlterTableExpr, CreateTableExpr};
|
||||||
|
|
||||||
pub type CreateTableExprGenerator =
|
pub type CreateTableExprGenerator<R> =
|
||||||
Box<dyn Generator<CreateTableExpr, Error = Error> + Sync + Send>;
|
Box<dyn Generator<CreateTableExpr, R, Error = Error> + Sync + Send>;
|
||||||
|
|
||||||
pub type AlterTableExprGenerator = Box<dyn Generator<AlterTableExpr, Error = Error> + Sync + Send>;
|
pub type AlterTableExprGenerator<R> =
|
||||||
|
Box<dyn Generator<AlterTableExpr, R, Error = Error> + Sync + Send>;
|
||||||
|
|
||||||
pub(crate) trait Generator<T> {
|
pub type ColumnOptionGenerator<R> = Box<dyn Fn(&mut R, &ConcreteDataType) -> Vec<ColumnOption>>;
|
||||||
|
|
||||||
|
pub type ConcreteDataTypeGenerator<R> = Box<dyn Random<ConcreteDataType, R>>;
|
||||||
|
|
||||||
|
pub trait Generator<T, R: Rng> {
|
||||||
type Error: Sync + Send + fmt::Debug;
|
type Error: Sync + Send + fmt::Debug;
|
||||||
|
|
||||||
fn generate(&self) -> Result<T, Self::Error>;
|
fn generate(&self, rng: &mut R) -> Result<T, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Random<T, R: Rng> {
|
||||||
|
/// Generates a random element.
|
||||||
|
fn gen(&self, rng: &mut R) -> T {
|
||||||
|
self.choose(rng, 1).remove(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uniformly sample `amount` distinct elements.
|
||||||
|
fn choose(&self, rng: &mut R, amount: usize) -> Vec<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_random {
|
||||||
|
($type: ident, $value:ident, $values: ident) => {
|
||||||
|
impl<R: Rng> Random<$type, R> for $value {
|
||||||
|
fn choose(&self, rng: &mut R, amount: usize) -> Vec<$type> {
|
||||||
|
$values.choose_multiple(rng, amount).cloned().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,47 +12,41 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::f64::consts::E;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use common_query::AddColumnLocation;
|
use common_query::AddColumnLocation;
|
||||||
use faker_rand::lorem::Word;
|
use derive_builder::Builder;
|
||||||
use rand::{random, Rng};
|
use rand::Rng;
|
||||||
use snafu::ensure;
|
use snafu::ensure;
|
||||||
|
|
||||||
use crate::context::TableContextRef;
|
use crate::context::TableContextRef;
|
||||||
use crate::error::{self, Error, Result};
|
use crate::error::{self, Error, Result};
|
||||||
use crate::generator::Generator;
|
use crate::fake::WordGenerator;
|
||||||
|
use crate::generator::{ColumnOptionGenerator, ConcreteDataTypeGenerator, Generator, Random};
|
||||||
use crate::ir::alter_expr::{AlterTableExpr, AlterTableOperation};
|
use crate::ir::alter_expr::{AlterTableExpr, AlterTableOperation};
|
||||||
use crate::ir::{droppable_columns, Column};
|
use crate::ir::{
|
||||||
|
column_options_generator, droppable_columns, generate_columns, ColumnTypeGenerator,
|
||||||
|
};
|
||||||
|
|
||||||
/// Generates the [AlterTableOperation::AddColumn] of [AlterTableExpr].
|
/// Generates the [AlterTableOperation::AddColumn] of [AlterTableExpr].
|
||||||
pub struct AlterExprAddColumnGenerator {
|
#[derive(Builder)]
|
||||||
|
#[builder(pattern = "owned")]
|
||||||
|
pub struct AlterExprAddColumnGenerator<R: Rng + 'static> {
|
||||||
table_ctx: TableContextRef,
|
table_ctx: TableContextRef,
|
||||||
|
#[builder(default)]
|
||||||
location: bool,
|
location: bool,
|
||||||
|
#[builder(default = "Box::new(WordGenerator)")]
|
||||||
|
name_generator: Box<dyn Random<String, R>>,
|
||||||
|
#[builder(default = "Box::new(column_options_generator)")]
|
||||||
|
column_options_generator: ColumnOptionGenerator<R>,
|
||||||
|
#[builder(default = "Box::new(ColumnTypeGenerator)")]
|
||||||
|
column_type_generator: ConcreteDataTypeGenerator<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlterExprAddColumnGenerator {
|
impl<R: Rng + 'static> Generator<AlterTableExpr, R> for AlterExprAddColumnGenerator<R> {
|
||||||
/// Returns an [AlterExprAddColumnGenerator].
|
|
||||||
pub fn new(table_ctx: &TableContextRef) -> Self {
|
|
||||||
Self {
|
|
||||||
table_ctx: table_ctx.clone(),
|
|
||||||
location: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets true to generate alter expr with a specific location.
|
|
||||||
pub fn with_location(mut self, v: bool) -> Self {
|
|
||||||
self.location = v;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Generator<AlterTableExpr> for AlterExprAddColumnGenerator {
|
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn generate(&self) -> Result<AlterTableExpr> {
|
fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let with_location = self.location && rng.gen::<bool>();
|
let with_location = self.location && rng.gen::<bool>();
|
||||||
let location = if with_location {
|
let location = if with_location {
|
||||||
let use_first = rng.gen::<bool>();
|
let use_first = rng.gen::<bool>();
|
||||||
@@ -71,7 +65,14 @@ impl Generator<AlterTableExpr> for AlterExprAddColumnGenerator {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let column = rng.gen::<Column>();
|
let name = self.name_generator.gen(rng);
|
||||||
|
let column = generate_columns(
|
||||||
|
rng,
|
||||||
|
vec![name],
|
||||||
|
self.column_type_generator.as_ref(),
|
||||||
|
self.column_options_generator.as_ref(),
|
||||||
|
)
|
||||||
|
.remove(0);
|
||||||
Ok(AlterTableExpr {
|
Ok(AlterTableExpr {
|
||||||
name: self.table_ctx.name.to_string(),
|
name: self.table_ctx.name.to_string(),
|
||||||
alter_options: AlterTableOperation::AddColumn { column, location },
|
alter_options: AlterTableOperation::AddColumn { column, location },
|
||||||
@@ -80,24 +81,18 @@ impl Generator<AlterTableExpr> for AlterExprAddColumnGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the [AlterTableOperation::DropColumn] of [AlterTableExpr].
|
/// Generates the [AlterTableOperation::DropColumn] of [AlterTableExpr].
|
||||||
pub struct AlterExprDropColumnGenerator {
|
#[derive(Builder)]
|
||||||
|
#[builder(pattern = "owned")]
|
||||||
|
pub struct AlterExprDropColumnGenerator<R> {
|
||||||
table_ctx: TableContextRef,
|
table_ctx: TableContextRef,
|
||||||
|
#[builder(default)]
|
||||||
|
_phantom: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlterExprDropColumnGenerator {
|
impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprDropColumnGenerator<R> {
|
||||||
/// Returns an [AlterExprDropColumnGenerator].
|
|
||||||
pub fn new(table_ctx: &TableContextRef) -> Self {
|
|
||||||
Self {
|
|
||||||
table_ctx: table_ctx.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Generator<AlterTableExpr> for AlterExprDropColumnGenerator {
|
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn generate(&self) -> Result<AlterTableExpr> {
|
fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let droppable = droppable_columns(&self.table_ctx.columns);
|
let droppable = droppable_columns(&self.table_ctx.columns);
|
||||||
ensure!(!droppable.is_empty(), error::DroppableColumnsSnafu);
|
ensure!(!droppable.is_empty(), error::DroppableColumnsSnafu);
|
||||||
let name = droppable[rng.gen_range(0..droppable.len())]
|
let name = droppable[rng.gen_range(0..droppable.len())]
|
||||||
@@ -110,25 +105,20 @@ impl Generator<AlterTableExpr> for AlterExprDropColumnGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AlterExprRenameGenerator {
|
/// Generates the [AlterTableOperation::RenameTable] of [AlterTableExpr].
|
||||||
|
#[derive(Builder)]
|
||||||
|
#[builder(pattern = "owned")]
|
||||||
|
pub struct AlterExprRenameGenerator<R: Rng> {
|
||||||
table_ctx: TableContextRef,
|
table_ctx: TableContextRef,
|
||||||
|
#[builder(default = "Box::new(WordGenerator)")]
|
||||||
|
name_generator: Box<dyn Random<String, R>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlterExprRenameGenerator {
|
impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprRenameGenerator<R> {
|
||||||
/// Returns an [AlterExprRenameGenerator].
|
|
||||||
pub fn new(table_ctx: &TableContextRef) -> Self {
|
|
||||||
Self {
|
|
||||||
table_ctx: table_ctx.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Generator<AlterTableExpr> for AlterExprRenameGenerator {
|
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn generate(&self) -> Result<AlterTableExpr> {
|
fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
|
||||||
let mut rng = rand::thread_rng();
|
let new_table_name = self.name_generator.gen(rng);
|
||||||
let mut new_table_name = rng.gen::<Word>().to_string();
|
|
||||||
Ok(AlterTableExpr {
|
Ok(AlterTableExpr {
|
||||||
name: self.table_ctx.name.to_string(),
|
name: self.table_ctx.name.to_string(),
|
||||||
alter_options: AlterTableOperation::RenameTable { new_table_name },
|
alter_options: AlterTableOperation::RenameTable { new_table_name },
|
||||||
@@ -140,29 +130,53 @@ impl Generator<AlterTableExpr> for AlterExprRenameGenerator {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rand::SeedableRng;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::context::TableContext;
|
use crate::context::TableContext;
|
||||||
use crate::generator::create_expr::CreateTableExprGenerator;
|
use crate::generator::create_expr::CreateTableExprGeneratorBuilder;
|
||||||
use crate::generator::Generator;
|
use crate::generator::Generator;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_alter_table_expr_generator() {
|
fn test_alter_table_expr_generator_deterministic() {
|
||||||
let create_expr = CreateTableExprGenerator::default()
|
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
|
||||||
|
let create_expr = CreateTableExprGeneratorBuilder::default()
|
||||||
.columns(10)
|
.columns(10)
|
||||||
.generate()
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let table_ctx = Arc::new(TableContext::from(&create_expr));
|
let table_ctx = Arc::new(TableContext::from(&create_expr));
|
||||||
|
|
||||||
let alter_expr = AlterExprAddColumnGenerator::new(&table_ctx)
|
let expr = AlterExprAddColumnGeneratorBuilder::default()
|
||||||
.generate()
|
.table_ctx(table_ctx.clone())
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let serialized = serde_json::to_string(&expr).unwrap();
|
||||||
|
let expected = r#"{"name":"DigNissIMOS","alter_options":{"AddColumn":{"column":{"name":"sit","column_type":{"Boolean":null},"options":["PrimaryKey"]},"location":null}}}"#;
|
||||||
|
assert_eq!(expected, serialized);
|
||||||
|
|
||||||
let alter_expr = AlterExprRenameGenerator::new(&table_ctx)
|
let expr = AlterExprRenameGeneratorBuilder::default()
|
||||||
.generate()
|
.table_ctx(table_ctx.clone())
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let serialized = serde_json::to_string(&expr).unwrap();
|
||||||
|
let expected = r#"{"name":"DigNissIMOS","alter_options":{"RenameTable":{"new_table_name":"excepturi"}}}"#;
|
||||||
|
assert_eq!(expected, serialized);
|
||||||
|
|
||||||
let alter_expr = AlterExprDropColumnGenerator::new(&table_ctx)
|
let expr = AlterExprDropColumnGeneratorBuilder::default()
|
||||||
.generate()
|
.table_ctx(table_ctx)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let serialized = serde_json::to_string(&expr).unwrap();
|
||||||
|
let expected =
|
||||||
|
r#"{"name":"DigNissIMOS","alter_options":{"DropColumn":{"name":"INVentORE"}}}"#;
|
||||||
|
assert_eq!(expected, serialized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use faker_rand::lorem::Word;
|
use derive_builder::Builder;
|
||||||
use partition::partition::{PartitionBound, PartitionDef};
|
use partition::partition::{PartitionBound, PartitionDef};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -20,61 +20,60 @@ use snafu::{ensure, ResultExt};
|
|||||||
|
|
||||||
use super::Generator;
|
use super::Generator;
|
||||||
use crate::error::{self, Error, Result};
|
use crate::error::{self, Error, Result};
|
||||||
use crate::ir::create_expr::{ColumnOption, CreateTableExprBuilder};
|
use crate::fake::{random_capitalize_map, MapWordGenerator};
|
||||||
|
use crate::generator::{ColumnOptionGenerator, ConcreteDataTypeGenerator, Random};
|
||||||
|
use crate::ir::create_expr::CreateTableExprBuilder;
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
self, generate_random_value, Column, CreateTableExpr, PartibleColumn, TsColumn,
|
column_options_generator, generate_columns, generate_random_value,
|
||||||
PARTIBLE_DATA_TYPES,
|
partible_column_options_generator, ts_column_options_generator, ColumnTypeGenerator,
|
||||||
|
CreateTableExpr, PartibleColumnTypeGenerator, TsColumnTypeGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct CreateTableExprGenerator {
|
#[derive(Builder)]
|
||||||
|
#[builder(default, pattern = "owned")]
|
||||||
|
pub struct CreateTableExprGenerator<R: Rng + 'static> {
|
||||||
columns: usize,
|
columns: usize,
|
||||||
|
#[builder(setter(into))]
|
||||||
engine: String,
|
engine: String,
|
||||||
partition: usize,
|
partition: usize,
|
||||||
if_not_exists: bool,
|
if_not_exists: bool,
|
||||||
|
#[builder(setter(into))]
|
||||||
|
name: String,
|
||||||
|
name_generator: Box<dyn Random<String, R>>,
|
||||||
|
ts_column_type_generator: ConcreteDataTypeGenerator<R>,
|
||||||
|
column_type_generator: ConcreteDataTypeGenerator<R>,
|
||||||
|
partible_column_type_generator: ConcreteDataTypeGenerator<R>,
|
||||||
|
partible_column_options_generator: ColumnOptionGenerator<R>,
|
||||||
|
column_options_generator: ColumnOptionGenerator<R>,
|
||||||
|
ts_column_options_generator: ColumnOptionGenerator<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_ENGINE: &str = "mito";
|
const DEFAULT_ENGINE: &str = "mito";
|
||||||
|
|
||||||
impl Default for CreateTableExprGenerator {
|
impl<R: Rng + 'static> Default for CreateTableExprGenerator<R> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
columns: 0,
|
columns: 0,
|
||||||
engine: DEFAULT_ENGINE.to_string(),
|
engine: DEFAULT_ENGINE.to_string(),
|
||||||
if_not_exists: false,
|
if_not_exists: false,
|
||||||
partition: 0,
|
partition: 0,
|
||||||
|
name: String::new(),
|
||||||
|
name_generator: Box::new(MapWordGenerator::new(Box::new(random_capitalize_map))),
|
||||||
|
ts_column_type_generator: Box::new(TsColumnTypeGenerator),
|
||||||
|
column_type_generator: Box::new(ColumnTypeGenerator),
|
||||||
|
partible_column_type_generator: Box::new(PartibleColumnTypeGenerator),
|
||||||
|
partible_column_options_generator: Box::new(partible_column_options_generator),
|
||||||
|
column_options_generator: Box::new(column_options_generator),
|
||||||
|
ts_column_options_generator: Box::new(ts_column_options_generator),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateTableExprGenerator {
|
impl<R: Rng + 'static> Generator<CreateTableExpr, R> for CreateTableExprGenerator<R> {
|
||||||
/// Sets column num.
|
type Error = Error;
|
||||||
pub fn columns(mut self, columns: usize) -> Self {
|
|
||||||
self.columns = columns;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `if_not_exists`.
|
|
||||||
pub fn create_is_not_exists(mut self, v: bool) -> Self {
|
|
||||||
self.if_not_exists = v;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `engine`.
|
|
||||||
pub fn engine(mut self, engine: &str) -> Self {
|
|
||||||
self.engine = engine.to_string();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the partition num.
|
|
||||||
/// If there is no primary key column,
|
|
||||||
/// it appends a primary key atomically.
|
|
||||||
pub fn partitions(mut self, partition: usize) -> Self {
|
|
||||||
self.partition = partition;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates the [CreateTableExpr].
|
/// Generates the [CreateTableExpr].
|
||||||
pub fn generate(&self) -> Result<CreateTableExpr> {
|
fn generate(&self, rng: &mut R) -> Result<CreateTableExpr> {
|
||||||
ensure!(
|
ensure!(
|
||||||
self.columns != 0,
|
self.columns != 0,
|
||||||
error::UnexpectedSnafu {
|
error::UnexpectedSnafu {
|
||||||
@@ -83,100 +82,153 @@ impl CreateTableExprGenerator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut builder = CreateTableExprBuilder::default();
|
let mut builder = CreateTableExprBuilder::default();
|
||||||
let mut columns = Vec::with_capacity(self.columns + 1);
|
let mut columns = Vec::with_capacity(self.columns);
|
||||||
let mut primary_keys = vec![];
|
let mut primary_keys = vec![];
|
||||||
let mut rng = rand::thread_rng();
|
let need_partible_column = self.partition > 1;
|
||||||
|
let mut column_names = self.name_generator.choose(rng, self.columns);
|
||||||
|
|
||||||
// Generates columns.
|
if self.columns == 1 {
|
||||||
for i in 0..self.columns {
|
// Generates the ts column.
|
||||||
let mut column = rng.gen::<Column>();
|
// Safety: columns must large than 0.
|
||||||
let is_primary_key = column
|
let name = column_names.pop().unwrap();
|
||||||
.options
|
let column = generate_columns(
|
||||||
.iter()
|
rng,
|
||||||
.any(|option| option == &ColumnOption::PrimaryKey);
|
vec![name.to_string()],
|
||||||
if is_primary_key {
|
self.ts_column_type_generator.as_ref(),
|
||||||
primary_keys.push(i);
|
self.ts_column_options_generator.as_ref(),
|
||||||
|
)
|
||||||
|
.remove(0);
|
||||||
|
|
||||||
|
if need_partible_column {
|
||||||
|
// Generates partition bounds.
|
||||||
|
let mut partition_bounds = Vec::with_capacity(self.partition);
|
||||||
|
for _ in 0..self.partition - 1 {
|
||||||
|
partition_bounds.push(PartitionBound::Value(generate_random_value(
|
||||||
|
rng,
|
||||||
|
&column.column_type,
|
||||||
|
)));
|
||||||
|
partition_bounds.sort();
|
||||||
|
}
|
||||||
|
partition_bounds.push(PartitionBound::MaxValue);
|
||||||
|
builder.partition(PartitionDef::new(vec![name], partition_bounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.push(column);
|
columns.push(column);
|
||||||
|
} else {
|
||||||
|
// Generates the partible column.
|
||||||
|
if need_partible_column {
|
||||||
|
// Safety: columns must large than 0.
|
||||||
|
let name = column_names.pop().unwrap();
|
||||||
|
let column = generate_columns(
|
||||||
|
rng,
|
||||||
|
vec![name.to_string()],
|
||||||
|
self.partible_column_type_generator.as_ref(),
|
||||||
|
self.partible_column_options_generator.as_ref(),
|
||||||
|
)
|
||||||
|
.remove(0);
|
||||||
|
|
||||||
|
// Generates partition bounds.
|
||||||
|
let mut partition_bounds = Vec::with_capacity(self.partition);
|
||||||
|
for _ in 0..self.partition - 1 {
|
||||||
|
partition_bounds.push(PartitionBound::Value(generate_random_value(
|
||||||
|
rng,
|
||||||
|
&column.column_type,
|
||||||
|
)));
|
||||||
|
partition_bounds.sort();
|
||||||
|
}
|
||||||
|
partition_bounds.push(PartitionBound::MaxValue);
|
||||||
|
builder.partition(PartitionDef::new(vec![name], partition_bounds));
|
||||||
|
columns.push(column);
|
||||||
|
}
|
||||||
|
// Generates the ts column.
|
||||||
|
// Safety: columns must large than 1.
|
||||||
|
let name = column_names.pop().unwrap();
|
||||||
|
columns.extend(generate_columns(
|
||||||
|
rng,
|
||||||
|
vec![name],
|
||||||
|
self.ts_column_type_generator.as_ref(),
|
||||||
|
self.ts_column_options_generator.as_ref(),
|
||||||
|
));
|
||||||
|
// Generates rest columns
|
||||||
|
columns.extend(generate_columns(
|
||||||
|
rng,
|
||||||
|
column_names,
|
||||||
|
self.column_type_generator.as_ref(),
|
||||||
|
self.column_options_generator.as_ref(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (idx, column) in columns.iter().enumerate() {
|
||||||
|
if column.is_primary_key() {
|
||||||
|
primary_keys.push(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Shuffles the primary keys.
|
// Shuffles the primary keys.
|
||||||
primary_keys.shuffle(&mut rng);
|
primary_keys.shuffle(rng);
|
||||||
let partitions = if self.partition > 1 {
|
|
||||||
// Finds a partible primary keys.
|
|
||||||
let partible_primary_keys = primary_keys
|
|
||||||
.iter()
|
|
||||||
.flat_map(|i| {
|
|
||||||
if PARTIBLE_DATA_TYPES.contains(&columns[*i].column_type) {
|
|
||||||
Some(*i)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Generates the partitions.
|
|
||||||
if partible_primary_keys.is_empty() {
|
|
||||||
columns.push(rng.gen::<PartibleColumn>().0);
|
|
||||||
primary_keys.push(columns.len() - 1);
|
|
||||||
}
|
|
||||||
let primary_key_idx = primary_keys[rng.gen_range(0..primary_keys.len())];
|
|
||||||
let primary_column = &columns[primary_key_idx];
|
|
||||||
|
|
||||||
// Generates partition bounds.
|
|
||||||
let mut partition_bounds = Vec::with_capacity(self.partition);
|
|
||||||
for _ in 0..self.partition - 1 {
|
|
||||||
partition_bounds.push(PartitionBound::Value(generate_random_value(
|
|
||||||
&primary_column.column_type,
|
|
||||||
)));
|
|
||||||
partition_bounds.sort();
|
|
||||||
}
|
|
||||||
partition_bounds.push(PartitionBound::MaxValue);
|
|
||||||
|
|
||||||
vec![PartitionDef::new(
|
|
||||||
vec![primary_column.name.to_string()],
|
|
||||||
partition_bounds,
|
|
||||||
)]
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
|
||||||
// Generates ts column.
|
|
||||||
columns.push(rng.gen::<TsColumn>().0);
|
|
||||||
|
|
||||||
builder.columns(columns);
|
builder.columns(columns);
|
||||||
builder.primary_keys(primary_keys);
|
builder.primary_keys(primary_keys);
|
||||||
builder.engine(self.engine.to_string());
|
builder.engine(self.engine.to_string());
|
||||||
builder.if_not_exists(self.if_not_exists);
|
builder.if_not_exists(self.if_not_exists);
|
||||||
builder.name(rng.gen::<Word>().to_string());
|
if self.name.is_empty() {
|
||||||
builder.partitions(partitions);
|
builder.name(self.name_generator.gen(rng));
|
||||||
|
} else {
|
||||||
|
builder.name(self.name.to_string());
|
||||||
|
}
|
||||||
builder.build().context(error::BuildCreateTableExprSnafu)
|
builder.build().context(error::BuildCreateTableExprSnafu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use rand::SeedableRng;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_table_expr_generator() {
|
fn test_create_table_expr_generator() {
|
||||||
let expr = CreateTableExprGenerator::default()
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let expr = CreateTableExprGeneratorBuilder::default()
|
||||||
.columns(10)
|
.columns(10)
|
||||||
.partitions(3)
|
.partition(3)
|
||||||
.create_is_not_exists(true)
|
.if_not_exists(true)
|
||||||
.engine("mito2")
|
.engine("mito2")
|
||||||
.generate()
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(expr.engine, "mito2");
|
assert_eq!(expr.engine, "mito2");
|
||||||
assert!(expr.if_not_exists);
|
assert!(expr.if_not_exists);
|
||||||
assert!(expr.columns.len() >= 11);
|
assert_eq!(expr.columns.len(), 10);
|
||||||
assert_eq!(expr.partitions[0].partition_bounds().len(), 3);
|
assert_eq!(expr.partition.unwrap().partition_bounds().len(), 3);
|
||||||
|
|
||||||
let expr = CreateTableExprGenerator::default()
|
let expr = CreateTableExprGeneratorBuilder::default()
|
||||||
.columns(10)
|
.columns(10)
|
||||||
.partitions(1)
|
.partition(1)
|
||||||
.generate()
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(expr.columns.len(), 11);
|
assert_eq!(expr.columns.len(), 10);
|
||||||
assert_eq!(expr.partitions.len(), 0);
|
assert!(expr.partition.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_table_expr_generator_deterministic() {
|
||||||
|
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
|
||||||
|
let expr = CreateTableExprGeneratorBuilder::default()
|
||||||
|
.columns(10)
|
||||||
|
.partition(3)
|
||||||
|
.if_not_exists(true)
|
||||||
|
.engine("mito2")
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.generate(&mut rng)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&expr).unwrap();
|
||||||
|
let expected = r#"{"name":"iN","columns":[{"name":"CUlpa","column_type":{"Int16":{}},"options":["PrimaryKey","NotNull"]},{"name":"dEBiTiS","column_type":{"Timestamp":{"Second":null}},"options":["TimeIndex"]},{"name":"HArum","column_type":{"Int16":{}},"options":["NotNull"]},{"name":"NObIS","column_type":{"Int32":{}},"options":["PrimaryKey"]},{"name":"IMPEDiT","column_type":{"Int16":{}},"options":[{"DefaultValue":{"Int16":-25151}}]},{"name":"bLanDITIis","column_type":{"Boolean":null},"options":[{"DefaultValue":{"Boolean":true}}]},{"name":"Dolores","column_type":{"Float32":{}},"options":["PrimaryKey"]},{"name":"eSt","column_type":{"Float32":{}},"options":[{"DefaultValue":{"Float32":0.9152612}}]},{"name":"INVentORE","column_type":{"Int64":{}},"options":["PrimaryKey"]},{"name":"aDIpiSci","column_type":{"Float64":{}},"options":["Null"]}],"if_not_exists":true,"partition":{"partition_columns":["CUlpa"],"partition_bounds":[{"Value":{"Int16":15966}},{"Value":{"Int16":31925}},"MaxValue"]},"engine":"mito2","options":{},"primary_keys":[6,0,8,3]}"#;
|
||||||
|
assert_eq!(expected, serialized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,17 @@ pub use create_expr::CreateTableExpr;
|
|||||||
use datatypes::data_type::ConcreteDataType;
|
use datatypes::data_type::ConcreteDataType;
|
||||||
use datatypes::value::Value;
|
use datatypes::value::Value;
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use faker_rand::lorem::Word;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::distributions::{Distribution, Standard};
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::generator::Random;
|
||||||
|
use crate::impl_random;
|
||||||
use crate::ir::create_expr::ColumnOption;
|
use crate::ir::create_expr::ColumnOption;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref DATA_TYPES: Vec<ConcreteDataType> = vec![
|
pub static ref DATA_TYPES: Vec<ConcreteDataType> = vec![
|
||||||
ConcreteDataType::boolean_datatype(),
|
ConcreteDataType::boolean_datatype(),
|
||||||
ConcreteDataType::int16_datatype(),
|
ConcreteDataType::int16_datatype(),
|
||||||
ConcreteDataType::int32_datatype(),
|
ConcreteDataType::int32_datatype(),
|
||||||
@@ -38,7 +40,7 @@ lazy_static! {
|
|||||||
ConcreteDataType::float32_datatype(),
|
ConcreteDataType::float32_datatype(),
|
||||||
ConcreteDataType::float64_datatype(),
|
ConcreteDataType::float64_datatype(),
|
||||||
];
|
];
|
||||||
static ref TS_DATA_TYPES: Vec<ConcreteDataType> = vec![
|
pub static ref TS_DATA_TYPES: Vec<ConcreteDataType> = vec![
|
||||||
ConcreteDataType::timestamp_nanosecond_datatype(),
|
ConcreteDataType::timestamp_nanosecond_datatype(),
|
||||||
ConcreteDataType::timestamp_microsecond_datatype(),
|
ConcreteDataType::timestamp_microsecond_datatype(),
|
||||||
ConcreteDataType::timestamp_millisecond_datatype(),
|
ConcreteDataType::timestamp_millisecond_datatype(),
|
||||||
@@ -56,25 +58,37 @@ lazy_static! {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_random!(ConcreteDataType, ColumnTypeGenerator, DATA_TYPES);
|
||||||
|
impl_random!(ConcreteDataType, TsColumnTypeGenerator, TS_DATA_TYPES);
|
||||||
|
impl_random!(
|
||||||
|
ConcreteDataType,
|
||||||
|
PartibleColumnTypeGenerator,
|
||||||
|
PARTIBLE_DATA_TYPES
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct ColumnTypeGenerator;
|
||||||
|
pub struct TsColumnTypeGenerator;
|
||||||
|
pub struct PartibleColumnTypeGenerator;
|
||||||
|
|
||||||
/// Generates a random [Value].
|
/// Generates a random [Value].
|
||||||
pub fn generate_random_value(datatype: &ConcreteDataType) -> Value {
|
pub fn generate_random_value<R: Rng>(rng: &mut R, datatype: &ConcreteDataType) -> Value {
|
||||||
match datatype {
|
match datatype {
|
||||||
&ConcreteDataType::Boolean(_) => Value::from(rand::random::<bool>()),
|
&ConcreteDataType::Boolean(_) => Value::from(rng.gen::<bool>()),
|
||||||
ConcreteDataType::Int16(_) => Value::from(rand::random::<i16>()),
|
ConcreteDataType::Int16(_) => Value::from(rng.gen::<i16>()),
|
||||||
ConcreteDataType::Int32(_) => Value::from(rand::random::<i32>()),
|
ConcreteDataType::Int32(_) => Value::from(rng.gen::<i32>()),
|
||||||
ConcreteDataType::Int64(_) => Value::from(rand::random::<i64>()),
|
ConcreteDataType::Int64(_) => Value::from(rng.gen::<i64>()),
|
||||||
ConcreteDataType::Float32(_) => Value::from(rand::random::<f32>()),
|
ConcreteDataType::Float32(_) => Value::from(rng.gen::<f32>()),
|
||||||
ConcreteDataType::Float64(_) => Value::from(rand::random::<f64>()),
|
ConcreteDataType::Float64(_) => Value::from(rng.gen::<f64>()),
|
||||||
ConcreteDataType::String(_) => Value::from(rand::random::<char>().to_string()),
|
ConcreteDataType::String(_) => Value::from(rng.gen::<char>().to_string()),
|
||||||
ConcreteDataType::Date(_) => Value::from(rand::random::<i32>()),
|
ConcreteDataType::Date(_) => Value::from(rng.gen::<i32>()),
|
||||||
ConcreteDataType::DateTime(_) => Value::from(rand::random::<i64>()),
|
ConcreteDataType::DateTime(_) => Value::from(rng.gen::<i64>()),
|
||||||
|
|
||||||
_ => unimplemented!("unsupported type: {datatype}"),
|
_ => unimplemented!("unsupported type: {datatype}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The IR column.
|
/// The IR column.
|
||||||
#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Builder, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
pub struct Column {
|
pub struct Column {
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -83,6 +97,22 @@ pub struct Column {
|
|||||||
pub options: Vec<ColumnOption>,
|
pub options: Vec<ColumnOption>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Column {
|
||||||
|
/// Returns true if it's [ColumnOption::TimeIndex] [Column].
|
||||||
|
pub fn is_time_index(&self) -> bool {
|
||||||
|
self.options
|
||||||
|
.iter()
|
||||||
|
.any(|opt| opt == &ColumnOption::TimeIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if it's the [ColumnOption::PrimaryKey] [Column].
|
||||||
|
pub fn is_primary_key(&self) -> bool {
|
||||||
|
self.options
|
||||||
|
.iter()
|
||||||
|
.any(|opt| opt == &ColumnOption::PrimaryKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns droppable columns. i.e., non-primary key columns, non-ts columns.
|
/// Returns droppable columns. i.e., non-primary key columns, non-ts columns.
|
||||||
pub fn droppable_columns(columns: &[Column]) -> Vec<&Column> {
|
pub fn droppable_columns(columns: &[Column]) -> Vec<&Column> {
|
||||||
columns
|
columns
|
||||||
@@ -95,59 +125,78 @@ pub fn droppable_columns(columns: &[Column]) -> Vec<&Column> {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Distribution<Column> for Standard {
|
/// Generates [ColumnOption] for [Column].
|
||||||
fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> Column {
|
pub fn column_options_generator<R: Rng>(
|
||||||
let column_type = DATA_TYPES[rng.gen_range(0..DATA_TYPES.len())].clone();
|
rng: &mut R,
|
||||||
// 0 -> NULL
|
column_type: &ConcreteDataType,
|
||||||
// 1 -> NOT NULL
|
) -> Vec<ColumnOption> {
|
||||||
// 2 -> DEFAULT VALUE
|
// 0 -> NULL
|
||||||
// 3 -> PRIMARY KEY
|
// 1 -> NOT NULL
|
||||||
// 4 -> EMPTY
|
// 2 -> DEFAULT VALUE
|
||||||
let option_idx = rng.gen_range(0..5);
|
// 3 -> PRIMARY KEY
|
||||||
let options = match option_idx {
|
// 4 -> EMPTY
|
||||||
0 => vec![ColumnOption::Null],
|
let option_idx = rng.gen_range(0..5);
|
||||||
1 => vec![ColumnOption::NotNull],
|
match option_idx {
|
||||||
2 => vec![ColumnOption::DefaultValue(generate_random_value(
|
0 => vec![ColumnOption::Null],
|
||||||
&column_type,
|
1 => vec![ColumnOption::NotNull],
|
||||||
))],
|
2 => vec![ColumnOption::DefaultValue(generate_random_value(
|
||||||
3 => vec![ColumnOption::PrimaryKey],
|
rng,
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
Column {
|
|
||||||
name: rng.gen::<Word>().to_string(),
|
|
||||||
column_type,
|
column_type,
|
||||||
options,
|
))],
|
||||||
}
|
3 => vec![ColumnOption::PrimaryKey],
|
||||||
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The IR ts column.
|
/// Generates [ColumnOption] for Partible [Column].
|
||||||
pub struct TsColumn(pub Column);
|
pub fn partible_column_options_generator<R: Rng + 'static>(
|
||||||
|
rng: &mut R,
|
||||||
impl Distribution<TsColumn> for Standard {
|
column_type: &ConcreteDataType,
|
||||||
fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> TsColumn {
|
) -> Vec<ColumnOption> {
|
||||||
let column_type = TS_DATA_TYPES[rng.gen_range(0..TS_DATA_TYPES.len())].clone();
|
// 0 -> NULL
|
||||||
TsColumn(Column {
|
// 1 -> NOT NULL
|
||||||
name: rng.gen::<Word>().to_string(),
|
// 2 -> DEFAULT VALUE
|
||||||
column_type,
|
// 3 -> PRIMARY KEY
|
||||||
options: vec![],
|
let option_idx = rng.gen_range(0..4);
|
||||||
})
|
match option_idx {
|
||||||
|
0 => vec![ColumnOption::PrimaryKey, ColumnOption::Null],
|
||||||
|
1 => vec![ColumnOption::PrimaryKey, ColumnOption::NotNull],
|
||||||
|
2 => vec![
|
||||||
|
ColumnOption::PrimaryKey,
|
||||||
|
ColumnOption::DefaultValue(generate_random_value(rng, column_type)),
|
||||||
|
],
|
||||||
|
3 => vec![ColumnOption::PrimaryKey],
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The IR partible column.
|
/// Generates [ColumnOption] for ts [Column].
|
||||||
pub struct PartibleColumn(pub Column);
|
pub fn ts_column_options_generator<R: Rng + 'static>(
|
||||||
|
_: &mut R,
|
||||||
|
_: &ConcreteDataType,
|
||||||
|
) -> Vec<ColumnOption> {
|
||||||
|
vec![ColumnOption::TimeIndex]
|
||||||
|
}
|
||||||
|
|
||||||
impl Distribution<PartibleColumn> for Standard {
|
/// Generates columns with given `names`.
|
||||||
fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> PartibleColumn {
|
pub fn generate_columns<R: Rng + 'static>(
|
||||||
let column_type = PARTIBLE_DATA_TYPES[rng.gen_range(0..PARTIBLE_DATA_TYPES.len())].clone();
|
rng: &mut R,
|
||||||
PartibleColumn(Column {
|
names: impl IntoIterator<Item = String>,
|
||||||
name: rng.gen::<Word>().to_string(),
|
types: &(impl Random<ConcreteDataType, R> + ?Sized),
|
||||||
column_type,
|
options: impl Fn(&mut R, &ConcreteDataType) -> Vec<ColumnOption>,
|
||||||
options: vec![],
|
) -> Vec<Column> {
|
||||||
|
names
|
||||||
|
.into_iter()
|
||||||
|
.map(|name| {
|
||||||
|
let column_type = types.gen(rng);
|
||||||
|
let options = options(rng, &column_type);
|
||||||
|
Column {
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
column_type,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::ir::Column;
|
use crate::ir::Column;
|
||||||
|
|
||||||
#[derive(Debug, Builder, Clone)]
|
#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
|
||||||
pub struct AlterTableExpr {
|
pub struct AlterTableExpr {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub alter_options: AlterTableOperation,
|
pub alter_options: AlterTableOperation,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::ir::Column;
|
use crate::ir::Column;
|
||||||
|
|
||||||
// The column options
|
// The column options
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||||
pub enum ColumnOption {
|
pub enum ColumnOption {
|
||||||
Null,
|
Null,
|
||||||
NotNull,
|
NotNull,
|
||||||
@@ -49,6 +49,7 @@ impl Display for ColumnOption {
|
|||||||
/// A naive create table expr builder.
|
/// A naive create table expr builder.
|
||||||
#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
|
||||||
pub struct CreateTableExpr {
|
pub struct CreateTableExpr {
|
||||||
|
#[builder(setter(into))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub columns: Vec<Column>,
|
pub columns: Vec<Column>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
@@ -56,7 +57,8 @@ pub struct CreateTableExpr {
|
|||||||
|
|
||||||
// GreptimeDB specific options
|
// GreptimeDB specific options
|
||||||
#[builder(default, setter(into))]
|
#[builder(default, setter(into))]
|
||||||
pub partitions: Vec<PartitionDef>,
|
pub partition: Option<PartitionDef>,
|
||||||
|
#[builder(default, setter(into))]
|
||||||
pub engine: String,
|
pub engine: String,
|
||||||
#[builder(default, setter(into))]
|
#[builder(default, setter(into))]
|
||||||
pub options: HashMap<String, Value>,
|
pub options: HashMap<String, Value>,
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
pub(crate) mod context;
|
#![feature(associated_type_bounds)]
|
||||||
pub(crate) mod error;
|
|
||||||
pub(crate) mod executor;
|
pub mod context;
|
||||||
// TODO(weny): removes it.
|
pub mod error;
|
||||||
#[allow(unused)]
|
pub mod executor;
|
||||||
pub(crate) mod generator;
|
pub mod fake;
|
||||||
pub(crate) mod ir;
|
pub mod generator;
|
||||||
pub(crate) mod table_creator;
|
pub mod ir;
|
||||||
pub(crate) mod translator;
|
pub mod translator;
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
pub mod greptime;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub(crate) trait DslTranslator<T, U> {
|
pub trait DslTranslator<T, U> {
|
||||||
type Error: Sync + Send + fmt::Debug;
|
type Error: Sync + Send + fmt::Debug;
|
||||||
|
|
||||||
fn translate(&self, input: &T) -> Result<U, Self::Error>;
|
fn translate(&self, input: &T) -> Result<U, Self::Error>;
|
||||||
|
|||||||
@@ -11,3 +11,6 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
pub mod alter_expr;
|
||||||
|
pub mod create_expr;
|
||||||
159
tests-fuzz/src/translator/greptime/alter_expr.rs
Normal file
159
tests-fuzz/src/translator/greptime/alter_expr.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use common_query::AddColumnLocation;
|
||||||
|
use datatypes::data_type::ConcreteDataType;
|
||||||
|
use sql::statements::concrete_data_type_to_sql_data_type;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::ir::alter_expr::AlterTableOperation;
|
||||||
|
use crate::ir::create_expr::ColumnOption;
|
||||||
|
use crate::ir::{AlterTableExpr, Column};
|
||||||
|
use crate::translator::DslTranslator;
|
||||||
|
|
||||||
|
pub struct AlterTableExprTranslator;
|
||||||
|
|
||||||
|
impl DslTranslator<AlterTableExpr, String> for AlterTableExprTranslator {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn translate(&self, input: &AlterTableExpr) -> Result<String> {
|
||||||
|
Ok(match &input.alter_options {
|
||||||
|
AlterTableOperation::AddColumn { column, location } => {
|
||||||
|
Self::format_add_column(&input.name, column, location)
|
||||||
|
}
|
||||||
|
AlterTableOperation::DropColumn { name } => Self::format_drop(&input.name, name),
|
||||||
|
AlterTableOperation::RenameTable { new_table_name } => {
|
||||||
|
Self::format_rename(&input.name, new_table_name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlterTableExprTranslator {
|
||||||
|
fn format_drop(name: &str, column: &str) -> String {
|
||||||
|
format!("ALTER TABLE {name} DROP COLUMN {column};")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_rename(name: &str, new_name: &str) -> String {
|
||||||
|
format!("ALTER TABLE {name} RENAME {new_name};")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_add_column(
|
||||||
|
name: &str,
|
||||||
|
column: &Column,
|
||||||
|
location: &Option<AddColumnLocation>,
|
||||||
|
) -> String {
|
||||||
|
format!(
|
||||||
|
"{};",
|
||||||
|
vec![
|
||||||
|
format!(
|
||||||
|
"ALTER TABLE {name} ADD COLUMN {}",
|
||||||
|
Self::format_column(column)
|
||||||
|
),
|
||||||
|
Self::format_location(location).unwrap_or_default(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_location(location: &Option<AddColumnLocation>) -> Option<String> {
|
||||||
|
location.as_ref().map(|location| match location {
|
||||||
|
AddColumnLocation::First => "FIRST".to_string(),
|
||||||
|
AddColumnLocation::After { column_name } => format!("AFTER {column_name}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_column(column: &Column) -> String {
|
||||||
|
vec![
|
||||||
|
column.name.to_string(),
|
||||||
|
Self::format_column_type(&column.column_type),
|
||||||
|
Self::format_column_options(&column.options),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_column_type(column_type: &ConcreteDataType) -> String {
|
||||||
|
// Safety: We don't use the `Dictionary` type
|
||||||
|
concrete_data_type_to_sql_data_type(column_type)
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_column_options(options: &[ColumnOption]) -> String {
|
||||||
|
options
|
||||||
|
.iter()
|
||||||
|
.map(|option| option.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use common_query::AddColumnLocation;
|
||||||
|
use datatypes::data_type::ConcreteDataType;
|
||||||
|
|
||||||
|
use super::AlterTableExprTranslator;
|
||||||
|
use crate::ir::alter_expr::AlterTableOperation;
|
||||||
|
use crate::ir::create_expr::ColumnOption;
|
||||||
|
use crate::ir::{AlterTableExpr, Column};
|
||||||
|
use crate::translator::DslTranslator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alter_table_expr() {
|
||||||
|
let alter_expr = AlterTableExpr {
|
||||||
|
name: "test".to_string(),
|
||||||
|
alter_options: AlterTableOperation::AddColumn {
|
||||||
|
column: Column {
|
||||||
|
name: "host".to_string(),
|
||||||
|
column_type: ConcreteDataType::string_datatype(),
|
||||||
|
options: vec![ColumnOption::PrimaryKey],
|
||||||
|
},
|
||||||
|
location: Some(AddColumnLocation::First),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
"ALTER TABLE test ADD COLUMN host STRING PRIMARY KEY FIRST;",
|
||||||
|
output
|
||||||
|
);
|
||||||
|
|
||||||
|
let alter_expr = AlterTableExpr {
|
||||||
|
name: "test".to_string(),
|
||||||
|
alter_options: AlterTableOperation::RenameTable {
|
||||||
|
new_table_name: "foo".to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
|
||||||
|
assert_eq!("ALTER TABLE test RENAME foo;", output);
|
||||||
|
|
||||||
|
let alter_expr = AlterTableExpr {
|
||||||
|
name: "test".to_string(),
|
||||||
|
alter_options: AlterTableOperation::DropColumn {
|
||||||
|
name: "foo".to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
|
||||||
|
assert_eq!("ALTER TABLE test DROP COLUMN foo;", output);
|
||||||
|
}
|
||||||
|
}
|
||||||
233
tests-fuzz/src/translator/greptime/create_expr.rs
Normal file
233
tests-fuzz/src/translator/greptime/create_expr.rs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use datatypes::data_type::ConcreteDataType;
|
||||||
|
use datatypes::value::Value;
|
||||||
|
use partition::partition::PartitionBound;
|
||||||
|
use sql::statements::concrete_data_type_to_sql_data_type;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::ir::create_expr::ColumnOption;
|
||||||
|
use crate::ir::{Column, CreateTableExpr};
|
||||||
|
use crate::translator::DslTranslator;
|
||||||
|
|
||||||
|
pub struct CreateTableExprTranslator;
|
||||||
|
|
||||||
|
impl DslTranslator<CreateTableExpr, String> for CreateTableExprTranslator {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn translate(&self, input: &CreateTableExpr) -> Result<String> {
|
||||||
|
Ok(format!(
|
||||||
|
"CREATE TABLE{}{}(\n{}\n)\n{};",
|
||||||
|
Self::create_if_not_exists(input),
|
||||||
|
input.name,
|
||||||
|
Self::format_columns(input),
|
||||||
|
Self::format_table_options(input)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateTableExprTranslator {
|
||||||
|
fn create_if_not_exists(input: &CreateTableExpr) -> &str {
|
||||||
|
if input.if_not_exists {
|
||||||
|
" IF NOT EXISTS "
|
||||||
|
} else {
|
||||||
|
" "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_columns(input: &CreateTableExpr) -> String {
|
||||||
|
let mut output =
|
||||||
|
Vec::with_capacity(input.columns.len() + (!input.primary_keys.is_empty()) as usize);
|
||||||
|
for column in &input.columns {
|
||||||
|
output.push(Self::format_column(column));
|
||||||
|
}
|
||||||
|
if let Some(primary_keys) = Self::format_primary_keys(input) {
|
||||||
|
output.push(primary_keys);
|
||||||
|
}
|
||||||
|
output.join(",\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_column(column: &Column) -> String {
|
||||||
|
vec![
|
||||||
|
column.name.to_string(),
|
||||||
|
Self::format_column_type(&column.column_type),
|
||||||
|
Self::format_column_options(&column.options),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_partition(input: &CreateTableExpr) -> Option<String> {
|
||||||
|
input.partition.as_ref().map(|partition| {
|
||||||
|
format!(
|
||||||
|
"PARTITION BY RANGE COLUMNS({}) (\n{}\n)",
|
||||||
|
partition.partition_columns().join(", "),
|
||||||
|
partition
|
||||||
|
.partition_bounds()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, bound)| format!(
|
||||||
|
"PARTITION r{} VALUES LESS THAN ({})",
|
||||||
|
i,
|
||||||
|
Self::format_partition_bound(bound)
|
||||||
|
))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",\n")
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_partition_bound(bound: &PartitionBound) -> String {
|
||||||
|
match bound {
|
||||||
|
PartitionBound::Value(v) => match v {
|
||||||
|
Value::String(v) => format!("'{}'", v.as_utf8()),
|
||||||
|
_ => format!("{v}"),
|
||||||
|
},
|
||||||
|
PartitionBound::MaxValue => "MAXVALUE".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_column_type(column_type: &ConcreteDataType) -> String {
|
||||||
|
// Safety: We don't use the `Dictionary` type
|
||||||
|
concrete_data_type_to_sql_data_type(column_type)
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_column_options(options: &[ColumnOption]) -> String {
|
||||||
|
let mut output = Vec::with_capacity(options.len());
|
||||||
|
for option in options {
|
||||||
|
if option != &ColumnOption::PrimaryKey {
|
||||||
|
output.push(option.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_primary_keys(input: &CreateTableExpr) -> Option<String> {
|
||||||
|
if input.primary_keys.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(format!(
|
||||||
|
"PRIMARY KEY({})",
|
||||||
|
input
|
||||||
|
.primary_keys
|
||||||
|
.iter()
|
||||||
|
.map(|idx| input.columns[*idx].name.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_table_options(input: &CreateTableExpr) -> String {
|
||||||
|
let mut output = vec![];
|
||||||
|
if !input.engine.is_empty() {
|
||||||
|
output.push(format!("ENGINE={}", input.engine));
|
||||||
|
}
|
||||||
|
if let Some(partition) = Self::format_partition(input) {
|
||||||
|
output.push(partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use datatypes::data_type::ConcreteDataType;
|
||||||
|
use datatypes::value::Value;
|
||||||
|
use partition::partition::{PartitionBound, PartitionDef};
|
||||||
|
|
||||||
|
use super::CreateTableExprTranslator;
|
||||||
|
use crate::ir::create_expr::{ColumnOption, CreateTableExprBuilder};
|
||||||
|
use crate::ir::Column;
|
||||||
|
use crate::translator::DslTranslator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_table_expr_translator() {
|
||||||
|
let create_table_expr = CreateTableExprBuilder::default()
|
||||||
|
.columns(vec![
|
||||||
|
Column {
|
||||||
|
name: "host".to_string(),
|
||||||
|
column_type: ConcreteDataType::string_datatype(),
|
||||||
|
options: vec![ColumnOption::PrimaryKey],
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "idc".to_string(),
|
||||||
|
column_type: ConcreteDataType::string_datatype(),
|
||||||
|
options: vec![ColumnOption::PrimaryKey],
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "cpu_util".to_string(),
|
||||||
|
column_type: ConcreteDataType::float64_datatype(),
|
||||||
|
options: vec![],
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "memory_util".to_string(),
|
||||||
|
column_type: ConcreteDataType::float64_datatype(),
|
||||||
|
options: vec![],
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "disk_util".to_string(),
|
||||||
|
column_type: ConcreteDataType::float64_datatype(),
|
||||||
|
options: vec![],
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
name: "ts".to_string(),
|
||||||
|
column_type: ConcreteDataType::timestamp_millisecond_datatype(),
|
||||||
|
options: vec![ColumnOption::TimeIndex],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.name("system_metrics")
|
||||||
|
.engine("mito")
|
||||||
|
.primary_keys(vec![0, 1])
|
||||||
|
.partition(PartitionDef::new(
|
||||||
|
vec!["idc".to_string()],
|
||||||
|
vec![
|
||||||
|
PartitionBound::Value(Value::String("a".into())),
|
||||||
|
PartitionBound::Value(Value::String("f".into())),
|
||||||
|
PartitionBound::MaxValue,
|
||||||
|
],
|
||||||
|
))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let output = CreateTableExprTranslator
|
||||||
|
.translate(&create_table_expr)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"CREATE TABLE system_metrics(
|
||||||
|
host STRING,
|
||||||
|
idc STRING,
|
||||||
|
cpu_util DOUBLE,
|
||||||
|
memory_util DOUBLE,
|
||||||
|
disk_util DOUBLE,
|
||||||
|
ts TIMESTAMP(3) TIME INDEX,
|
||||||
|
PRIMARY KEY(host, idc)
|
||||||
|
)
|
||||||
|
ENGINE=mito
|
||||||
|
PARTITION BY RANGE COLUMNS(idc) (
|
||||||
|
PARTITION r0 VALUES LESS THAN ('a'),
|
||||||
|
PARTITION r1 VALUES LESS THAN ('f'),
|
||||||
|
PARTITION r2 VALUES LESS THAN (MAXVALUE)
|
||||||
|
);",
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
ue = "ue"
|
ue = "ue"
|
||||||
datas = "datas"
|
datas = "datas"
|
||||||
[files]
|
[files]
|
||||||
extend-exclude = ["corrupted"]
|
extend-exclude = ["corrupted","tests-fuzz/src/data/lorem_words"]
|
||||||
|
|||||||
Reference in New Issue
Block a user