feat(json2): support ALTER syntax for JSON column settings (#9094)

feat(json2): support alter syntax for JSON2 columns

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

This is the commit message #3:
This commit is contained in:
fys
2026-09-10 10:33:41 +00:00
committed by GitHub
parent 0f625a7e92
commit 536f42e4a2
8 changed files with 210 additions and 6 deletions
+35
View File
@@ -826,9 +826,16 @@ pub(crate) fn to_alter_table_expr(
AlterTableOperation::ModifyColumnType {
column_name,
target_type,
..
} => {
let target_type =
sql_data_type_to_concrete_data_type(&target_type).context(ParseSqlSnafu)?;
if target_type.is_json2() {
return NotSupportedSnafu {
feat: "ALTER TABLE MODIFY COLUMN to JSON2 type",
}
.fail();
}
let (target_type, target_type_extension) = ColumnDataTypeWrapper::try_from(target_type)
.map(|w| w.to_parts())
.context(ColumnDataTypeSnafu)?;
@@ -1744,6 +1751,34 @@ SELECT max(c1), min(c2) FROM schema_2.table_2;";
assert!(modify_column_type.target_type_extension.is_none());
}
#[test]
fn test_alter_json2_is_not_supported() {
for sql in [
"ALTER TABLE monitor MODIFY COLUMN payload JSON2;",
"ALTER TABLE monitor MODIFY COLUMN payload JSON2 (service STRING);",
] {
let stmt = ParserContext::create_with_dialect(
sql,
&GreptimeDbDialect {},
ParseOptions::default(),
)
.unwrap()
.pop()
.unwrap();
let Statement::AlterTable(alter_table) = stmt else {
unreachable!()
};
let err = to_alter_table_expr(alter_table, &QueryContext::arc()).unwrap_err();
assert!(matches!(err, crate::error::Error::NotSupported { .. }));
assert_eq!(
"Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type",
err.to_string()
);
}
}
#[test]
fn test_to_repartition_request() {
let sql = r#"
+77 -2
View File
@@ -29,7 +29,7 @@ use sqlparser::tokenizer::{Token, TokenWithSpan};
use crate::ast::ObjectNamePartExt;
use crate::error::{self, InvalidColumnOptionSnafu, Result, SetFulltextOptionSnafu};
use crate::parser::ParserContext;
use crate::parsers::create_parser::INVERTED;
use crate::parsers::create_parser::{INVERTED, parse_json2_type_and_options};
use crate::parsers::utils::{
parse_with_options, validate_column_fulltext_create_option,
validate_column_skipping_index_create_option,
@@ -462,10 +462,19 @@ impl ParserContext<'_> {
.context(error::SyntaxSnafu)?;
self.parse_alter_table_drop_default(column_name)
} else {
let data_type = self.parser.parse_data_type().context(error::SyntaxSnafu)?;
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,
)
};
Ok(AlterTableOperation::ModifyColumnType {
column_name,
target_type: data_type,
json2_options,
})
}
}
@@ -1537,9 +1546,11 @@ ALTER TABLE metrics REPARTITION
AlterTableOperation::ModifyColumnType {
column_name,
target_type,
json2_options,
} => {
assert_eq!("a", column_name.value);
assert_eq!(DataType::String(None), *target_type);
assert!(json2_options.is_none());
}
_ => unreachable!(),
}
@@ -1548,6 +1559,66 @@ ALTER TABLE metrics REPARTITION
}
}
#[test]
fn test_parse_alter_json2() {
let sql = r#"ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
max_auto_expanded_paths = 2000,
trace_id STRING,
user.id STRING NOT NULL,
user.name STRING DEFAULT 'anonymous',
request_id STRING INVERTED INDEX
)"#;
let mut statements =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
let Statement::AlterTable(alter_table) = statements.remove(0) else {
unreachable!()
};
let AlterTableOperation::ModifyColumnType {
column_name,
target_type,
json2_options: Some(options),
} = alter_table.alter_operation()
else {
unreachable!()
};
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);
assert!(!options.type_hints[1].nullable);
assert!(options.type_hints[2].default.is_some());
assert!(options.type_hints[3].inverted_index);
let formatted = alter_table.to_string();
let reparsed = ParserContext::create_with_dialect(
&formatted,
&GreptimeDbDialect {},
ParseOptions::default(),
)
.unwrap();
assert_eq!(Statement::AlterTable(alter_table), reparsed[0]);
let mut empty = ParserContext::create_with_dialect(
"ALTER TABLE application_logs MODIFY COLUMN attrs JSON2 ()",
&GreptimeDbDialect {},
ParseOptions::default(),
)
.unwrap();
let Statement::AlterTable(empty) = empty.remove(0) else {
unreachable!()
};
let AlterTableOperation::ModifyColumnType { json2_options, .. } = empty.alter_operation()
else {
unreachable!()
};
assert!(json2_options.is_none());
}
#[test]
fn test_parse_alter_change_column_alias_type() {
let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a MediumText";
@@ -1571,9 +1642,11 @@ ALTER TABLE metrics REPARTITION
AlterTableOperation::ModifyColumnType {
column_name,
target_type,
json2_options,
} => {
assert_eq!("a", column_name.value);
assert_eq!(DataType::MediumText, *target_type);
assert!(json2_options.is_none());
}
_ => unreachable!(),
}
@@ -1602,9 +1675,11 @@ ALTER TABLE metrics REPARTITION
AlterTableOperation::ModifyColumnType {
column_name,
target_type,
json2_options,
} => {
assert_eq!("a", column_name.value);
assert!(matches!(target_type, DataType::Timestamp(Some(6), _)));
assert!(json2_options.is_none());
}
_ => unreachable!(),
}
+1
View File
@@ -24,6 +24,7 @@ use datafusion_common::ScalarValue;
use datatypes::arrow::datatypes::{DataType as ArrowDataType, IntervalUnit};
use datatypes::data_type::ConcreteDataType;
use itertools::Itertools;
pub(crate) use json::parse_json2_type_and_options;
pub use json::parse_json2_type_hint_path;
use snafu::{OptionExt, ResultExt, ensure};
use sqlparser::ast::{
+1 -1
View File
@@ -49,7 +49,7 @@ pub fn parse_json2_type_hint_path(path: &str) -> Result<Vec<String>> {
Ok(path)
}
pub(super) fn parse_json2_type_and_options(
pub(crate) fn parse_json2_type_and_options(
parser: &mut Parser<'_>,
) -> Result<Option<(DataType, Option<Json2Options>)>> {
let token = parser.peek_token();
+8 -2
View File
@@ -26,7 +26,7 @@ use sqlparser::ast::{ColumnDef, DataType, Expr, Ident, ObjectName, TableConstrai
use sqlparser_derive::{Visit, VisitMut};
use crate::statements::OptionMap;
use crate::statements::create::Partitions;
use crate::statements::create::{Json2Options, Partitions};
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
pub struct AlterTable {
@@ -86,6 +86,7 @@ pub enum AlterTableOperation {
ModifyColumnType {
column_name: Ident,
target_type: DataType,
json2_options: Option<Json2Options>,
},
/// `SET <table attrs key> = <table attr value>`
SetTableOptions {
@@ -256,8 +257,13 @@ impl Display for AlterTableOperation {
AlterTableOperation::ModifyColumnType {
column_name,
target_type,
json2_options,
} => {
write!(f, r#"MODIFY COLUMN {column_name} {target_type}"#)
write!(f, r#"MODIFY COLUMN {column_name} {target_type}"#)?;
if let Some(options) = json2_options {
write!(f, "{options}")?;
}
Ok(())
}
AlterTableOperation::SetTableOptions { options } => {
let kvs = options
+1 -1
View File
@@ -141,7 +141,7 @@ pub struct ColumnExtensions {
}
/// JSON2-specific options represented in the SQL AST.
#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Default, Serialize)]
pub struct Json2Options {
/// Maximum number of unhinted JSON2 paths expanded into Arrow fields.
pub(crate) max_auto_expanded_paths: Option<u32>,
@@ -0,0 +1,51 @@
CREATE TABLE application_logs (
ts TIMESTAMP TIME INDEX,
attrs JSON2
) WITH (
'append_mode' = 'true'
);
Affected Rows: 0
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2;
Error: 1001(Unsupported), Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 ();
Error: 1001(Unsupported), Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
max_auto_expanded_paths = 2000
);
Error: 1001(Unsupported), Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
trace_id STRING,
user.id STRING NOT NULL,
user.name STRING DEFAULT 'anonymous',
request_id STRING INVERTED INDEX
);
Error: 1001(Unsupported), Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
max_auto_expanded_paths = 2000,
trace_id STRING,
user.id STRING NOT NULL,
user.name STRING DEFAULT 'anonymous',
request_id STRING INVERTED INDEX
);
Error: 1001(Unsupported), Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type
DROP TABLE application_logs;
Affected Rows: 0
@@ -0,0 +1,36 @@
CREATE TABLE application_logs (
ts TIMESTAMP TIME INDEX,
attrs JSON2
) WITH (
'append_mode' = 'true'
);
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2;
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 ();
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
max_auto_expanded_paths = 2000
);
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
trace_id STRING,
user.id STRING NOT NULL,
user.name STRING DEFAULT 'anonymous',
request_id STRING INVERTED INDEX
);
ALTER TABLE application_logs
MODIFY COLUMN attrs JSON2 (
max_auto_expanded_paths = 2000,
trace_id STRING,
user.id STRING NOT NULL,
user.name STRING DEFAULT 'anonymous',
request_id STRING INVERTED INDEX
);
DROP TABLE application_logs;