feat(json2): support altering JSON2 settings (#9029)

* feat(sql): support alter syntax for JSON2 columns

Signed-off-by: fys <fengys1996@gmail.com>

* fix(json2): preserve rows on type hint mismatch during compaction

* refactor(json2): simplify alter settings handling

* fix(json2): preserve coerced values during compaction

* chore: remove unnecessary clone

* chor: reduce memory allocations

* fix: cargo clippy

* chore: update greptime-proto to main branch

* refactor(datatypes): unify string handling with other JSON type hints

* fix: cr

---------

Signed-off-by: fys <fengys1996@gmail.com>
This commit is contained in:
fys
2026-09-22 12:56:26 +00:00
committed by GitHub
parent aa36f74feb
commit 045441e3cc
30 changed files with 1602 additions and 231 deletions
+13 -15
View File
@@ -462,19 +462,19 @@ impl ParserContext<'_> {
.context(error::SyntaxSnafu)?;
self.parse_alter_table_drop_default(column_name)
} else {
let (data_type, json2_options) =
if let Some(json2) = parse_json2_type_and_options(&mut self.parser)? {
json2
} else {
(
self.parser.parse_data_type().context(error::SyntaxSnafu)?,
None,
)
};
if let Some((_, json2_options)) =
parse_json2_type_and_options(&mut self.parser)?
{
return Ok(AlterTableOperation::SetJsonSettings {
column_name,
json2_options,
});
}
Ok(AlterTableOperation::ModifyColumnType {
column_name,
target_type: data_type,
json2_options,
target_type: self.parser.parse_data_type().context(error::SyntaxSnafu)?,
json2_options: None,
})
}
}
@@ -1576,9 +1576,8 @@ MODIFY COLUMN attrs JSON2 (
let Statement::AlterTable(alter_table) = statements.remove(0) else {
unreachable!()
};
let AlterTableOperation::ModifyColumnType {
let AlterTableOperation::SetJsonSettings {
column_name,
target_type,
json2_options: Some(options),
} = alter_table.alter_operation()
else {
@@ -1586,7 +1585,6 @@ MODIFY COLUMN attrs JSON2 (
};
assert_eq!("attrs", column_name.value);
assert_eq!("JSON2", target_type.to_string());
assert_eq!(Some(2000), options.max_auto_expanded_paths);
assert_eq!(4, options.type_hints.len());
assert_eq!(vec!["user", "id"], options.type_hints[1].path);
@@ -1610,7 +1608,7 @@ MODIFY COLUMN attrs JSON2 (
let Statement::AlterTable(empty) = empty.remove(0) else {
unreachable!()
};
let AlterTableOperation::ModifyColumnType { json2_options, .. } = empty.alter_operation()
let AlterTableOperation::SetJsonSettings { json2_options, .. } = empty.alter_operation()
else {
unreachable!()
};
+37 -1
View File
@@ -88,6 +88,11 @@ pub enum AlterTableOperation {
target_type: DataType,
json2_options: Option<Json2Options>,
},
/// `MODIFY <column_name> JSON2 [json2_options]`
SetJsonSettings {
column_name: Ident,
json2_options: Option<Json2Options>,
},
/// `SET <table attrs key> = <table attr value>`
SetTableOptions {
options: Vec<KeyValueOption>,
@@ -265,6 +270,16 @@ impl Display for AlterTableOperation {
}
Ok(())
}
AlterTableOperation::SetJsonSettings {
column_name,
json2_options,
} => {
write!(f, r#"MODIFY COLUMN {column_name} JSON2"#)?;
if let Some(options) = json2_options {
write!(f, "{options}")?;
}
Ok(())
}
AlterTableOperation::SetTableOptions { options } => {
let kvs = options
.iter()
@@ -433,8 +448,10 @@ impl Display for AlterDatabaseOperation {
mod tests {
use std::assert_matches;
use super::AlterTableOperation;
use crate::dialect::GreptimeDbDialect;
use crate::parser::{ParseOptions, ParserContext};
use crate::statements::create::Json2Options;
use crate::statements::statement::Statement;
#[test]
@@ -503,7 +520,7 @@ ALTER TABLE monitor ADD COLUMN app STRING DEFAULT 'shop' PRIMARY KEY, ADD COLUMN
}
let sql = r"alter table monitor modify column load_15 string;";
let stmts =
let mut stmts =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
assert_eq!(1, stmts.len());
@@ -523,6 +540,25 @@ ALTER TABLE monitor MODIFY COLUMN load_15 STRING"#,
}
}
let Statement::AlterTable(alter_table) = &mut stmts[0] else {
unreachable!();
};
let AlterTableOperation::ModifyColumnType { json2_options, .. } =
alter_table.alter_operation_mut()
else {
unreachable!();
};
*json2_options = Some(Json2Options {
max_auto_expanded_paths: Some(1),
type_hints: vec![],
});
assert_eq!(
r#"ALTER TABLE monitor MODIFY COLUMN load_15 STRING(
max_auto_expanded_paths = 1
)"#,
alter_table.to_string()
);
let sql = r"alter table monitor drop column load_15;";
let stmts =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
+21 -18
View File
@@ -157,6 +157,26 @@ impl Display for Json2Options {
}
}
impl Json2Options {
pub fn build_json_settings(&self) -> Result<JsonSettings> {
let type_hints = self
.type_hints
.iter()
.map(|hint| {
Ok(datatypes::json::JsonTypeHint {
path: hint.path.clone(),
data_type: json_type_hint_concrete_data_type(&hint.data_type)?,
inverted_index: hint.inverted_index,
})
})
.collect::<Result<Vec<_>>>()?;
let max_auto_expanded_paths = self
.max_auto_expanded_paths
.or(Some(JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS));
JsonSettings::try_new(type_hints, max_auto_expanded_paths).map_err(Into::into)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
pub struct JsonTypeHint {
pub path: Vec<String>,
@@ -351,24 +371,7 @@ impl ColumnExtensions {
return Ok(None);
};
let type_hints = options
.type_hints
.iter()
.map(|hint| {
Ok(datatypes::json::JsonTypeHint {
path: hint.path.clone(),
data_type: json_type_hint_concrete_data_type(&hint.data_type)?,
inverted_index: hint.inverted_index,
})
})
.collect::<Result<Vec<_>>>()?;
let settings = JsonSettings::try_new(
type_hints,
options
.max_auto_expanded_paths
.or(Some(JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS)),
)?;
Ok(Some(settings))
options.build_json_settings().map(Some)
}
pub fn set_json_settings(&mut self, settings: JsonSettings) -> Result<()> {