mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-06-01 21:00:38 +00:00
feat: support add if not exists in the gRPC alter kind (#5273)
* test: test adding existing columns * chore: add more checks to AlterKind * chore: update logs * fix: check and build table info first * feat: Add add_if_not_exists flag to alter expr * feat: skip existing columns when building alter kind * checks in make_region_alter_kind() * reuse the alter kind * test: fix tests in common-meta * chore: fix typos * chore: update comments
This commit is contained in:
@@ -194,12 +194,9 @@ impl TableMeta {
|
||||
&self,
|
||||
table_name: &str,
|
||||
alter_kind: &AlterKind,
|
||||
add_if_not_exists: bool,
|
||||
) -> Result<TableMetaBuilder> {
|
||||
match alter_kind {
|
||||
AlterKind::AddColumns { columns } => {
|
||||
self.add_columns(table_name, columns, add_if_not_exists)
|
||||
}
|
||||
AlterKind::AddColumns { columns } => self.add_columns(table_name, columns),
|
||||
AlterKind::DropColumns { names } => self.remove_columns(table_name, names),
|
||||
AlterKind::ModifyColumnTypes { columns } => {
|
||||
self.modify_column_types(table_name, columns)
|
||||
@@ -340,6 +337,7 @@ impl TableMeta {
|
||||
Ok(meta_builder)
|
||||
}
|
||||
|
||||
// TODO(yingwen): Remove this.
|
||||
/// Allocate a new column for the table.
|
||||
///
|
||||
/// This method would bump the `next_column_id` of the meta.
|
||||
@@ -384,11 +382,11 @@ impl TableMeta {
|
||||
builder
|
||||
}
|
||||
|
||||
// TODO(yingwen): Tests add if not exists.
|
||||
fn add_columns(
|
||||
&self,
|
||||
table_name: &str,
|
||||
requests: &[AddColumnRequest],
|
||||
add_if_not_exists: bool,
|
||||
) -> Result<TableMetaBuilder> {
|
||||
let table_schema = &self.schema;
|
||||
let mut meta_builder = self.new_meta_builder();
|
||||
@@ -396,63 +394,61 @@ impl TableMeta {
|
||||
self.primary_key_indices.iter().collect();
|
||||
|
||||
let mut names = HashSet::with_capacity(requests.len());
|
||||
let mut new_requests = Vec::with_capacity(requests.len());
|
||||
let requests = if add_if_not_exists {
|
||||
for col_to_add in requests {
|
||||
if let Some(column_schema) =
|
||||
table_schema.column_schema_by_name(&col_to_add.column_schema.name)
|
||||
{
|
||||
// If the column already exists, we should check if the type is the same.
|
||||
ensure!(
|
||||
column_schema.data_type == col_to_add.column_schema.data_type,
|
||||
error::InvalidAlterRequestSnafu {
|
||||
table: table_name,
|
||||
err: format!(
|
||||
"column {} already exists with different type",
|
||||
col_to_add.column_schema.name
|
||||
),
|
||||
}
|
||||
);
|
||||
} else {
|
||||
new_requests.push(col_to_add.clone());
|
||||
}
|
||||
}
|
||||
&new_requests[..]
|
||||
} else {
|
||||
requests
|
||||
};
|
||||
let mut new_columns = Vec::with_capacity(requests.len());
|
||||
for col_to_add in requests {
|
||||
ensure!(
|
||||
names.insert(&col_to_add.column_schema.name),
|
||||
error::InvalidAlterRequestSnafu {
|
||||
table: table_name,
|
||||
err: format!(
|
||||
"add column {} more than once",
|
||||
col_to_add.column_schema.name
|
||||
),
|
||||
}
|
||||
);
|
||||
if let Some(column_schema) =
|
||||
table_schema.column_schema_by_name(&col_to_add.column_schema.name)
|
||||
{
|
||||
// If the column already exists.
|
||||
ensure!(
|
||||
col_to_add.add_if_not_exists,
|
||||
error::ColumnExistsSnafu {
|
||||
table_name,
|
||||
column_name: &col_to_add.column_schema.name
|
||||
},
|
||||
);
|
||||
|
||||
ensure!(
|
||||
!table_schema.contains_column(&col_to_add.column_schema.name),
|
||||
error::ColumnExistsSnafu {
|
||||
table_name,
|
||||
column_name: col_to_add.column_schema.name.to_string()
|
||||
},
|
||||
);
|
||||
// Checks if the type is the same
|
||||
ensure!(
|
||||
column_schema.data_type == col_to_add.column_schema.data_type,
|
||||
error::InvalidAlterRequestSnafu {
|
||||
table: table_name,
|
||||
err: format!(
|
||||
"column {} already exists with different type {:?}",
|
||||
col_to_add.column_schema.name, column_schema.data_type,
|
||||
),
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// A new column.
|
||||
// Ensures we only add a column once.
|
||||
ensure!(
|
||||
names.insert(&col_to_add.column_schema.name),
|
||||
error::InvalidAlterRequestSnafu {
|
||||
table: table_name,
|
||||
err: format!(
|
||||
"add column {} more than once",
|
||||
col_to_add.column_schema.name
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
ensure!(
|
||||
col_to_add.column_schema.is_nullable()
|
||||
|| col_to_add.column_schema.default_constraint().is_some(),
|
||||
error::InvalidAlterRequestSnafu {
|
||||
table: table_name,
|
||||
err: format!(
|
||||
"no default value for column {}",
|
||||
col_to_add.column_schema.name
|
||||
),
|
||||
},
|
||||
);
|
||||
ensure!(
|
||||
col_to_add.column_schema.is_nullable()
|
||||
|| col_to_add.column_schema.default_constraint().is_some(),
|
||||
error::InvalidAlterRequestSnafu {
|
||||
table: table_name,
|
||||
err: format!(
|
||||
"no default value for column {}",
|
||||
col_to_add.column_schema.name
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
new_columns.push(col_to_add.clone());
|
||||
}
|
||||
}
|
||||
let requests = &new_columns[..];
|
||||
|
||||
let SplitResult {
|
||||
columns_at_first,
|
||||
@@ -881,6 +877,7 @@ pub struct RawTableMeta {
|
||||
pub value_indices: Vec<usize>,
|
||||
/// Engine type of this table. Usually in small case.
|
||||
pub engine: String,
|
||||
/// Next column id of a new column.
|
||||
/// Deprecated. See https://github.com/GreptimeTeam/greptimedb/issues/2982
|
||||
pub next_column_id: ColumnId,
|
||||
pub region_numbers: Vec<u32>,
|
||||
@@ -1078,6 +1075,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Create a test schema with 3 columns: `[col1 int32, ts timestampmills, col2 int32]`.
|
||||
fn new_test_schema() -> Schema {
|
||||
let column_schemas = vec![
|
||||
ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
|
||||
@@ -1129,17 +1127,19 @@ mod tests {
|
||||
column_schema: new_tag,
|
||||
is_key: true,
|
||||
location: None,
|
||||
add_if_not_exists: false,
|
||||
},
|
||||
AddColumnRequest {
|
||||
column_schema: new_field,
|
||||
is_key: false,
|
||||
location: None,
|
||||
add_if_not_exists: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let builder = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap();
|
||||
builder.build().unwrap()
|
||||
}
|
||||
@@ -1157,6 +1157,7 @@ mod tests {
|
||||
column_schema: new_tag,
|
||||
is_key: true,
|
||||
location: Some(AddColumnLocation::First),
|
||||
add_if_not_exists: false,
|
||||
},
|
||||
AddColumnRequest {
|
||||
column_schema: new_field,
|
||||
@@ -1164,12 +1165,13 @@ mod tests {
|
||||
location: Some(AddColumnLocation::After {
|
||||
column_name: "ts".to_string(),
|
||||
}),
|
||||
add_if_not_exists: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let builder = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap();
|
||||
builder.build().unwrap()
|
||||
}
|
||||
@@ -1199,6 +1201,48 @@ mod tests {
|
||||
assert_eq!(&[1, 2, 4], &new_meta.value_indices[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_columns_multiple_times() {
|
||||
let schema = Arc::new(new_test_schema());
|
||||
let meta = TableMetaBuilder::default()
|
||||
.schema(schema)
|
||||
.primary_key_indices(vec![0])
|
||||
.engine("engine")
|
||||
.next_column_id(3)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let alter_kind = AlterKind::AddColumns {
|
||||
columns: vec![
|
||||
AddColumnRequest {
|
||||
column_schema: ColumnSchema::new(
|
||||
"col3",
|
||||
ConcreteDataType::int32_datatype(),
|
||||
true,
|
||||
),
|
||||
is_key: true,
|
||||
location: None,
|
||||
add_if_not_exists: true,
|
||||
},
|
||||
AddColumnRequest {
|
||||
column_schema: ColumnSchema::new(
|
||||
"col3",
|
||||
ConcreteDataType::int32_datatype(),
|
||||
true,
|
||||
),
|
||||
is_key: true,
|
||||
location: None,
|
||||
add_if_not_exists: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_columns() {
|
||||
let schema = Arc::new(new_test_schema());
|
||||
@@ -1216,7 +1260,7 @@ mod tests {
|
||||
names: vec![String::from("col2"), String::from("my_field")],
|
||||
};
|
||||
let new_meta = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -1271,7 +1315,7 @@ mod tests {
|
||||
names: vec![String::from("col3"), String::from("col1")],
|
||||
};
|
||||
let new_meta = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -1307,14 +1351,62 @@ mod tests {
|
||||
column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
|
||||
is_key: false,
|
||||
location: None,
|
||||
add_if_not_exists: false,
|
||||
}],
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::TableColumnExists, err.status_code());
|
||||
|
||||
// Add if not exists
|
||||
let alter_kind = AlterKind::AddColumns {
|
||||
columns: vec![AddColumnRequest {
|
||||
column_schema: ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), true),
|
||||
is_key: true,
|
||||
location: None,
|
||||
add_if_not_exists: true,
|
||||
}],
|
||||
};
|
||||
let new_meta = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
meta.schema.column_schemas(),
|
||||
new_meta.schema.column_schemas()
|
||||
);
|
||||
assert_eq!(meta.schema.version() + 1, new_meta.schema.version());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_different_type_column() {
|
||||
let schema = Arc::new(new_test_schema());
|
||||
let meta = TableMetaBuilder::default()
|
||||
.schema(schema)
|
||||
.primary_key_indices(vec![0])
|
||||
.engine("engine")
|
||||
.next_column_id(3)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Add if not exists, but different type.
|
||||
let alter_kind = AlterKind::AddColumns {
|
||||
columns: vec![AddColumnRequest {
|
||||
column_schema: ColumnSchema::new("col1", ConcreteDataType::string_datatype(), true),
|
||||
is_key: false,
|
||||
location: None,
|
||||
add_if_not_exists: true,
|
||||
}],
|
||||
};
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1328,6 +1420,7 @@ mod tests {
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Not nullable and no default value.
|
||||
let alter_kind = AlterKind::AddColumns {
|
||||
columns: vec![AddColumnRequest {
|
||||
column_schema: ColumnSchema::new(
|
||||
@@ -1337,11 +1430,12 @@ mod tests {
|
||||
),
|
||||
is_key: false,
|
||||
location: None,
|
||||
add_if_not_exists: false,
|
||||
}],
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
@@ -1363,7 +1457,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
|
||||
@@ -1388,7 +1482,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::TableColumnNotFound, err.status_code());
|
||||
@@ -1411,7 +1505,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
@@ -1422,7 +1516,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
@@ -1448,7 +1542,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
@@ -1462,7 +1556,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(StatusCode::InvalidArguments, err.status_code());
|
||||
@@ -1531,7 +1625,7 @@ mod tests {
|
||||
options: FulltextOptions::default(),
|
||||
};
|
||||
let err = meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -1552,7 +1646,7 @@ mod tests {
|
||||
},
|
||||
};
|
||||
let new_meta = new_meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -1572,7 +1666,7 @@ mod tests {
|
||||
column_name: "my_tag_first".to_string(),
|
||||
};
|
||||
let new_meta = new_meta
|
||||
.builder_with_alter_kind("my_table", &alter_kind, false)
|
||||
.builder_with_alter_kind("my_table", &alter_kind)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
@@ -185,6 +185,8 @@ pub struct AddColumnRequest {
|
||||
pub column_schema: ColumnSchema,
|
||||
pub is_key: bool,
|
||||
pub location: Option<AddColumnLocation>,
|
||||
/// Add column if not exists.
|
||||
pub add_if_not_exists: bool,
|
||||
}
|
||||
|
||||
/// Change column datatype request
|
||||
|
||||
Reference in New Issue
Block a user