mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-12-23 11:12:51 +00:00
Complete PostgreSQL CREATE TYPE Support (#2094)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
parent
a00d5cd967
commit
78be8b178b
6 changed files with 889 additions and 55 deletions
313
src/ast/ddl.rs
313
src/ast/ddl.rs
|
|
@ -2023,21 +2023,44 @@ impl fmt::Display for DropBehavior {
|
|||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UserDefinedTypeRepresentation {
|
||||
/// Composite type: `CREATE TYPE name AS (attributes)`
|
||||
Composite {
|
||||
attributes: Vec<UserDefinedTypeCompositeAttributeDef>,
|
||||
},
|
||||
/// Enum type: `CREATE TYPE name AS ENUM (labels)`
|
||||
///
|
||||
/// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
Enum { labels: Vec<Ident> },
|
||||
/// Range type: `CREATE TYPE name AS RANGE (options)`
|
||||
///
|
||||
/// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
Range {
|
||||
options: Vec<UserDefinedTypeRangeOption>,
|
||||
},
|
||||
/// Base type (SQL definition): `CREATE TYPE name (options)`
|
||||
///
|
||||
/// Note the lack of `AS` keyword
|
||||
///
|
||||
/// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
SqlDefinition {
|
||||
options: Vec<UserDefinedTypeSqlDefinitionOption>,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for UserDefinedTypeRepresentation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UserDefinedTypeRepresentation::Composite { attributes } => {
|
||||
write!(f, "({})", display_comma_separated(attributes))
|
||||
Self::Composite { attributes } => {
|
||||
write!(f, "AS ({})", display_comma_separated(attributes))
|
||||
}
|
||||
UserDefinedTypeRepresentation::Enum { labels } => {
|
||||
write!(f, "ENUM ({})", display_comma_separated(labels))
|
||||
Self::Enum { labels } => {
|
||||
write!(f, "AS ENUM ({})", display_comma_separated(labels))
|
||||
}
|
||||
Self::Range { options } => {
|
||||
write!(f, "AS RANGE ({})", display_comma_separated(options))
|
||||
}
|
||||
Self::SqlDefinition { options } => {
|
||||
write!(f, "({})", display_comma_separated(options))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2063,6 +2086,288 @@ impl fmt::Display for UserDefinedTypeCompositeAttributeDef {
|
|||
}
|
||||
}
|
||||
|
||||
/// Internal length specification for PostgreSQL user-defined base types.
|
||||
///
|
||||
/// Specifies the internal length in bytes of the new type's internal representation.
|
||||
/// The default assumption is that it is variable-length.
|
||||
///
|
||||
/// # PostgreSQL Documentation
|
||||
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
///
|
||||
/// # Examples
|
||||
/// ```sql
|
||||
/// CREATE TYPE mytype (
|
||||
/// INPUT = in_func,
|
||||
/// OUTPUT = out_func,
|
||||
/// INTERNALLENGTH = 16 -- Fixed 16-byte length
|
||||
/// );
|
||||
///
|
||||
/// CREATE TYPE mytype2 (
|
||||
/// INPUT = in_func,
|
||||
/// OUTPUT = out_func,
|
||||
/// INTERNALLENGTH = VARIABLE -- Variable length
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UserDefinedTypeInternalLength {
|
||||
/// Fixed internal length: `INTERNALLENGTH = <number>`
|
||||
Fixed(u64),
|
||||
/// Variable internal length: `INTERNALLENGTH = VARIABLE`
|
||||
Variable,
|
||||
}
|
||||
|
||||
impl fmt::Display for UserDefinedTypeInternalLength {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UserDefinedTypeInternalLength::Fixed(n) => write!(f, "{}", n),
|
||||
UserDefinedTypeInternalLength::Variable => write!(f, "VARIABLE"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alignment specification for PostgreSQL user-defined base types.
|
||||
///
|
||||
/// Specifies the storage alignment requirement for values of the data type.
|
||||
/// The allowed values equate to alignment on 1, 2, 4, or 8 byte boundaries.
|
||||
/// Note that variable-length types must have an alignment of at least 4, since
|
||||
/// they necessarily contain an int4 as their first component.
|
||||
///
|
||||
/// # PostgreSQL Documentation
|
||||
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
///
|
||||
/// # Examples
|
||||
/// ```sql
|
||||
/// CREATE TYPE mytype (
|
||||
/// INPUT = in_func,
|
||||
/// OUTPUT = out_func,
|
||||
/// ALIGNMENT = int4 -- 4-byte alignment
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum Alignment {
|
||||
/// Single-byte alignment: `ALIGNMENT = char`
|
||||
Char,
|
||||
/// 2-byte alignment: `ALIGNMENT = int2`
|
||||
Int2,
|
||||
/// 4-byte alignment: `ALIGNMENT = int4`
|
||||
Int4,
|
||||
/// 8-byte alignment: `ALIGNMENT = double`
|
||||
Double,
|
||||
}
|
||||
|
||||
impl fmt::Display for Alignment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Alignment::Char => write!(f, "char"),
|
||||
Alignment::Int2 => write!(f, "int2"),
|
||||
Alignment::Int4 => write!(f, "int4"),
|
||||
Alignment::Double => write!(f, "double"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage specification for PostgreSQL user-defined base types.
|
||||
///
|
||||
/// Specifies the storage strategy for values of the data type:
|
||||
/// - `plain`: Prevents compression and out-of-line storage (for fixed-length types)
|
||||
/// - `external`: Allows out-of-line storage but not compression
|
||||
/// - `extended`: Allows both compression and out-of-line storage (default for most types)
|
||||
/// - `main`: Allows compression but discourages out-of-line storage
|
||||
///
|
||||
/// # PostgreSQL Documentation
|
||||
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
///
|
||||
/// # Examples
|
||||
/// ```sql
|
||||
/// CREATE TYPE mytype (
|
||||
/// INPUT = in_func,
|
||||
/// OUTPUT = out_func,
|
||||
/// STORAGE = plain
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UserDefinedTypeStorage {
|
||||
/// No compression or out-of-line storage: `STORAGE = plain`
|
||||
Plain,
|
||||
/// Out-of-line storage allowed, no compression: `STORAGE = external`
|
||||
External,
|
||||
/// Both compression and out-of-line storage allowed: `STORAGE = extended`
|
||||
Extended,
|
||||
/// Compression allowed, out-of-line discouraged: `STORAGE = main`
|
||||
Main,
|
||||
}
|
||||
|
||||
impl fmt::Display for UserDefinedTypeStorage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UserDefinedTypeStorage::Plain => write!(f, "plain"),
|
||||
UserDefinedTypeStorage::External => write!(f, "external"),
|
||||
UserDefinedTypeStorage::Extended => write!(f, "extended"),
|
||||
UserDefinedTypeStorage::Main => write!(f, "main"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for PostgreSQL `CREATE TYPE ... AS RANGE` statement.
|
||||
///
|
||||
/// Range types are data types representing a range of values of some element type
|
||||
/// (called the range's subtype). These options configure the behavior of the range type.
|
||||
///
|
||||
/// # PostgreSQL Documentation
|
||||
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
///
|
||||
/// # Examples
|
||||
/// ```sql
|
||||
/// CREATE TYPE int4range AS RANGE (
|
||||
/// SUBTYPE = int4,
|
||||
/// SUBTYPE_OPCLASS = int4_ops,
|
||||
/// CANONICAL = int4range_canonical,
|
||||
/// SUBTYPE_DIFF = int4range_subdiff
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UserDefinedTypeRangeOption {
|
||||
/// The element type that the range type will represent: `SUBTYPE = subtype`
|
||||
Subtype(DataType),
|
||||
/// The operator class for the subtype: `SUBTYPE_OPCLASS = subtype_operator_class`
|
||||
SubtypeOpClass(ObjectName),
|
||||
/// Collation to use for ordering the subtype: `COLLATION = collation`
|
||||
Collation(ObjectName),
|
||||
/// Function to convert range values to canonical form: `CANONICAL = canonical_function`
|
||||
Canonical(ObjectName),
|
||||
/// Function to compute the difference between two subtype values: `SUBTYPE_DIFF = subtype_diff_function`
|
||||
SubtypeDiff(ObjectName),
|
||||
/// Name of the corresponding multirange type: `MULTIRANGE_TYPE_NAME = multirange_type_name`
|
||||
MultirangeTypeName(ObjectName),
|
||||
}
|
||||
|
||||
impl fmt::Display for UserDefinedTypeRangeOption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UserDefinedTypeRangeOption::Subtype(dt) => write!(f, "SUBTYPE = {}", dt),
|
||||
UserDefinedTypeRangeOption::SubtypeOpClass(name) => {
|
||||
write!(f, "SUBTYPE_OPCLASS = {}", name)
|
||||
}
|
||||
UserDefinedTypeRangeOption::Collation(name) => write!(f, "COLLATION = {}", name),
|
||||
UserDefinedTypeRangeOption::Canonical(name) => write!(f, "CANONICAL = {}", name),
|
||||
UserDefinedTypeRangeOption::SubtypeDiff(name) => write!(f, "SUBTYPE_DIFF = {}", name),
|
||||
UserDefinedTypeRangeOption::MultirangeTypeName(name) => {
|
||||
write!(f, "MULTIRANGE_TYPE_NAME = {}", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for PostgreSQL `CREATE TYPE ... (<options>)` statement (base type definition).
|
||||
///
|
||||
/// Base types are the lowest-level data types in PostgreSQL. To define a new base type,
|
||||
/// you must specify functions that convert it to and from text representation, and optionally
|
||||
/// binary representation and other properties.
|
||||
///
|
||||
/// Note: This syntax uses parentheses directly after the type name, without the `AS` keyword.
|
||||
///
|
||||
/// # PostgreSQL Documentation
|
||||
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
|
||||
///
|
||||
/// # Examples
|
||||
/// ```sql
|
||||
/// CREATE TYPE complex (
|
||||
/// INPUT = complex_in,
|
||||
/// OUTPUT = complex_out,
|
||||
/// INTERNALLENGTH = 16,
|
||||
/// ALIGNMENT = double
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum UserDefinedTypeSqlDefinitionOption {
|
||||
/// Function to convert from external text representation to internal: `INPUT = input_function`
|
||||
Input(ObjectName),
|
||||
/// Function to convert from internal to external text representation: `OUTPUT = output_function`
|
||||
Output(ObjectName),
|
||||
/// Function to convert from external binary representation to internal: `RECEIVE = receive_function`
|
||||
Receive(ObjectName),
|
||||
/// Function to convert from internal to external binary representation: `SEND = send_function`
|
||||
Send(ObjectName),
|
||||
/// Function to convert type modifiers from text array to internal form: `TYPMOD_IN = type_modifier_input_function`
|
||||
TypmodIn(ObjectName),
|
||||
/// Function to convert type modifiers from internal to text form: `TYPMOD_OUT = type_modifier_output_function`
|
||||
TypmodOut(ObjectName),
|
||||
/// Function to compute statistics for the data type: `ANALYZE = analyze_function`
|
||||
Analyze(ObjectName),
|
||||
/// Function to handle subscripting operations: `SUBSCRIPT = subscript_function`
|
||||
Subscript(ObjectName),
|
||||
/// Internal storage size in bytes, or VARIABLE for variable-length: `INTERNALLENGTH = { internallength | VARIABLE }`
|
||||
InternalLength(UserDefinedTypeInternalLength),
|
||||
/// Indicates values are passed by value rather than by reference: `PASSEDBYVALUE`
|
||||
PassedByValue,
|
||||
/// Storage alignment requirement (1, 2, 4, or 8 bytes): `ALIGNMENT = alignment`
|
||||
Alignment(Alignment),
|
||||
/// Storage strategy for varlena types: `STORAGE = storage`
|
||||
Storage(UserDefinedTypeStorage),
|
||||
/// Copy properties from an existing type: `LIKE = like_type`
|
||||
Like(ObjectName),
|
||||
/// Type category for implicit casting rules (single char): `CATEGORY = category`
|
||||
Category(char),
|
||||
/// Whether this type is preferred within its category: `PREFERRED = preferred`
|
||||
Preferred(bool),
|
||||
/// Default value for the type: `DEFAULT = default`
|
||||
Default(Expr),
|
||||
/// Element type for array types: `ELEMENT = element`
|
||||
Element(DataType),
|
||||
/// Delimiter character for array value display: `DELIMITER = delimiter`
|
||||
Delimiter(String),
|
||||
/// Whether the type supports collation: `COLLATABLE = collatable`
|
||||
Collatable(bool),
|
||||
}
|
||||
|
||||
impl fmt::Display for UserDefinedTypeSqlDefinitionOption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UserDefinedTypeSqlDefinitionOption::Input(name) => write!(f, "INPUT = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::Output(name) => write!(f, "OUTPUT = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::Receive(name) => write!(f, "RECEIVE = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::Send(name) => write!(f, "SEND = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::TypmodIn(name) => write!(f, "TYPMOD_IN = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::TypmodOut(name) => {
|
||||
write!(f, "TYPMOD_OUT = {}", name)
|
||||
}
|
||||
UserDefinedTypeSqlDefinitionOption::Analyze(name) => write!(f, "ANALYZE = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::Subscript(name) => {
|
||||
write!(f, "SUBSCRIPT = {}", name)
|
||||
}
|
||||
UserDefinedTypeSqlDefinitionOption::InternalLength(len) => {
|
||||
write!(f, "INTERNALLENGTH = {}", len)
|
||||
}
|
||||
UserDefinedTypeSqlDefinitionOption::PassedByValue => write!(f, "PASSEDBYVALUE"),
|
||||
UserDefinedTypeSqlDefinitionOption::Alignment(align) => {
|
||||
write!(f, "ALIGNMENT = {}", align)
|
||||
}
|
||||
UserDefinedTypeSqlDefinitionOption::Storage(storage) => {
|
||||
write!(f, "STORAGE = {}", storage)
|
||||
}
|
||||
UserDefinedTypeSqlDefinitionOption::Like(name) => write!(f, "LIKE = {}", name),
|
||||
UserDefinedTypeSqlDefinitionOption::Category(c) => write!(f, "CATEGORY = '{}'", c),
|
||||
UserDefinedTypeSqlDefinitionOption::Preferred(b) => write!(f, "PREFERRED = {}", b),
|
||||
UserDefinedTypeSqlDefinitionOption::Default(expr) => write!(f, "DEFAULT = {}", expr),
|
||||
UserDefinedTypeSqlDefinitionOption::Element(dt) => write!(f, "ELEMENT = {}", dt),
|
||||
UserDefinedTypeSqlDefinitionOption::Delimiter(s) => {
|
||||
write!(f, "DELIMITER = '{}'", escape_single_quote_string(s))
|
||||
}
|
||||
UserDefinedTypeSqlDefinitionOption::Collatable(b) => write!(f, "COLLATABLE = {}", b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PARTITION statement used in ALTER TABLE et al. such as in Hive and ClickHouse SQL.
|
||||
/// For example, ClickHouse's OPTIMIZE TABLE supports syntax like PARTITION ID 'partition_id' and PARTITION expr.
|
||||
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
|
||||
|
|
|
|||
|
|
@ -59,19 +59,21 @@ pub use self::dcl::{
|
|||
AlterRoleOperation, CreateRole, ResetConfig, RoleOption, SecondaryRoles, SetConfigValue, Use,
|
||||
};
|
||||
pub use self::ddl::{
|
||||
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
|
||||
AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock,
|
||||
AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
|
||||
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
|
||||
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
|
||||
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateExtension, CreateFunction,
|
||||
CreateIndex, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
|
||||
DropBehavior, DropExtension, DropFunction, DropTrigger, GeneratedAs, GeneratedExpressionMode,
|
||||
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
|
||||
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
|
||||
NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction, RenameTableNameKind,
|
||||
ReplicaIdentity, TagsColumnOption, TriggerObjectKind, Truncate,
|
||||
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
|
||||
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation,
|
||||
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
|
||||
AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
|
||||
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
|
||||
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
|
||||
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
|
||||
CreateExtension, CreateFunction, CreateIndex, CreateTable, CreateTrigger, CreateView,
|
||||
Deduplicate, DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropTrigger,
|
||||
GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
|
||||
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
|
||||
IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, Owner, Partition,
|
||||
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
|
||||
TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
|
||||
UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
|
||||
UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
|
||||
};
|
||||
pub use self::dml::{Delete, Insert, Update};
|
||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||
|
|
@ -2787,7 +2789,7 @@ impl fmt::Display for Declare {
|
|||
}
|
||||
|
||||
/// Sql options of a `CREATE TABLE` statement.
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum CreateTableOptions {
|
||||
|
|
@ -4107,7 +4109,7 @@ pub enum Statement {
|
|||
/// ```
|
||||
CreateType {
|
||||
name: ObjectName,
|
||||
representation: UserDefinedTypeRepresentation,
|
||||
representation: Option<UserDefinedTypeRepresentation>,
|
||||
},
|
||||
/// ```sql
|
||||
/// PRAGMA <schema-name>.<pragma-name> = <pragma-value>
|
||||
|
|
@ -5657,7 +5659,11 @@ impl fmt::Display for Statement {
|
|||
name,
|
||||
representation,
|
||||
} => {
|
||||
write!(f, "CREATE TYPE {name} AS {representation}")
|
||||
write!(f, "CREATE TYPE {name}")?;
|
||||
if let Some(repr) = representation {
|
||||
write!(f, " {repr}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Statement::Pragma { name, value, is_eq } => {
|
||||
write!(f, "PRAGMA {name}")?;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ define_keywords!(
|
|||
ALERT,
|
||||
ALGORITHM,
|
||||
ALIAS,
|
||||
ALIGNMENT,
|
||||
ALL,
|
||||
ALLOCATE,
|
||||
ALLOWOVERWRITE,
|
||||
|
|
@ -166,6 +167,7 @@ define_keywords!(
|
|||
CACHE,
|
||||
CALL,
|
||||
CALLED,
|
||||
CANONICAL,
|
||||
CARDINALITY,
|
||||
CASCADE,
|
||||
CASCADED,
|
||||
|
|
@ -177,6 +179,7 @@ define_keywords!(
|
|||
CATALOG_SYNC_NAMESPACE_FLATTEN_DELIMITER,
|
||||
CATALOG_SYNC_NAMESPACE_MODE,
|
||||
CATCH,
|
||||
CATEGORY,
|
||||
CEIL,
|
||||
CEILING,
|
||||
CENTURY,
|
||||
|
|
@ -203,6 +206,7 @@ define_keywords!(
|
|||
CLUSTERED,
|
||||
CLUSTERING,
|
||||
COALESCE,
|
||||
COLLATABLE,
|
||||
COLLATE,
|
||||
COLLATION,
|
||||
COLLECT,
|
||||
|
|
@ -501,6 +505,7 @@ define_keywords!(
|
|||
INT8,
|
||||
INTEGER,
|
||||
INTEGRATION,
|
||||
INTERNALLENGTH,
|
||||
INTERPOLATE,
|
||||
INTERSECT,
|
||||
INTERSECTION,
|
||||
|
|
@ -563,6 +568,7 @@ define_keywords!(
|
|||
LS,
|
||||
LSEG,
|
||||
MACRO,
|
||||
MAIN,
|
||||
MANAGE,
|
||||
MANAGED,
|
||||
MANAGEDLOCATION,
|
||||
|
|
@ -615,6 +621,7 @@ define_keywords!(
|
|||
MONTH,
|
||||
MONTHS,
|
||||
MSCK,
|
||||
MULTIRANGE_TYPE_NAME,
|
||||
MULTISET,
|
||||
MUTATION,
|
||||
NAME,
|
||||
|
|
@ -718,6 +725,7 @@ define_keywords!(
|
|||
PARTITION,
|
||||
PARTITIONED,
|
||||
PARTITIONS,
|
||||
PASSEDBYVALUE,
|
||||
PASSING,
|
||||
PASSKEY,
|
||||
PASSWORD,
|
||||
|
|
@ -734,6 +742,7 @@ define_keywords!(
|
|||
PERSISTENT,
|
||||
PIVOT,
|
||||
PLACING,
|
||||
PLAIN,
|
||||
PLAN,
|
||||
PLANS,
|
||||
POINT,
|
||||
|
|
@ -748,6 +757,7 @@ define_keywords!(
|
|||
PRECEDES,
|
||||
PRECEDING,
|
||||
PRECISION,
|
||||
PREFERRED,
|
||||
PREPARE,
|
||||
PRESERVE,
|
||||
PRESET,
|
||||
|
|
@ -778,6 +788,7 @@ define_keywords!(
|
|||
READS,
|
||||
READ_ONLY,
|
||||
REAL,
|
||||
RECEIVE,
|
||||
RECLUSTER,
|
||||
RECURSIVE,
|
||||
REF,
|
||||
|
|
@ -868,6 +879,7 @@ define_keywords!(
|
|||
SELECT,
|
||||
SEMANTIC_VIEW,
|
||||
SEMI,
|
||||
SEND,
|
||||
SENSITIVE,
|
||||
SEPARATOR,
|
||||
SEQUENCE,
|
||||
|
|
@ -936,9 +948,13 @@ define_keywords!(
|
|||
STRING,
|
||||
STRUCT,
|
||||
SUBMULTISET,
|
||||
SUBSCRIPT,
|
||||
SUBSTR,
|
||||
SUBSTRING,
|
||||
SUBSTRING_REGEX,
|
||||
SUBTYPE,
|
||||
SUBTYPE_DIFF,
|
||||
SUBTYPE_OPCLASS,
|
||||
SUCCEEDS,
|
||||
SUM,
|
||||
SUPER,
|
||||
|
|
@ -1008,6 +1024,8 @@ define_keywords!(
|
|||
TSVECTOR,
|
||||
TUPLE,
|
||||
TYPE,
|
||||
TYPMOD_IN,
|
||||
TYPMOD_OUT,
|
||||
UBIGINT,
|
||||
UESCAPE,
|
||||
UHUGEINT,
|
||||
|
|
@ -1057,6 +1075,7 @@ define_keywords!(
|
|||
VARBINARY,
|
||||
VARBIT,
|
||||
VARCHAR,
|
||||
VARIABLE,
|
||||
VARIABLES,
|
||||
VARYING,
|
||||
VAR_POP,
|
||||
|
|
|
|||
|
|
@ -17359,20 +17359,59 @@ impl<'a> Parser<'a> {
|
|||
|
||||
pub fn parse_create_type(&mut self) -> Result<Statement, ParserError> {
|
||||
let name = self.parse_object_name(false)?;
|
||||
self.expect_keyword_is(Keyword::AS)?;
|
||||
|
||||
if self.parse_keyword(Keyword::ENUM) {
|
||||
return self.parse_create_type_enum(name);
|
||||
}
|
||||
// Check if we have AS keyword
|
||||
let has_as = self.parse_keyword(Keyword::AS);
|
||||
|
||||
let mut attributes = vec![];
|
||||
if !self.consume_token(&Token::LParen) || self.consume_token(&Token::RParen) {
|
||||
if !has_as {
|
||||
// Two cases: CREATE TYPE name; or CREATE TYPE name (options);
|
||||
if self.consume_token(&Token::LParen) {
|
||||
// CREATE TYPE name (options) - SQL definition without AS
|
||||
let options = self.parse_create_type_sql_definition_options()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
return Ok(Statement::CreateType {
|
||||
name,
|
||||
representation: Some(UserDefinedTypeRepresentation::SqlDefinition { options }),
|
||||
});
|
||||
}
|
||||
|
||||
// CREATE TYPE name; - no representation
|
||||
return Ok(Statement::CreateType {
|
||||
name,
|
||||
representation: UserDefinedTypeRepresentation::Composite { attributes },
|
||||
representation: None,
|
||||
});
|
||||
}
|
||||
|
||||
// We have AS keyword
|
||||
if self.parse_keyword(Keyword::ENUM) {
|
||||
// CREATE TYPE name AS ENUM (labels)
|
||||
self.parse_create_type_enum(name)
|
||||
} else if self.parse_keyword(Keyword::RANGE) {
|
||||
// CREATE TYPE name AS RANGE (options)
|
||||
self.parse_create_type_range(name)
|
||||
} else if self.consume_token(&Token::LParen) {
|
||||
// CREATE TYPE name AS (attributes) - Composite
|
||||
self.parse_create_type_composite(name)
|
||||
} else {
|
||||
self.expected("ENUM, RANGE, or '(' after AS", self.peek_token())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse remainder of `CREATE TYPE AS (attributes)` statement (composite type)
|
||||
///
|
||||
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtype.html)
|
||||
fn parse_create_type_composite(&mut self, name: ObjectName) -> Result<Statement, ParserError> {
|
||||
if self.consume_token(&Token::RParen) {
|
||||
// Empty composite type
|
||||
return Ok(Statement::CreateType {
|
||||
name,
|
||||
representation: Some(UserDefinedTypeRepresentation::Composite {
|
||||
attributes: vec![],
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
let mut attributes = vec![];
|
||||
loop {
|
||||
let attr_name = self.parse_identifier()?;
|
||||
let attr_data_type = self.parse_data_type()?;
|
||||
|
|
@ -17386,18 +17425,16 @@ impl<'a> Parser<'a> {
|
|||
data_type: attr_data_type,
|
||||
collation: attr_collation,
|
||||
});
|
||||
let comma = self.consume_token(&Token::Comma);
|
||||
if self.consume_token(&Token::RParen) {
|
||||
// allow a trailing comma
|
||||
|
||||
if !self.consume_token(&Token::Comma) {
|
||||
break;
|
||||
} else if !comma {
|
||||
return self.expected("',' or ')' after attribute definition", self.peek_token());
|
||||
}
|
||||
}
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
Ok(Statement::CreateType {
|
||||
name,
|
||||
representation: UserDefinedTypeRepresentation::Composite { attributes },
|
||||
representation: Some(UserDefinedTypeRepresentation::Composite { attributes }),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -17411,10 +17448,258 @@ impl<'a> Parser<'a> {
|
|||
|
||||
Ok(Statement::CreateType {
|
||||
name,
|
||||
representation: UserDefinedTypeRepresentation::Enum { labels },
|
||||
representation: Some(UserDefinedTypeRepresentation::Enum { labels }),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse remainder of `CREATE TYPE AS RANGE` statement
|
||||
///
|
||||
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtype.html)
|
||||
fn parse_create_type_range(&mut self, name: ObjectName) -> Result<Statement, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let options = self.parse_comma_separated0(|p| p.parse_range_option(), Token::RParen)?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
Ok(Statement::CreateType {
|
||||
name,
|
||||
representation: Some(UserDefinedTypeRepresentation::Range { options }),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a single range option for a `CREATE TYPE AS RANGE` statement
|
||||
fn parse_range_option(&mut self) -> Result<UserDefinedTypeRangeOption, ParserError> {
|
||||
let keyword = self.parse_one_of_keywords(&[
|
||||
Keyword::SUBTYPE,
|
||||
Keyword::SUBTYPE_OPCLASS,
|
||||
Keyword::COLLATION,
|
||||
Keyword::CANONICAL,
|
||||
Keyword::SUBTYPE_DIFF,
|
||||
Keyword::MULTIRANGE_TYPE_NAME,
|
||||
]);
|
||||
|
||||
match keyword {
|
||||
Some(Keyword::SUBTYPE) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let data_type = self.parse_data_type()?;
|
||||
Ok(UserDefinedTypeRangeOption::Subtype(data_type))
|
||||
}
|
||||
Some(Keyword::SUBTYPE_OPCLASS) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeRangeOption::SubtypeOpClass(name))
|
||||
}
|
||||
Some(Keyword::COLLATION) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeRangeOption::Collation(name))
|
||||
}
|
||||
Some(Keyword::CANONICAL) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeRangeOption::Canonical(name))
|
||||
}
|
||||
Some(Keyword::SUBTYPE_DIFF) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeRangeOption::SubtypeDiff(name))
|
||||
}
|
||||
Some(Keyword::MULTIRANGE_TYPE_NAME) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeRangeOption::MultirangeTypeName(name))
|
||||
}
|
||||
_ => self.expected("range option keyword", self.peek_token()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse SQL definition options for CREATE TYPE (options)
|
||||
fn parse_create_type_sql_definition_options(
|
||||
&mut self,
|
||||
) -> Result<Vec<UserDefinedTypeSqlDefinitionOption>, ParserError> {
|
||||
self.parse_comma_separated0(|p| p.parse_sql_definition_option(), Token::RParen)
|
||||
}
|
||||
|
||||
/// Parse a single SQL definition option for CREATE TYPE (options)
|
||||
fn parse_sql_definition_option(
|
||||
&mut self,
|
||||
) -> Result<UserDefinedTypeSqlDefinitionOption, ParserError> {
|
||||
let keyword = self.parse_one_of_keywords(&[
|
||||
Keyword::INPUT,
|
||||
Keyword::OUTPUT,
|
||||
Keyword::RECEIVE,
|
||||
Keyword::SEND,
|
||||
Keyword::TYPMOD_IN,
|
||||
Keyword::TYPMOD_OUT,
|
||||
Keyword::ANALYZE,
|
||||
Keyword::SUBSCRIPT,
|
||||
Keyword::INTERNALLENGTH,
|
||||
Keyword::PASSEDBYVALUE,
|
||||
Keyword::ALIGNMENT,
|
||||
Keyword::STORAGE,
|
||||
Keyword::LIKE,
|
||||
Keyword::CATEGORY,
|
||||
Keyword::PREFERRED,
|
||||
Keyword::DEFAULT,
|
||||
Keyword::ELEMENT,
|
||||
Keyword::DELIMITER,
|
||||
Keyword::COLLATABLE,
|
||||
]);
|
||||
|
||||
match keyword {
|
||||
Some(Keyword::INPUT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Input(name))
|
||||
}
|
||||
Some(Keyword::OUTPUT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Output(name))
|
||||
}
|
||||
Some(Keyword::RECEIVE) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Receive(name))
|
||||
}
|
||||
Some(Keyword::SEND) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Send(name))
|
||||
}
|
||||
Some(Keyword::TYPMOD_IN) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::TypmodIn(name))
|
||||
}
|
||||
Some(Keyword::TYPMOD_OUT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::TypmodOut(name))
|
||||
}
|
||||
Some(Keyword::ANALYZE) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Analyze(name))
|
||||
}
|
||||
Some(Keyword::SUBSCRIPT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Subscript(name))
|
||||
}
|
||||
Some(Keyword::INTERNALLENGTH) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
if self.parse_keyword(Keyword::VARIABLE) {
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::InternalLength(
|
||||
UserDefinedTypeInternalLength::Variable,
|
||||
))
|
||||
} else {
|
||||
let value = self.parse_literal_uint()?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::InternalLength(
|
||||
UserDefinedTypeInternalLength::Fixed(value),
|
||||
))
|
||||
}
|
||||
}
|
||||
Some(Keyword::PASSEDBYVALUE) => Ok(UserDefinedTypeSqlDefinitionOption::PassedByValue),
|
||||
Some(Keyword::ALIGNMENT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let align_keyword = self.parse_one_of_keywords(&[
|
||||
Keyword::CHAR,
|
||||
Keyword::INT2,
|
||||
Keyword::INT4,
|
||||
Keyword::DOUBLE,
|
||||
]);
|
||||
match align_keyword {
|
||||
Some(Keyword::CHAR) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment(
|
||||
Alignment::Char,
|
||||
)),
|
||||
Some(Keyword::INT2) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment(
|
||||
Alignment::Int2,
|
||||
)),
|
||||
Some(Keyword::INT4) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment(
|
||||
Alignment::Int4,
|
||||
)),
|
||||
Some(Keyword::DOUBLE) => Ok(UserDefinedTypeSqlDefinitionOption::Alignment(
|
||||
Alignment::Double,
|
||||
)),
|
||||
_ => self.expected(
|
||||
"alignment value (char, int2, int4, or double)",
|
||||
self.peek_token(),
|
||||
),
|
||||
}
|
||||
}
|
||||
Some(Keyword::STORAGE) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let storage_keyword = self.parse_one_of_keywords(&[
|
||||
Keyword::PLAIN,
|
||||
Keyword::EXTERNAL,
|
||||
Keyword::EXTENDED,
|
||||
Keyword::MAIN,
|
||||
]);
|
||||
match storage_keyword {
|
||||
Some(Keyword::PLAIN) => Ok(UserDefinedTypeSqlDefinitionOption::Storage(
|
||||
UserDefinedTypeStorage::Plain,
|
||||
)),
|
||||
Some(Keyword::EXTERNAL) => Ok(UserDefinedTypeSqlDefinitionOption::Storage(
|
||||
UserDefinedTypeStorage::External,
|
||||
)),
|
||||
Some(Keyword::EXTENDED) => Ok(UserDefinedTypeSqlDefinitionOption::Storage(
|
||||
UserDefinedTypeStorage::Extended,
|
||||
)),
|
||||
Some(Keyword::MAIN) => Ok(UserDefinedTypeSqlDefinitionOption::Storage(
|
||||
UserDefinedTypeStorage::Main,
|
||||
)),
|
||||
_ => self.expected(
|
||||
"storage value (plain, external, extended, or main)",
|
||||
self.peek_token(),
|
||||
),
|
||||
}
|
||||
}
|
||||
Some(Keyword::LIKE) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let name = self.parse_object_name(false)?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Like(name))
|
||||
}
|
||||
Some(Keyword::CATEGORY) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let category_str = self.parse_literal_string()?;
|
||||
let category_char = category_str.chars().next().ok_or_else(|| {
|
||||
ParserError::ParserError(
|
||||
"CATEGORY value must be a single character".to_string(),
|
||||
)
|
||||
})?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Category(category_char))
|
||||
}
|
||||
Some(Keyword::PREFERRED) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let value =
|
||||
self.parse_keyword(Keyword::TRUE) || !self.parse_keyword(Keyword::FALSE);
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Preferred(value))
|
||||
}
|
||||
Some(Keyword::DEFAULT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let expr = self.parse_expr()?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Default(expr))
|
||||
}
|
||||
Some(Keyword::ELEMENT) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let data_type = self.parse_data_type()?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Element(data_type))
|
||||
}
|
||||
Some(Keyword::DELIMITER) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let delimiter = self.parse_literal_string()?;
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Delimiter(delimiter))
|
||||
}
|
||||
Some(Keyword::COLLATABLE) => {
|
||||
self.expect_token(&Token::Eq)?;
|
||||
let value =
|
||||
self.parse_keyword(Keyword::TRUE) || !self.parse_keyword(Keyword::FALSE);
|
||||
Ok(UserDefinedTypeSqlDefinitionOption::Collatable(value))
|
||||
}
|
||||
_ => self.expected("SQL definition option keyword", self.peek_token()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_parenthesized_identifiers(&mut self) -> Result<Vec<Ident>, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let idents = self.parse_comma_separated0(|p| p.parse_identifier(), Token::RParen)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue