Complete PostgreSQL CREATE TYPE Support (#2094)

Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
Luca Cappelletti 2025-11-13 10:25:27 +01:00 committed by GitHub
parent a00d5cd967
commit 78be8b178b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 889 additions and 55 deletions

View file

@ -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)

View file

@ -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}")?;

View file

@ -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,

View file

@ -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)?;