mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-25 08:24:05 +00:00
Support for DuckDB Union datatype (#1322)
This commit is contained in:
parent
f5ccef6ea9
commit
f9ab8dcc27
4 changed files with 151 additions and 1 deletions
|
@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "visitor")]
|
#[cfg(feature = "visitor")]
|
||||||
use sqlparser_derive::{Visit, VisitMut};
|
use sqlparser_derive::{Visit, VisitMut};
|
||||||
|
|
||||||
use crate::ast::{display_comma_separated, ObjectName, StructField};
|
use crate::ast::{display_comma_separated, ObjectName, StructField, UnionField};
|
||||||
|
|
||||||
use super::{value::escape_single_quote_string, ColumnDef};
|
use super::{value::escape_single_quote_string, ColumnDef};
|
||||||
|
|
||||||
|
@ -303,6 +303,10 @@ pub enum DataType {
|
||||||
/// [hive]: https://docs.cloudera.com/cdw-runtime/cloud/impala-sql-reference/topics/impala-struct.html
|
/// [hive]: https://docs.cloudera.com/cdw-runtime/cloud/impala-sql-reference/topics/impala-struct.html
|
||||||
/// [bigquery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
|
/// [bigquery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
|
||||||
Struct(Vec<StructField>),
|
Struct(Vec<StructField>),
|
||||||
|
/// Union
|
||||||
|
///
|
||||||
|
/// [duckdb]: https://duckdb.org/docs/sql/data_types/union.html
|
||||||
|
Union(Vec<UnionField>),
|
||||||
/// Nullable - special marker NULL represents in ClickHouse as a data type.
|
/// Nullable - special marker NULL represents in ClickHouse as a data type.
|
||||||
///
|
///
|
||||||
/// [clickhouse]: https://clickhouse.com/docs/en/sql-reference/data-types/nullable
|
/// [clickhouse]: https://clickhouse.com/docs/en/sql-reference/data-types/nullable
|
||||||
|
@ -516,6 +520,9 @@ impl fmt::Display for DataType {
|
||||||
write!(f, "STRUCT")
|
write!(f, "STRUCT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType::Union(fields) => {
|
||||||
|
write!(f, "UNION({})", display_comma_separated(fields))
|
||||||
|
}
|
||||||
// ClickHouse
|
// ClickHouse
|
||||||
DataType::Nullable(data_type) => {
|
DataType::Nullable(data_type) => {
|
||||||
write!(f, "Nullable({})", data_type)
|
write!(f, "Nullable({})", data_type)
|
||||||
|
|
|
@ -294,6 +294,23 @@ impl fmt::Display for StructField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A field definition within a union
|
||||||
|
///
|
||||||
|
/// [duckdb]: https://duckdb.org/docs/sql/data_types/union.html
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct UnionField {
|
||||||
|
pub field_name: Ident,
|
||||||
|
pub field_type: DataType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UnionField {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} {}", self.field_name, self.field_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A dictionary field within a dictionary.
|
/// A dictionary field within a dictionary.
|
||||||
///
|
///
|
||||||
/// [duckdb]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
|
/// [duckdb]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
|
||||||
|
|
|
@ -2246,6 +2246,32 @@ impl<'a> Parser<'a> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DuckDB specific: Parse a Union type definition as a sequence of field-value pairs.
|
||||||
|
///
|
||||||
|
/// Syntax:
|
||||||
|
///
|
||||||
|
/// ```sql
|
||||||
|
/// UNION(field_name field_type[,...])
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [1]: https://duckdb.org/docs/sql/data_types/union.html
|
||||||
|
fn parse_union_type_def(&mut self) -> Result<Vec<UnionField>, ParserError> {
|
||||||
|
self.expect_keyword(Keyword::UNION)?;
|
||||||
|
|
||||||
|
self.expect_token(&Token::LParen)?;
|
||||||
|
|
||||||
|
let fields = self.parse_comma_separated(|p| {
|
||||||
|
Ok(UnionField {
|
||||||
|
field_name: p.parse_identifier(false)?,
|
||||||
|
field_type: p.parse_data_type()?,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.expect_token(&Token::RParen)?;
|
||||||
|
|
||||||
|
Ok(fields)
|
||||||
|
}
|
||||||
|
|
||||||
/// DuckDB specific: Parse a duckdb dictionary [1]
|
/// DuckDB specific: Parse a duckdb dictionary [1]
|
||||||
///
|
///
|
||||||
/// Syntax:
|
/// Syntax:
|
||||||
|
@ -7136,6 +7162,11 @@ impl<'a> Parser<'a> {
|
||||||
trailing_bracket = _trailing_bracket;
|
trailing_bracket = _trailing_bracket;
|
||||||
Ok(DataType::Struct(field_defs))
|
Ok(DataType::Struct(field_defs))
|
||||||
}
|
}
|
||||||
|
Keyword::UNION if dialect_of!(self is DuckDbDialect | GenericDialect) => {
|
||||||
|
self.prev_token();
|
||||||
|
let fields = self.parse_union_type_def()?;
|
||||||
|
Ok(DataType::Union(fields))
|
||||||
|
}
|
||||||
Keyword::NULLABLE if dialect_of!(self is ClickHouseDialect | GenericDialect) => {
|
Keyword::NULLABLE if dialect_of!(self is ClickHouseDialect | GenericDialect) => {
|
||||||
Ok(self.parse_sub_type(DataType::Nullable)?)
|
Ok(self.parse_sub_type(DataType::Nullable)?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -545,3 +545,98 @@ fn test_array_index() {
|
||||||
expr
|
expr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_duckdb_union_datatype() {
|
||||||
|
let sql = "CREATE TABLE tbl1 (one UNION(a INT), two UNION(a INT, b INT), nested UNION(a UNION(b INT)))";
|
||||||
|
let stmt = duckdb_and_generic().verified_stmt(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Statement::CreateTable(CreateTable {
|
||||||
|
or_replace: Default::default(),
|
||||||
|
temporary: Default::default(),
|
||||||
|
external: Default::default(),
|
||||||
|
global: Default::default(),
|
||||||
|
if_not_exists: Default::default(),
|
||||||
|
transient: Default::default(),
|
||||||
|
volatile: Default::default(),
|
||||||
|
name: ObjectName(vec!["tbl1".into()]),
|
||||||
|
columns: vec![
|
||||||
|
ColumnDef {
|
||||||
|
name: "one".into(),
|
||||||
|
data_type: DataType::Union(vec![UnionField {
|
||||||
|
field_name: "a".into(),
|
||||||
|
field_type: DataType::Int(None)
|
||||||
|
}]),
|
||||||
|
collation: Default::default(),
|
||||||
|
options: Default::default()
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: "two".into(),
|
||||||
|
data_type: DataType::Union(vec![
|
||||||
|
UnionField {
|
||||||
|
field_name: "a".into(),
|
||||||
|
field_type: DataType::Int(None)
|
||||||
|
},
|
||||||
|
UnionField {
|
||||||
|
field_name: "b".into(),
|
||||||
|
field_type: DataType::Int(None)
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
collation: Default::default(),
|
||||||
|
options: Default::default()
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: "nested".into(),
|
||||||
|
data_type: DataType::Union(vec![UnionField {
|
||||||
|
field_name: "a".into(),
|
||||||
|
field_type: DataType::Union(vec![UnionField {
|
||||||
|
field_name: "b".into(),
|
||||||
|
field_type: DataType::Int(None)
|
||||||
|
}])
|
||||||
|
}]),
|
||||||
|
collation: Default::default(),
|
||||||
|
options: Default::default()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
constraints: Default::default(),
|
||||||
|
hive_distribution: HiveDistributionStyle::NONE,
|
||||||
|
hive_formats: Some(HiveFormat {
|
||||||
|
row_format: Default::default(),
|
||||||
|
serde_properties: Default::default(),
|
||||||
|
storage: Default::default(),
|
||||||
|
location: Default::default()
|
||||||
|
}),
|
||||||
|
table_properties: Default::default(),
|
||||||
|
with_options: Default::default(),
|
||||||
|
file_format: Default::default(),
|
||||||
|
location: Default::default(),
|
||||||
|
query: Default::default(),
|
||||||
|
without_rowid: Default::default(),
|
||||||
|
like: Default::default(),
|
||||||
|
clone: Default::default(),
|
||||||
|
engine: Default::default(),
|
||||||
|
comment: Default::default(),
|
||||||
|
auto_increment_offset: Default::default(),
|
||||||
|
default_charset: Default::default(),
|
||||||
|
collation: Default::default(),
|
||||||
|
on_commit: Default::default(),
|
||||||
|
on_cluster: Default::default(),
|
||||||
|
primary_key: Default::default(),
|
||||||
|
order_by: Default::default(),
|
||||||
|
partition_by: Default::default(),
|
||||||
|
cluster_by: Default::default(),
|
||||||
|
options: Default::default(),
|
||||||
|
strict: Default::default(),
|
||||||
|
copy_grants: Default::default(),
|
||||||
|
enable_schema_evolution: Default::default(),
|
||||||
|
change_tracking: Default::default(),
|
||||||
|
data_retention_time_in_days: Default::default(),
|
||||||
|
max_data_extension_time_in_days: Default::default(),
|
||||||
|
default_ddl_collation: Default::default(),
|
||||||
|
with_aggregation_policy: Default::default(),
|
||||||
|
with_row_access_policy: Default::default(),
|
||||||
|
with_tags: Default::default()
|
||||||
|
}),
|
||||||
|
stmt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue