fix: alter table update table column default (#6155)

* fix: alter table update table column default

* fix: fuzz test also cast default value

* chore: more testcase

* test: non-zero value

* refactor: per review

* tests: unexpected alter result(WIP on fix)

* ub

* ub more

* test: update sqlness
This commit is contained in:
discord9
2025-05-27 17:42:27 +08:00
committed by GitHub
parent 40bfa98d4b
commit 53752e4f6c
6 changed files with 419 additions and 49 deletions

View File

@@ -205,7 +205,7 @@ impl FlowNameManager {
catalog: &str,
) -> BoxStream<'static, Result<(String, FlowNameValue)>> {
let start_key = FlowNameKey::range_start_key(catalog);
common_telemetry::debug!("flow_names: start_key: {:?}", start_key);
common_telemetry::trace!("flow_names: start_key: {:?}", start_key);
let req = RangeRequest::new().with_prefix(start_key);
let stream = PaginationStream::new(

View File

@@ -175,6 +175,14 @@ pub enum Error {
#[snafu(display("Invalid table name: '{s}'"))]
InvalidTableName { s: String },
#[snafu(display("Failed to cast default value, reason: {}", reason))]
CastDefaultValue {
reason: String,
source: datatypes::Error,
#[snafu(implicit)]
location: Location,
},
}
impl ErrorExt for Error {
@@ -187,6 +195,7 @@ impl ErrorExt for Error {
Error::RemoveColumnInIndex { .. }
| Error::BuildColumnDescriptor { .. }
| Error::InvalidAlterRequest { .. } => StatusCode::InvalidArguments,
Error::CastDefaultValue { source, .. } => source.status_code(),
Error::TablesRecordBatch { .. } => StatusCode::Unexpected,
Error::ColumnExists { .. } => StatusCode::TableColumnExists,
Error::SchemaBuild { source, .. } | Error::SetFulltextOptions { source, .. } => {

View File

@@ -817,17 +817,34 @@ impl TableMeta {
);
}
// Collect columns after changed.
let columns: Vec<_> = table_schema
.column_schemas()
.iter()
.cloned()
.map(|mut column| {
if let Some(change_column) = modify_column_types.get(&column.name) {
column.data_type = change_column.target_type.clone();
}
column
})
.collect();
let mut columns: Vec<_> = Vec::with_capacity(table_schema.column_schemas().len());
for mut column in table_schema.column_schemas().iter().cloned() {
if let Some(change_column) = modify_column_types.get(&column.name) {
column.data_type = change_column.target_type.clone();
let new_default = if let Some(default_value) = column.default_constraint() {
Some(
default_value
.cast_to_datatype(&change_column.target_type)
.with_context(|_| error::CastDefaultValueSnafu {
reason: format!(
"Failed to cast default value from {:?} to type {:?}",
default_value, &change_column.target_type
),
})?,
)
} else {
None
};
column = column
.clone()
.with_default_constraint(new_default.clone())
.with_context(|_| error::CastDefaultValueSnafu {
reason: format!("Failed to set new default: {:?}", new_default),
})?;
}
columns.push(column)
}
let mut builder = SchemaBuilder::try_from_columns(columns)
.with_context(|_| error::SchemaBuildSnafu {

View File

@@ -15,6 +15,7 @@
use std::sync::Arc;
use common_query::AddColumnLocation;
use datatypes::types::cast;
use partition::partition::PartitionDef;
use rand::Rng;
use snafu::{ensure, OptionExt};
@@ -22,6 +23,7 @@ use snafu::{ensure, OptionExt};
use crate::error::{self, Result};
use crate::generator::Random;
use crate::ir::alter_expr::{AlterTableOperation, AlterTableOption};
use crate::ir::create_expr::ColumnOption;
use crate::ir::{AlterTableExpr, Column, CreateTableExpr, Ident};
pub type TableContextRef = Arc<TableContext>;
@@ -138,7 +140,12 @@ impl TableContext {
}
AlterTableOperation::ModifyDataType { column } => {
if let Some(idx) = self.columns.iter().position(|col| col.name == column.name) {
self.columns[idx].column_type = column.column_type;
self.columns[idx].column_type = column.column_type.clone();
for opt in self.columns[idx].options.iter_mut() {
if let ColumnOption::DefaultValue(value) = opt {
*value = cast(value.clone(), &column.column_type).unwrap();
}
}
}
Ok(self)
}

View File

@@ -133,26 +133,49 @@ DROP TABLE test_alt_table;
Affected Rows: 0
-- test if column with default value can change type properly
CREATE TABLE test_alt_table_default(h INTEGER, i INTEGER DEFAULT 0, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
CREATE TABLE test_alt_table_default(h INTEGER, i Float64 DEFAULT 0.0, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
Affected Rows: 0
INSERT INTO test_alt_table_default (h, j) VALUES (0, 0);
Affected Rows: 1
INSERT INTO test_alt_table_default (h, i, j) VALUES (1, 0.1, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_default ORDER BY h;
+---+-----+---------------------+
| h | i | j |
+---+-----+---------------------+
| 0 | 0.0 | 1970-01-01T00:00:00 |
| 1 | 0.1 | 1970-01-01T00:00:00 |
+---+-----+---------------------+
ALTER TABLE test_alt_table_default MODIFY COLUMN i BOOLEAN;
Affected Rows: 0
INSERT INTO test_alt_table_default (h, j) VALUES (1, 0), (2, 1);
INSERT INTO test_alt_table_default (h, j) VALUES (2, 0);
Affected Rows: 2
Affected Rows: 1
INSERT INTO test_alt_table_default (h, i, j) VALUES (3, TRUE, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_default ORDER BY h;
+---+-------+-------------------------+
| h | i | j |
+---+-------+-------------------------+
| 1 | false | 1970-01-01T00:00:00 |
| 2 | false | 1970-01-01T00:00:00.001 |
+---+-------+-------------------------+
+---+-------+---------------------+
| h | i | j |
+---+-------+---------------------+
| 0 | false | 1970-01-01T00:00:00 |
| 1 | true | 1970-01-01T00:00:00 |
| 2 | false | 1970-01-01T00:00:00 |
| 3 | true | 1970-01-01T00:00:00 |
+---+-------+---------------------+
ALTER TABLE test_alt_table_default MODIFY COLUMN i INTEGER;
@@ -168,50 +191,279 @@ DESC TABLE test_alt_table_default;
| j | TimestampMillisecond | PRI | NO | | TIMESTAMP |
+--------+----------------------+-----+------+---------+---------------+
INSERT INTO test_alt_table_default (h, j) VALUES (3, 0), (4, 1);
INSERT INTO test_alt_table_default (h, j) VALUES (4, 0);
Affected Rows: 2
Affected Rows: 1
INSERT INTO test_alt_table_default (h, i, j) VALUES (5, 42, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_default ORDER BY h;
+---+---+-------------------------+
| h | i | j |
+---+---+-------------------------+
| 1 | 0 | 1970-01-01T00:00:00 |
| 2 | 0 | 1970-01-01T00:00:00.001 |
| 3 | 0 | 1970-01-01T00:00:00 |
| 4 | 0 | 1970-01-01T00:00:00.001 |
+---+---+-------------------------+
+---+----+---------------------+
| h | i | j |
+---+----+---------------------+
| 0 | 0 | 1970-01-01T00:00:00 |
| 1 | 0 | 1970-01-01T00:00:00 |
| 2 | 0 | 1970-01-01T00:00:00 |
| 3 | 1 | 1970-01-01T00:00:00 |
| 4 | 0 | 1970-01-01T00:00:00 |
| 5 | 42 | 1970-01-01T00:00:00 |
+---+----+---------------------+
ALTER TABLE test_alt_table_default MODIFY COLUMN i STRING;
Affected Rows: 0
INSERT INTO test_alt_table_default (h, j) VALUES (5, 0);
INSERT INTO test_alt_table_default (h, j) VALUES (6, 0);
Affected Rows: 1
INSERT INTO test_alt_table_default (h, i, j) VALUES (6, "word" ,1);
INSERT INTO test_alt_table_default (h, i, j) VALUES (7, "word" ,1);
Affected Rows: 1
SELECT * FROM test_alt_table_default ORDER BY h;
+---+------+-------------------------+
| h | i | j |
+---+------+-------------------------+
| 1 | 0 | 1970-01-01T00:00:00 |
| 2 | 0 | 1970-01-01T00:00:00.001 |
| 3 | 0 | 1970-01-01T00:00:00 |
| 4 | 0 | 1970-01-01T00:00:00.001 |
| 5 | 0 | 1970-01-01T00:00:00 |
| 6 | word | 1970-01-01T00:00:00.001 |
+---+------+-------------------------+
+---+-------+-------------------------+
| h | i | j |
+---+-------+-------------------------+
| 0 | 0.0 | 1970-01-01T00:00:00 |
| 1 | 0.1 | 1970-01-01T00:00:00 |
| 2 | false | 1970-01-01T00:00:00 |
| 3 | true | 1970-01-01T00:00:00 |
| 4 | 0 | 1970-01-01T00:00:00 |
| 5 | 42 | 1970-01-01T00:00:00 |
| 6 | 0 | 1970-01-01T00:00:00 |
| 7 | word | 1970-01-01T00:00:00.001 |
+---+-------+-------------------------+
DROP TABLE test_alt_table_default;
Affected Rows: 0
-- test with non-zero default value
CREATE TABLE test_alt_table_default_nz(h INTEGER, i Float64 DEFAULT 0.1, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
Affected Rows: 0
INSERT INTO test_alt_table_default_nz (h, j) VALUES (0, 0);
Affected Rows: 1
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (1, 0.0, 0);
Affected Rows: 1
ADMIN FLUSH_TABLE('test_alt_table_default_nz');
+------------------------------------------------+
| ADMIN FLUSH_TABLE('test_alt_table_default_nz') |
+------------------------------------------------+
| 0 |
+------------------------------------------------+
SELECT * FROM test_alt_table_default_nz ORDER BY h;
+---+-----+---------------------+
| h | i | j |
+---+-----+---------------------+
| 0 | 0.1 | 1970-01-01T00:00:00 |
| 1 | 0.0 | 1970-01-01T00:00:00 |
+---+-----+---------------------+
ALTER TABLE test_alt_table_default_nz MODIFY COLUMN i BOOLEAN;
Affected Rows: 0
INSERT INTO test_alt_table_default_nz (h, j) VALUES (2, 0);
Affected Rows: 1
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (3, FALSE, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_default_nz ORDER BY h;
+---+-------+---------------------+
| h | i | j |
+---+-------+---------------------+
| 0 | true | 1970-01-01T00:00:00 |
| 1 | false | 1970-01-01T00:00:00 |
| 2 | true | 1970-01-01T00:00:00 |
| 3 | false | 1970-01-01T00:00:00 |
+---+-------+---------------------+
ALTER TABLE test_alt_table_default_nz MODIFY COLUMN i INTEGER;
Affected Rows: 0
DESC TABLE test_alt_table_default_nz;
+--------+----------------------+-----+------+---------+---------------+
| Column | Type | Key | Null | Default | Semantic Type |
+--------+----------------------+-----+------+---------+---------------+
| h | Int32 | PRI | YES | | TAG |
| i | Int32 | | YES | 1 | FIELD |
| j | TimestampMillisecond | PRI | NO | | TIMESTAMP |
+--------+----------------------+-----+------+---------+---------------+
INSERT INTO test_alt_table_default_nz (h, j) VALUES (4, 0);
Affected Rows: 1
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (5, 42, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_default_nz ORDER BY h;
+---+----+---------------------+
| h | i | j |
+---+----+---------------------+
| 0 | 0 | 1970-01-01T00:00:00 |
| 1 | 0 | 1970-01-01T00:00:00 |
| 2 | 1 | 1970-01-01T00:00:00 |
| 3 | 0 | 1970-01-01T00:00:00 |
| 4 | 1 | 1970-01-01T00:00:00 |
| 5 | 42 | 1970-01-01T00:00:00 |
+---+----+---------------------+
ALTER TABLE test_alt_table_default_nz MODIFY COLUMN i STRING;
Affected Rows: 0
INSERT INTO test_alt_table_default_nz (h, j) VALUES (6, 0);
Affected Rows: 1
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (7, "word" ,1);
Affected Rows: 1
SELECT * FROM test_alt_table_default_nz ORDER BY h;
+---+-------+-------------------------+
| h | i | j |
+---+-------+-------------------------+
| 0 | 0.1 | 1970-01-01T00:00:00 |
| 1 | 0.0 | 1970-01-01T00:00:00 |
| 2 | true | 1970-01-01T00:00:00 |
| 3 | false | 1970-01-01T00:00:00 |
| 4 | 1 | 1970-01-01T00:00:00 |
| 5 | 42 | 1970-01-01T00:00:00 |
| 6 | 1 | 1970-01-01T00:00:00 |
| 7 | word | 1970-01-01T00:00:00.001 |
+---+-------+-------------------------+
DROP TABLE test_alt_table_default_nz;
Affected Rows: 0
-- test alter table type will cause wired behavior due to underlying column data is unchanged
CREATE TABLE test_alt_table_col_ty(h INTEGER, i Float64 DEFAULT 0.1, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
Affected Rows: 0
INSERT INTO test_alt_table_col_ty (h, j) VALUES (0, 0);
Affected Rows: 1
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (1, 0.2, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_col_ty ORDER BY h;
+---+-----+---------------------+
| h | i | j |
+---+-----+---------------------+
| 0 | 0.1 | 1970-01-01T00:00:00 |
| 1 | 0.2 | 1970-01-01T00:00:00 |
+---+-----+---------------------+
ALTER TABLE test_alt_table_col_ty MODIFY COLUMN i BOOLEAN;
Affected Rows: 0
INSERT INTO test_alt_table_col_ty (h, j) VALUES (2, 0);
Affected Rows: 1
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (3, TRUE, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_col_ty ORDER BY h;
+---+------+---------------------+
| h | i | j |
+---+------+---------------------+
| 0 | true | 1970-01-01T00:00:00 |
| 1 | true | 1970-01-01T00:00:00 |
| 2 | true | 1970-01-01T00:00:00 |
| 3 | true | 1970-01-01T00:00:00 |
+---+------+---------------------+
ALTER TABLE test_alt_table_col_ty MODIFY COLUMN i INTEGER;
Affected Rows: 0
INSERT INTO test_alt_table_col_ty (h, j) VALUES (4, 0);
Affected Rows: 1
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (5, 42, 0);
Affected Rows: 1
SELECT * FROM test_alt_table_col_ty ORDER BY h;
+---+----+---------------------+
| h | i | j |
+---+----+---------------------+
| 0 | 0 | 1970-01-01T00:00:00 |
| 1 | 0 | 1970-01-01T00:00:00 |
| 2 | 1 | 1970-01-01T00:00:00 |
| 3 | 1 | 1970-01-01T00:00:00 |
| 4 | 1 | 1970-01-01T00:00:00 |
| 5 | 42 | 1970-01-01T00:00:00 |
+---+----+---------------------+
ALTER TABLE test_alt_table_col_ty MODIFY COLUMN i STRING;
Affected Rows: 0
INSERT INTO test_alt_table_col_ty (h, j) VALUES (6, 0);
Affected Rows: 1
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (7, "how many roads must a man walk down before they call him a man", 0);
Affected Rows: 1
-- here see 0.1 is converted to "0.1" since underlying column data is unchanged
SELECT * FROM test_alt_table_col_ty ORDER BY h;
+---+----------------------------------------------------------------+---------------------+
| h | i | j |
+---+----------------------------------------------------------------+---------------------+
| 0 | 0.1 | 1970-01-01T00:00:00 |
| 1 | 0.2 | 1970-01-01T00:00:00 |
| 2 | true | 1970-01-01T00:00:00 |
| 3 | true | 1970-01-01T00:00:00 |
| 4 | 1 | 1970-01-01T00:00:00 |
| 5 | 42 | 1970-01-01T00:00:00 |
| 6 | 1 | 1970-01-01T00:00:00 |
| 7 | how many roads must a man walk down before they call him a man | 1970-01-01T00:00:00 |
+---+----------------------------------------------------------------+---------------------+
DROP TABLE test_alt_table_col_ty;
Affected Rows: 0
-- to test if same name column can be added
CREATE TABLE phy (ts timestamp time index, val double) engine = metric with ("physical_metric_table" = "");

View File

@@ -44,11 +44,19 @@ DESC TABLE test_alt_table;
DROP TABLE test_alt_table;
-- test if column with default value can change type properly
CREATE TABLE test_alt_table_default(h INTEGER, i INTEGER DEFAULT 0, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
CREATE TABLE test_alt_table_default(h INTEGER, i Float64 DEFAULT 0.0, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
INSERT INTO test_alt_table_default (h, j) VALUES (0, 0);
INSERT INTO test_alt_table_default (h, i, j) VALUES (1, 0.1, 0);
SELECT * FROM test_alt_table_default ORDER BY h;
ALTER TABLE test_alt_table_default MODIFY COLUMN i BOOLEAN;
INSERT INTO test_alt_table_default (h, j) VALUES (1, 0), (2, 1);
INSERT INTO test_alt_table_default (h, j) VALUES (2, 0);
INSERT INTO test_alt_table_default (h, i, j) VALUES (3, TRUE, 0);
SELECT * FROM test_alt_table_default ORDER BY h;
@@ -56,20 +64,97 @@ ALTER TABLE test_alt_table_default MODIFY COLUMN i INTEGER;
DESC TABLE test_alt_table_default;
INSERT INTO test_alt_table_default (h, j) VALUES (3, 0), (4, 1);
INSERT INTO test_alt_table_default (h, j) VALUES (4, 0);
INSERT INTO test_alt_table_default (h, i, j) VALUES (5, 42, 0);
SELECT * FROM test_alt_table_default ORDER BY h;
ALTER TABLE test_alt_table_default MODIFY COLUMN i STRING;
INSERT INTO test_alt_table_default (h, j) VALUES (5, 0);
INSERT INTO test_alt_table_default (h, j) VALUES (6, 0);
INSERT INTO test_alt_table_default (h, i, j) VALUES (6, "word" ,1);
INSERT INTO test_alt_table_default (h, i, j) VALUES (7, "word" ,1);
SELECT * FROM test_alt_table_default ORDER BY h;
DROP TABLE test_alt_table_default;
-- test with non-zero default value
CREATE TABLE test_alt_table_default_nz(h INTEGER, i Float64 DEFAULT 0.1, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
INSERT INTO test_alt_table_default_nz (h, j) VALUES (0, 0);
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (1, 0.0, 0);
ADMIN FLUSH_TABLE('test_alt_table_default_nz');
SELECT * FROM test_alt_table_default_nz ORDER BY h;
ALTER TABLE test_alt_table_default_nz MODIFY COLUMN i BOOLEAN;
INSERT INTO test_alt_table_default_nz (h, j) VALUES (2, 0);
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (3, FALSE, 0);
SELECT * FROM test_alt_table_default_nz ORDER BY h;
ALTER TABLE test_alt_table_default_nz MODIFY COLUMN i INTEGER;
DESC TABLE test_alt_table_default_nz;
INSERT INTO test_alt_table_default_nz (h, j) VALUES (4, 0);
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (5, 42, 0);
SELECT * FROM test_alt_table_default_nz ORDER BY h;
ALTER TABLE test_alt_table_default_nz MODIFY COLUMN i STRING;
INSERT INTO test_alt_table_default_nz (h, j) VALUES (6, 0);
INSERT INTO test_alt_table_default_nz (h, i, j) VALUES (7, "word" ,1);
SELECT * FROM test_alt_table_default_nz ORDER BY h;
DROP TABLE test_alt_table_default_nz;
-- test alter table type will cause wired behavior due to underlying column data is unchanged
CREATE TABLE test_alt_table_col_ty(h INTEGER, i Float64 DEFAULT 0.1, j TIMESTAMP TIME INDEX, PRIMARY KEY (h));
INSERT INTO test_alt_table_col_ty (h, j) VALUES (0, 0);
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (1, 0.2, 0);
SELECT * FROM test_alt_table_col_ty ORDER BY h;
ALTER TABLE test_alt_table_col_ty MODIFY COLUMN i BOOLEAN;
INSERT INTO test_alt_table_col_ty (h, j) VALUES (2, 0);
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (3, TRUE, 0);
SELECT * FROM test_alt_table_col_ty ORDER BY h;
ALTER TABLE test_alt_table_col_ty MODIFY COLUMN i INTEGER;
INSERT INTO test_alt_table_col_ty (h, j) VALUES (4, 0);
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (5, 42, 0);
SELECT * FROM test_alt_table_col_ty ORDER BY h;
ALTER TABLE test_alt_table_col_ty MODIFY COLUMN i STRING;
INSERT INTO test_alt_table_col_ty (h, j) VALUES (6, 0);
INSERT INTO test_alt_table_col_ty (h, i, j) VALUES (7, "how many roads must a man walk down before they call him a man", 0);
-- here see 0.1 is converted to "0.1" since underlying column data is unchanged
SELECT * FROM test_alt_table_col_ty ORDER BY h;
DROP TABLE test_alt_table_col_ty;
-- to test if same name column can be added
CREATE TABLE phy (ts timestamp time index, val double) engine = metric with ("physical_metric_table" = "");