Skip to content
90 changes: 89 additions & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,14 @@ pub struct CreateTable {
/// <https://www.postgresql.org/docs/current/ddl-inherit.html>
/// <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-INHERITS>
pub inherits: Option<Vec<ObjectName>>,
/// PostgreSQL `PARTITION OF` clause to create a partition of a parent table.
/// Contains the parent table name.
/// <https://www.postgresql.org/docs/current/sql-createtable.html>
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub partition_of: Option<ObjectName>,
/// PostgreSQL partition bound specification for PARTITION OF.
/// <https://www.postgresql.org/docs/current/sql-createtable.html>
pub for_values: Option<ForValues>,
/// SQLite "STRICT" clause.
/// if the "STRICT" table-option keyword is added to the end, after the closing ")",
/// then strict typing rules apply to that table.
Expand Down Expand Up @@ -2792,6 +2800,9 @@ impl fmt::Display for CreateTable {
dynamic = if self.dynamic { "DYNAMIC " } else { "" },
name = self.name,
)?;
if let Some(partition_of) = &self.partition_of {
write!(f, " PARTITION OF {partition_of}")?;
}
if let Some(on_cluster) = &self.on_cluster {
write!(f, " ON CLUSTER {on_cluster}")?;
}
Expand All @@ -2806,12 +2817,19 @@ impl fmt::Display for CreateTable {
Indent(DisplayCommaSeparated(&self.constraints)).fmt(f)?;
NewLine.fmt(f)?;
f.write_str(")")?;
} else if self.query.is_none() && self.like.is_none() && self.clone.is_none() {
} else if self.query.is_none()
&& self.like.is_none()
&& self.clone.is_none()
&& self.partition_of.is_none()
{
// PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens
f.write_str(" ()")?;
} else if let Some(CreateTableLikeKind::Parenthesized(like_in_columns_list)) = &self.like {
write!(f, " ({like_in_columns_list})")?;
}
if let Some(for_values) = &self.for_values {
write!(f, " {for_values}")?;
}

// Hive table comment should be after column definitions, please refer to:
// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
Expand Down Expand Up @@ -3053,6 +3071,76 @@ impl fmt::Display for CreateTable {
}
}

/// PostgreSQL partition bound specification for `PARTITION OF`.
///
/// Specifies partition bounds for a child partition table.
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtable.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ForValues {
/// `FOR VALUES IN (expr, ...)`
In(Vec<Expr>),
/// `FOR VALUES FROM (expr|MINVALUE|MAXVALUE, ...) TO (expr|MINVALUE|MAXVALUE, ...)`
From {
from: Vec<PartitionBoundValue>,
to: Vec<PartitionBoundValue>,
},
/// `FOR VALUES WITH (MODULUS n, REMAINDER r)`
With { modulus: u64, remainder: u64 },
/// `DEFAULT`
Default,
}

impl fmt::Display for ForValues {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ForValues::In(values) => {
write!(f, "FOR VALUES IN ({})", display_comma_separated(values))
}
ForValues::From { from, to } => {
write!(
f,
"FOR VALUES FROM ({}) TO ({})",
display_comma_separated(from),
display_comma_separated(to)
)
}
ForValues::With { modulus, remainder } => {
write!(
f,
"FOR VALUES WITH (MODULUS {modulus}, REMAINDER {remainder})"
)
}
ForValues::Default => write!(f, "DEFAULT"),
}
}
}

/// A value in a partition bound specification.
///
/// Used in RANGE partition bounds where values can be expressions,
/// MINVALUE (negative infinity), or MAXVALUE (positive infinity).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum PartitionBoundValue {
Expr(Expr),
MinValue,
MaxValue,
}

impl fmt::Display for PartitionBoundValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PartitionBoundValue::Expr(expr) => write!(f, "{expr}"),
PartitionBoundValue::MinValue => write!(f, "MINVALUE"),
PartitionBoundValue::MaxValue => write!(f, "MAXVALUE"),
}
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand Down
24 changes: 22 additions & 2 deletions src/ast/helpers/stmt_create_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use sqlparser_derive::{Visit, VisitMut};

use crate::ast::{
ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableLikeKind, CreateTableOptions, Expr,
FileFormat, HiveDistributionStyle, HiveFormat, Ident, InitializeKind, ObjectName, OnCommit,
OneOrManyWithParens, Query, RefreshModeKind, RowAccessPolicy, Statement,
FileFormat, ForValues, HiveDistributionStyle, HiveFormat, Ident, InitializeKind, ObjectName,
OnCommit, OneOrManyWithParens, Query, RefreshModeKind, RowAccessPolicy, Statement,
StorageSerializationPolicy, TableConstraint, TableVersion, Tag, WrappedCollection,
};

Expand Down Expand Up @@ -94,6 +94,8 @@ pub struct CreateTableBuilder {
pub cluster_by: Option<WrappedCollection<Vec<Expr>>>,
pub clustered_by: Option<ClusteredBy>,
pub inherits: Option<Vec<ObjectName>>,
pub partition_of: Option<ObjectName>,
pub for_values: Option<ForValues>,
pub strict: bool,
pub copy_grants: bool,
pub enable_schema_evolution: Option<bool>,
Expand Down Expand Up @@ -150,6 +152,8 @@ impl CreateTableBuilder {
cluster_by: None,
clustered_by: None,
inherits: None,
partition_of: None,
for_values: None,
strict: false,
copy_grants: false,
enable_schema_evolution: None,
Expand Down Expand Up @@ -317,6 +321,16 @@ impl CreateTableBuilder {
self
}

pub fn partition_of(mut self, partition_of: Option<ObjectName>) -> Self {
self.partition_of = partition_of;
self
}

pub fn for_values(mut self, for_values: Option<ForValues>) -> Self {
self.for_values = for_values;
self
}

pub fn strict(mut self, strict: bool) -> Self {
self.strict = strict;
self
Expand Down Expand Up @@ -463,6 +477,8 @@ impl CreateTableBuilder {
cluster_by: self.cluster_by,
clustered_by: self.clustered_by,
inherits: self.inherits,
partition_of: self.partition_of,
for_values: self.for_values,
strict: self.strict,
copy_grants: self.copy_grants,
enable_schema_evolution: self.enable_schema_evolution,
Expand Down Expand Up @@ -527,6 +543,8 @@ impl TryFrom<Statement> for CreateTableBuilder {
cluster_by,
clustered_by,
inherits,
partition_of,
for_values,
strict,
copy_grants,
enable_schema_evolution,
Expand Down Expand Up @@ -577,6 +595,8 @@ impl TryFrom<Statement> for CreateTableBuilder {
cluster_by,
clustered_by,
inherits,
partition_of,
for_values,
strict,
iceberg,
copy_grants,
Expand Down
13 changes: 7 additions & 6 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ pub use self::ddl::{
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem,
OperatorOption, OperatorPurpose, Owner, Partition, ProcedureParam, ReferentialAction,
RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind, Truncate,
DropOperatorSignature, DropTrigger, ForValues, GeneratedAs, GeneratedExpressionMode,
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
NullsDistinctOption, OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem,
OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition, PartitionBoundValue,
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
TriggerObjectKind, Truncate,
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
UserDefinedTypeStorage, ViewColumnDef,
Expand Down
48 changes: 40 additions & 8 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,20 @@ use super::{
ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements,
ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr,
ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr,
ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr,
FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound,
IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join,
JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause,
MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr,
MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName,
ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy,
OrderByExpr, OrderByKind, OutputClause, Partition, PivotValueSource, ProjectionSelect, Query,
RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values,
ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill,
OrderByExpr, OrderByKind, OutputClause, Partition, PartitionBoundValue, PivotValueSource,
ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction,
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update,
UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement,
WildcardAdditionalOptions, With, WithFill,
};

/// Given an iterator of spans, return the [Span::union] of all spans.
Expand Down Expand Up @@ -554,6 +555,8 @@ impl Spanned for CreateTable {
cluster_by: _, // todo, BigQuery specific
clustered_by: _, // todo, Hive specific
inherits: _, // todo, PostgreSQL specific
partition_of,
for_values,
strict: _, // bool
copy_grants: _, // bool
enable_schema_evolution: _, // bool
Expand Down Expand Up @@ -584,7 +587,9 @@ impl Spanned for CreateTable {
.chain(columns.iter().map(|i| i.span()))
.chain(constraints.iter().map(|i| i.span()))
.chain(query.iter().map(|i| i.span()))
.chain(clone.iter().map(|i| i.span())),
.chain(clone.iter().map(|i| i.span()))
.chain(partition_of.iter().map(|i| i.span()))
.chain(for_values.iter().map(|i| i.span())),
)
}
}
Expand Down Expand Up @@ -622,6 +627,33 @@ impl Spanned for TableConstraint {
}
}

impl Spanned for PartitionBoundValue {
fn span(&self) -> Span {
match self {
PartitionBoundValue::Expr(expr) => expr.span(),
// MINVALUE and MAXVALUE are keywords without tracked spans
PartitionBoundValue::MinValue => Span::empty(),
PartitionBoundValue::MaxValue => Span::empty(),
}
}
}

impl Spanned for ForValues {
fn span(&self) -> Span {
match self {
ForValues::In(exprs) => union_spans(exprs.iter().map(|e| e.span())),
ForValues::From { from, to } => union_spans(
from.iter()
.map(|v| v.span())
.chain(to.iter().map(|v| v.span())),
),
// WITH (MODULUS n, REMAINDER r) - u64 values have no spans
ForValues::With { .. } => Span::empty(),
ForValues::Default => Span::empty(),
}
}
}

impl Spanned for CreateIndex {
fn span(&self) -> Span {
let CreateIndex {
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ define_keywords!(
MODIFIES,
MODIFY,
MODULE,
MODULUS,
MONITOR,
MONTH,
MONTHS,
Expand Down Expand Up @@ -837,6 +838,7 @@ define_keywords!(
RELAY,
RELEASE,
RELEASES,
REMAINDER,
REMOTE,
REMOVE,
REMOVEQUOTES,
Expand Down
Loading