Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: validate constraints eagerly #3472

Merged
merged 6 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 20 additions & 24 deletions src/sql/src/parsers/create_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,21 @@ impl<'a> ParserContext<'a> {
let _ = self.parser.next_token();
self.parser
.expect_keyword(Keyword::TABLE)
.context(error::SyntaxSnafu)?;
.context(SyntaxSnafu)?;
let if_not_exists =
self.parser
.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let table_name = self.intern_parse_table_name()?;
let (columns, constraints) = self.parse_columns()?;
if !columns.is_empty() {
waynexia marked this conversation as resolved.
Show resolved Hide resolved
validate_time_index(&columns, &constraints)?;
}

let engine = self.parse_table_engine(common_catalog::consts::FILE_ENGINE)?;
let options = self
.parser
.parse_options(Keyword::WITH)
.context(error::SyntaxSnafu)?
.context(SyntaxSnafu)?
.into_iter()
.filter_map(|option| {
if let Some(v) = parse_option_string(option.value) {
Expand Down Expand Up @@ -140,8 +144,12 @@ impl<'a> ParserContext<'a> {
}

let (columns, constraints) = self.parse_columns()?;
validate_time_index(&columns, &constraints)?;

let partitions = self.parse_partitions()?;
if let Some(partitions) = &partitions {
validate_partitions(&columns, partitions)?;
}

let engine = self.parse_table_engine(default_engine())?;
let options = self
Expand All @@ -168,7 +176,6 @@ impl<'a> ParserContext<'a> {
table_id: 0, // table id is assigned by catalog manager
partitions,
};
validate_create(&create_table)?;

Ok(Statement::CreateTable(create_table))
}
Expand Down Expand Up @@ -553,18 +560,8 @@ impl<'a> ParserContext<'a> {
}
}

fn validate_create(create_table: &CreateTable) -> Result<()> {
if let Some(partitions) = &create_table.partitions {
validate_partitions(&create_table.columns, partitions)?;
}
validate_time_index(create_table)?;

Ok(())
}

fn validate_time_index(create_table: &CreateTable) -> Result<()> {
let time_index_constraints: Vec<_> = create_table
.constraints
fn validate_time_index(columns: &[ColumnDef], constraints: &[TableConstraint]) -> Result<()> {
let time_index_constraints: Vec<_> = constraints
.iter()
.filter_map(|c| {
if let TableConstraint::Unique {
Expand Down Expand Up @@ -605,8 +602,7 @@ fn validate_time_index(create_table: &CreateTable) -> Result<()> {
// It's safe to use time_index_constraints[0][0],
// we already check the bound above.
let time_index_column_ident = &time_index_constraints[0][0];
let time_index_column = create_table
.columns
let time_index_column = columns
.iter()
.find(|c| c.name.value == *time_index_column_ident.value)
.with_context(|| InvalidTimeIndexSnafu {
Expand Down Expand Up @@ -753,7 +749,7 @@ mod tests {
fn test_validate_external_table_options() {
let sql = "CREATE EXTERNAL TABLE city (
host string,
ts int64,
ts timestamp,
cpu float64 default 0,
memory float64,
TIME INDEX (ts),
Expand Down Expand Up @@ -825,7 +821,7 @@ mod tests {
fn test_parse_create_external_table_with_schema() {
let sql = "CREATE EXTERNAL TABLE city (
host string,
ts int64,
ts timestamp,
cpu float32 default 0,
memory float64,
TIME INDEX (ts),
Expand All @@ -848,7 +844,7 @@ mod tests {

let columns = &c.columns;
assert_column_def(&columns[0], "host", "STRING");
assert_column_def(&columns[1], "ts", "BIGINT");
assert_column_def(&columns[1], "ts", "TIMESTAMP");
assert_column_def(&columns[2], "cpu", "FLOAT");
assert_column_def(&columns[3], "memory", "DOUBLE");

Expand Down Expand Up @@ -927,7 +923,7 @@ ENGINE=mito";
let _ = result.unwrap();

let sql = r"
CREATE TABLE rcx ( a INT, b STRING, c INT )
CREATE TABLE rcx ( ts TIMESTAMP TIME INDEX, a INT, b STRING, c INT )
PARTITION ON COLUMNS(x) ()
ENGINE=mito";
let result =
Expand Down Expand Up @@ -1315,7 +1311,7 @@ ENGINE=mito";
#[test]
fn test_parse_partitions_with_error_syntax() {
let sql = r"
CREATE TABLE rcx ( a INT, b STRING, c INT )
CREATE TABLE rcx ( ts TIMESTAMP TIME INDEX, a INT, b STRING, c INT )
PARTITION COLUMNS(c, a) (
a < 10,
a > 10 AND a < 20,
Expand Down Expand Up @@ -1344,7 +1340,7 @@ ENGINE=mito";
#[test]
fn test_parse_partitions_unreferenced_column() {
let sql = r"
CREATE TABLE rcx ( a INT, b STRING, c INT )
CREATE TABLE rcx ( ts TIMESTAMP TIME INDEX, a INT, b STRING, c INT )
PARTITION ON COLUMNS(c, a) (
b = 'foo'
)
Expand All @@ -1360,7 +1356,7 @@ ENGINE=mito";
#[test]
fn test_parse_partitions_not_binary_expr() {
let sql = r"
CREATE TABLE rcx ( a INT, b STRING, c INT )
CREATE TABLE rcx ( ts TIMESTAMP TIME INDEX, a INT, b STRING, c INT )
PARTITION ON COLUMNS(c, a) (
b
)
Expand Down
6 changes: 4 additions & 2 deletions src/sql/src/statements/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ pub struct CreateTableLike {

#[cfg(test)]
mod tests {
use std::assert_matches::assert_matches;

use crate::dialect::GreptimeDbDialect;
use crate::error::Error::InvalidTableOption;
use crate::parser::{ParseOptions, ParserContext};
Expand Down Expand Up @@ -344,7 +346,7 @@ ENGINE=mito
fn test_validate_table_options() {
let sql = r"create table if not exists demo(
host string,
ts bigint,
ts timestamp,
cpu double default 0,
memory double,
TIME INDEX (ts),
Expand All @@ -356,6 +358,6 @@ ENGINE=mito
";
let result =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
assert!(matches!(result, Err(InvalidTableOption { .. })))
assert_matches!(result, Err(InvalidTableOption { .. }))
}
}
1 change: 1 addition & 0 deletions tests-integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(assert_matches)]
pub mod cluster;
mod grpc;
mod influxdb;
Expand Down
3 changes: 2 additions & 1 deletion tests-integration/src/tests/instance_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::assert_matches::assert_matches;
use std::env;
use std::sync::Arc;

Expand Down Expand Up @@ -613,7 +614,7 @@ async fn test_execute_external_create_without_ts(instance: Arc<dyn MockInstance>
),
)
.await;
assert!(matches!(result, Err(Error::TableOperation { .. })));
assert_matches!(result, Err(Error::ParseSql { .. }));
}

#[apply(both_instances_cases)]
Expand Down