mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-09-12 16:32:16 +00:00
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:
@@ -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#"
|
||||
|
||||
@@ -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!(),
|
||||
}
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user