Skip to content

Commit 78be8b1

Browse files
Complete PostgreSQL CREATE TYPE Support (#2094)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent a00d5cd commit 78be8b1

File tree

6 files changed

+886
-52
lines changed

6 files changed

+886
-52
lines changed

src/ast/ddl.rs

Lines changed: 309 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,21 +2023,44 @@ impl fmt::Display for DropBehavior {
20232023
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20242024
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
20252025
pub enum UserDefinedTypeRepresentation {
2026+
/// Composite type: `CREATE TYPE name AS (attributes)`
20262027
Composite {
20272028
attributes: Vec<UserDefinedTypeCompositeAttributeDef>,
20282029
},
2030+
/// Enum type: `CREATE TYPE name AS ENUM (labels)`
2031+
///
20292032
/// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
20302033
Enum { labels: Vec<Ident> },
2034+
/// Range type: `CREATE TYPE name AS RANGE (options)`
2035+
///
2036+
/// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
2037+
Range {
2038+
options: Vec<UserDefinedTypeRangeOption>,
2039+
},
2040+
/// Base type (SQL definition): `CREATE TYPE name (options)`
2041+
///
2042+
/// Note the lack of `AS` keyword
2043+
///
2044+
/// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
2045+
SqlDefinition {
2046+
options: Vec<UserDefinedTypeSqlDefinitionOption>,
2047+
},
20312048
}
20322049

20332050
impl fmt::Display for UserDefinedTypeRepresentation {
20342051
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20352052
match self {
2036-
UserDefinedTypeRepresentation::Composite { attributes } => {
2037-
write!(f, "({})", display_comma_separated(attributes))
2053+
Self::Composite { attributes } => {
2054+
write!(f, "AS ({})", display_comma_separated(attributes))
2055+
}
2056+
Self::Enum { labels } => {
2057+
write!(f, "AS ENUM ({})", display_comma_separated(labels))
20382058
}
2039-
UserDefinedTypeRepresentation::Enum { labels } => {
2040-
write!(f, "ENUM ({})", display_comma_separated(labels))
2059+
Self::Range { options } => {
2060+
write!(f, "AS RANGE ({})", display_comma_separated(options))
2061+
}
2062+
Self::SqlDefinition { options } => {
2063+
write!(f, "({})", display_comma_separated(options))
20412064
}
20422065
}
20432066
}
@@ -2063,6 +2086,288 @@ impl fmt::Display for UserDefinedTypeCompositeAttributeDef {
20632086
}
20642087
}
20652088

2089+
/// Internal length specification for PostgreSQL user-defined base types.
2090+
///
2091+
/// Specifies the internal length in bytes of the new type's internal representation.
2092+
/// The default assumption is that it is variable-length.
2093+
///
2094+
/// # PostgreSQL Documentation
2095+
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
2096+
///
2097+
/// # Examples
2098+
/// ```sql
2099+
/// CREATE TYPE mytype (
2100+
/// INPUT = in_func,
2101+
/// OUTPUT = out_func,
2102+
/// INTERNALLENGTH = 16 -- Fixed 16-byte length
2103+
/// );
2104+
///
2105+
/// CREATE TYPE mytype2 (
2106+
/// INPUT = in_func,
2107+
/// OUTPUT = out_func,
2108+
/// INTERNALLENGTH = VARIABLE -- Variable length
2109+
/// );
2110+
/// ```
2111+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2112+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2113+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2114+
pub enum UserDefinedTypeInternalLength {
2115+
/// Fixed internal length: `INTERNALLENGTH = <number>`
2116+
Fixed(u64),
2117+
/// Variable internal length: `INTERNALLENGTH = VARIABLE`
2118+
Variable,
2119+
}
2120+
2121+
impl fmt::Display for UserDefinedTypeInternalLength {
2122+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2123+
match self {
2124+
UserDefinedTypeInternalLength::Fixed(n) => write!(f, "{}", n),
2125+
UserDefinedTypeInternalLength::Variable => write!(f, "VARIABLE"),
2126+
}
2127+
}
2128+
}
2129+
2130+
/// Alignment specification for PostgreSQL user-defined base types.
2131+
///
2132+
/// Specifies the storage alignment requirement for values of the data type.
2133+
/// The allowed values equate to alignment on 1, 2, 4, or 8 byte boundaries.
2134+
/// Note that variable-length types must have an alignment of at least 4, since
2135+
/// they necessarily contain an int4 as their first component.
2136+
///
2137+
/// # PostgreSQL Documentation
2138+
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
2139+
///
2140+
/// # Examples
2141+
/// ```sql
2142+
/// CREATE TYPE mytype (
2143+
/// INPUT = in_func,
2144+
/// OUTPUT = out_func,
2145+
/// ALIGNMENT = int4 -- 4-byte alignment
2146+
/// );
2147+
/// ```
2148+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2149+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2150+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2151+
pub enum Alignment {
2152+
/// Single-byte alignment: `ALIGNMENT = char`
2153+
Char,
2154+
/// 2-byte alignment: `ALIGNMENT = int2`
2155+
Int2,
2156+
/// 4-byte alignment: `ALIGNMENT = int4`
2157+
Int4,
2158+
/// 8-byte alignment: `ALIGNMENT = double`
2159+
Double,
2160+
}
2161+
2162+
impl fmt::Display for Alignment {
2163+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2164+
match self {
2165+
Alignment::Char => write!(f, "char"),
2166+
Alignment::Int2 => write!(f, "int2"),
2167+
Alignment::Int4 => write!(f, "int4"),
2168+
Alignment::Double => write!(f, "double"),
2169+
}
2170+
}
2171+
}
2172+
2173+
/// Storage specification for PostgreSQL user-defined base types.
2174+
///
2175+
/// Specifies the storage strategy for values of the data type:
2176+
/// - `plain`: Prevents compression and out-of-line storage (for fixed-length types)
2177+
/// - `external`: Allows out-of-line storage but not compression
2178+
/// - `extended`: Allows both compression and out-of-line storage (default for most types)
2179+
/// - `main`: Allows compression but discourages out-of-line storage
2180+
///
2181+
/// # PostgreSQL Documentation
2182+
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
2183+
///
2184+
/// # Examples
2185+
/// ```sql
2186+
/// CREATE TYPE mytype (
2187+
/// INPUT = in_func,
2188+
/// OUTPUT = out_func,
2189+
/// STORAGE = plain
2190+
/// );
2191+
/// ```
2192+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2193+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2194+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2195+
pub enum UserDefinedTypeStorage {
2196+
/// No compression or out-of-line storage: `STORAGE = plain`
2197+
Plain,
2198+
/// Out-of-line storage allowed, no compression: `STORAGE = external`
2199+
External,
2200+
/// Both compression and out-of-line storage allowed: `STORAGE = extended`
2201+
Extended,
2202+
/// Compression allowed, out-of-line discouraged: `STORAGE = main`
2203+
Main,
2204+
}
2205+
2206+
impl fmt::Display for UserDefinedTypeStorage {
2207+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2208+
match self {
2209+
UserDefinedTypeStorage::Plain => write!(f, "plain"),
2210+
UserDefinedTypeStorage::External => write!(f, "external"),
2211+
UserDefinedTypeStorage::Extended => write!(f, "extended"),
2212+
UserDefinedTypeStorage::Main => write!(f, "main"),
2213+
}
2214+
}
2215+
}
2216+
2217+
/// Options for PostgreSQL `CREATE TYPE ... AS RANGE` statement.
2218+
///
2219+
/// Range types are data types representing a range of values of some element type
2220+
/// (called the range's subtype). These options configure the behavior of the range type.
2221+
///
2222+
/// # PostgreSQL Documentation
2223+
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
2224+
///
2225+
/// # Examples
2226+
/// ```sql
2227+
/// CREATE TYPE int4range AS RANGE (
2228+
/// SUBTYPE = int4,
2229+
/// SUBTYPE_OPCLASS = int4_ops,
2230+
/// CANONICAL = int4range_canonical,
2231+
/// SUBTYPE_DIFF = int4range_subdiff
2232+
/// );
2233+
/// ```
2234+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2235+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2236+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2237+
pub enum UserDefinedTypeRangeOption {
2238+
/// The element type that the range type will represent: `SUBTYPE = subtype`
2239+
Subtype(DataType),
2240+
/// The operator class for the subtype: `SUBTYPE_OPCLASS = subtype_operator_class`
2241+
SubtypeOpClass(ObjectName),
2242+
/// Collation to use for ordering the subtype: `COLLATION = collation`
2243+
Collation(ObjectName),
2244+
/// Function to convert range values to canonical form: `CANONICAL = canonical_function`
2245+
Canonical(ObjectName),
2246+
/// Function to compute the difference between two subtype values: `SUBTYPE_DIFF = subtype_diff_function`
2247+
SubtypeDiff(ObjectName),
2248+
/// Name of the corresponding multirange type: `MULTIRANGE_TYPE_NAME = multirange_type_name`
2249+
MultirangeTypeName(ObjectName),
2250+
}
2251+
2252+
impl fmt::Display for UserDefinedTypeRangeOption {
2253+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2254+
match self {
2255+
UserDefinedTypeRangeOption::Subtype(dt) => write!(f, "SUBTYPE = {}", dt),
2256+
UserDefinedTypeRangeOption::SubtypeOpClass(name) => {
2257+
write!(f, "SUBTYPE_OPCLASS = {}", name)
2258+
}
2259+
UserDefinedTypeRangeOption::Collation(name) => write!(f, "COLLATION = {}", name),
2260+
UserDefinedTypeRangeOption::Canonical(name) => write!(f, "CANONICAL = {}", name),
2261+
UserDefinedTypeRangeOption::SubtypeDiff(name) => write!(f, "SUBTYPE_DIFF = {}", name),
2262+
UserDefinedTypeRangeOption::MultirangeTypeName(name) => {
2263+
write!(f, "MULTIRANGE_TYPE_NAME = {}", name)
2264+
}
2265+
}
2266+
}
2267+
}
2268+
2269+
/// Options for PostgreSQL `CREATE TYPE ... (<options>)` statement (base type definition).
2270+
///
2271+
/// Base types are the lowest-level data types in PostgreSQL. To define a new base type,
2272+
/// you must specify functions that convert it to and from text representation, and optionally
2273+
/// binary representation and other properties.
2274+
///
2275+
/// Note: This syntax uses parentheses directly after the type name, without the `AS` keyword.
2276+
///
2277+
/// # PostgreSQL Documentation
2278+
/// See: <https://www.postgresql.org/docs/current/sql-createtype.html>
2279+
///
2280+
/// # Examples
2281+
/// ```sql
2282+
/// CREATE TYPE complex (
2283+
/// INPUT = complex_in,
2284+
/// OUTPUT = complex_out,
2285+
/// INTERNALLENGTH = 16,
2286+
/// ALIGNMENT = double
2287+
/// );
2288+
/// ```
2289+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2290+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2291+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2292+
pub enum UserDefinedTypeSqlDefinitionOption {
2293+
/// Function to convert from external text representation to internal: `INPUT = input_function`
2294+
Input(ObjectName),
2295+
/// Function to convert from internal to external text representation: `OUTPUT = output_function`
2296+
Output(ObjectName),
2297+
/// Function to convert from external binary representation to internal: `RECEIVE = receive_function`
2298+
Receive(ObjectName),
2299+
/// Function to convert from internal to external binary representation: `SEND = send_function`
2300+
Send(ObjectName),
2301+
/// Function to convert type modifiers from text array to internal form: `TYPMOD_IN = type_modifier_input_function`
2302+
TypmodIn(ObjectName),
2303+
/// Function to convert type modifiers from internal to text form: `TYPMOD_OUT = type_modifier_output_function`
2304+
TypmodOut(ObjectName),
2305+
/// Function to compute statistics for the data type: `ANALYZE = analyze_function`
2306+
Analyze(ObjectName),
2307+
/// Function to handle subscripting operations: `SUBSCRIPT = subscript_function`
2308+
Subscript(ObjectName),
2309+
/// Internal storage size in bytes, or VARIABLE for variable-length: `INTERNALLENGTH = { internallength | VARIABLE }`
2310+
InternalLength(UserDefinedTypeInternalLength),
2311+
/// Indicates values are passed by value rather than by reference: `PASSEDBYVALUE`
2312+
PassedByValue,
2313+
/// Storage alignment requirement (1, 2, 4, or 8 bytes): `ALIGNMENT = alignment`
2314+
Alignment(Alignment),
2315+
/// Storage strategy for varlena types: `STORAGE = storage`
2316+
Storage(UserDefinedTypeStorage),
2317+
/// Copy properties from an existing type: `LIKE = like_type`
2318+
Like(ObjectName),
2319+
/// Type category for implicit casting rules (single char): `CATEGORY = category`
2320+
Category(char),
2321+
/// Whether this type is preferred within its category: `PREFERRED = preferred`
2322+
Preferred(bool),
2323+
/// Default value for the type: `DEFAULT = default`
2324+
Default(Expr),
2325+
/// Element type for array types: `ELEMENT = element`
2326+
Element(DataType),
2327+
/// Delimiter character for array value display: `DELIMITER = delimiter`
2328+
Delimiter(String),
2329+
/// Whether the type supports collation: `COLLATABLE = collatable`
2330+
Collatable(bool),
2331+
}
2332+
2333+
impl fmt::Display for UserDefinedTypeSqlDefinitionOption {
2334+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2335+
match self {
2336+
UserDefinedTypeSqlDefinitionOption::Input(name) => write!(f, "INPUT = {}", name),
2337+
UserDefinedTypeSqlDefinitionOption::Output(name) => write!(f, "OUTPUT = {}", name),
2338+
UserDefinedTypeSqlDefinitionOption::Receive(name) => write!(f, "RECEIVE = {}", name),
2339+
UserDefinedTypeSqlDefinitionOption::Send(name) => write!(f, "SEND = {}", name),
2340+
UserDefinedTypeSqlDefinitionOption::TypmodIn(name) => write!(f, "TYPMOD_IN = {}", name),
2341+
UserDefinedTypeSqlDefinitionOption::TypmodOut(name) => {
2342+
write!(f, "TYPMOD_OUT = {}", name)
2343+
}
2344+
UserDefinedTypeSqlDefinitionOption::Analyze(name) => write!(f, "ANALYZE = {}", name),
2345+
UserDefinedTypeSqlDefinitionOption::Subscript(name) => {
2346+
write!(f, "SUBSCRIPT = {}", name)
2347+
}
2348+
UserDefinedTypeSqlDefinitionOption::InternalLength(len) => {
2349+
write!(f, "INTERNALLENGTH = {}", len)
2350+
}
2351+
UserDefinedTypeSqlDefinitionOption::PassedByValue => write!(f, "PASSEDBYVALUE"),
2352+
UserDefinedTypeSqlDefinitionOption::Alignment(align) => {
2353+
write!(f, "ALIGNMENT = {}", align)
2354+
}
2355+
UserDefinedTypeSqlDefinitionOption::Storage(storage) => {
2356+
write!(f, "STORAGE = {}", storage)
2357+
}
2358+
UserDefinedTypeSqlDefinitionOption::Like(name) => write!(f, "LIKE = {}", name),
2359+
UserDefinedTypeSqlDefinitionOption::Category(c) => write!(f, "CATEGORY = '{}'", c),
2360+
UserDefinedTypeSqlDefinitionOption::Preferred(b) => write!(f, "PREFERRED = {}", b),
2361+
UserDefinedTypeSqlDefinitionOption::Default(expr) => write!(f, "DEFAULT = {}", expr),
2362+
UserDefinedTypeSqlDefinitionOption::Element(dt) => write!(f, "ELEMENT = {}", dt),
2363+
UserDefinedTypeSqlDefinitionOption::Delimiter(s) => {
2364+
write!(f, "DELIMITER = '{}'", escape_single_quote_string(s))
2365+
}
2366+
UserDefinedTypeSqlDefinitionOption::Collatable(b) => write!(f, "COLLATABLE = {}", b),
2367+
}
2368+
}
2369+
}
2370+
20662371
/// PARTITION statement used in ALTER TABLE et al. such as in Hive and ClickHouse SQL.
20672372
/// For example, ClickHouse's OPTIMIZE TABLE supports syntax like PARTITION ID 'partition_id' and PARTITION expr.
20682373
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)

0 commit comments

Comments
 (0)