Skip to content

Commit fe92bd9

Browse files
postgres: support UNLOGGED tables and SET LOGGED/UNLOGGED
Add parser and AST support for PostgreSQL CREATE UNLOGGED TABLE and\nALTER TABLE ... SET LOGGED|UNLOGGED operations.\n\n- add LOGGED keyword\n- add CreateTable.unlogged and wire it through CreateTableBuilder\n- render UNLOGGED in CreateTable display\n- add AlterTableOperation::SetLogged and ::SetUnlogged display/spans\n- parse UNLOGGED only for PostgreSqlDialect|GenericDialect\n- parse ALTER TABLE SET LOGGED|UNLOGGED operations
1 parent bd7f70e commit fe92bd9

File tree

5 files changed

+61
-14
lines changed

5 files changed

+61
-14
lines changed

src/ast/ddl.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,14 @@ pub enum AlterTableOperation {
442442
/// Table properties specified as SQL options.
443443
table_properties: Vec<SqlOption>,
444444
},
445+
/// `SET LOGGED`
446+
///
447+
/// Note: this is PostgreSQL-specific.
448+
SetLogged,
449+
/// `SET UNLOGGED`
450+
///
451+
/// Note: this is PostgreSQL-specific.
452+
SetUnlogged,
445453
/// `OWNER TO { <new_owner> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
446454
///
447455
/// Note: this is PostgreSQL-specific <https://www.postgresql.org/docs/current/sql-altertable.html>
@@ -965,6 +973,12 @@ impl fmt::Display for AlterTableOperation {
965973
display_comma_separated(table_properties)
966974
)
967975
}
976+
AlterTableOperation::SetLogged => {
977+
write!(f, "SET LOGGED")
978+
}
979+
AlterTableOperation::SetUnlogged => {
980+
write!(f, "SET UNLOGGED")
981+
}
968982
AlterTableOperation::FreezePartition {
969983
partition,
970984
with_name,
@@ -2889,6 +2903,8 @@ pub struct CreateTable {
28892903
pub or_replace: bool,
28902904
/// `TEMP` or `TEMPORARY` clause
28912905
pub temporary: bool,
2906+
/// `UNLOGGED` clause
2907+
pub unlogged: bool,
28922908
/// `EXTERNAL` clause
28932909
pub external: bool,
28942910
/// `DYNAMIC` clause
@@ -3045,7 +3061,7 @@ impl fmt::Display for CreateTable {
30453061
// `CREATE TABLE t (a INT) AS SELECT a from t2`
30463062
write!(
30473063
f,
3048-
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{dynamic}{iceberg}TABLE {if_not_exists}{name}",
3064+
"CREATE {or_replace}{external}{global}{temporary}{unlogged}{transient}{volatile}{dynamic}{iceberg}TABLE {if_not_exists}{name}",
30493065
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
30503066
external = if self.external { "EXTERNAL " } else { "" },
30513067
global = self.global
@@ -3059,6 +3075,7 @@ impl fmt::Display for CreateTable {
30593075
.unwrap_or(""),
30603076
if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" },
30613077
temporary = if self.temporary { "TEMPORARY " } else { "" },
3078+
unlogged = if self.unlogged { "UNLOGGED " } else { "" },
30623079
transient = if self.transient { "TRANSIENT " } else { "" },
30633080
volatile = if self.volatile { "VOLATILE " } else { "" },
30643081
// Only for Snowflake

src/ast/helpers/stmt_create_table.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub struct CreateTableBuilder {
6868
pub or_replace: bool,
6969
/// Whether the table is `TEMPORARY`.
7070
pub temporary: bool,
71+
/// Whether the table is `UNLOGGED`.
72+
pub unlogged: bool,
7173
/// Whether the table is `EXTERNAL`.
7274
pub external: bool,
7375
/// Optional `GLOBAL` flag for dialects that support it.
@@ -178,6 +180,7 @@ impl CreateTableBuilder {
178180
Self {
179181
or_replace: false,
180182
temporary: false,
183+
unlogged: false,
181184
external: false,
182185
global: None,
183186
if_not_exists: false,
@@ -241,6 +244,11 @@ impl CreateTableBuilder {
241244
self.temporary = temporary;
242245
self
243246
}
247+
/// Mark the table as `UNLOGGED`.
248+
pub fn unlogged(mut self, unlogged: bool) -> Self {
249+
self.unlogged = unlogged;
250+
self
251+
}
244252
/// Mark the table as `EXTERNAL`.
245253
pub fn external(mut self, external: bool) -> Self {
246254
self.external = external;
@@ -509,6 +517,7 @@ impl CreateTableBuilder {
509517
CreateTable {
510518
or_replace: self.or_replace,
511519
temporary: self.temporary,
520+
unlogged: self.unlogged,
512521
external: self.external,
513522
global: self.global,
514523
if_not_exists: self.if_not_exists,
@@ -584,6 +593,7 @@ impl From<CreateTable> for CreateTableBuilder {
584593
Self {
585594
or_replace: table.or_replace,
586595
temporary: table.temporary,
596+
unlogged: table.unlogged,
587597
external: table.external,
588598
global: table.global,
589599
if_not_exists: table.if_not_exists,

src/ast/spans.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ impl Spanned for CreateTable {
531531
let CreateTable {
532532
or_replace: _, // bool
533533
temporary: _, // bool
534+
unlogged: _, // bool
534535
external: _, // bool
535536
global: _, // bool
536537
dynamic: _, // bool
@@ -1184,6 +1185,8 @@ impl Spanned for AlterTableOperation {
11841185
AlterTableOperation::SetTblProperties { table_properties } => {
11851186
union_spans(table_properties.iter().map(|i| i.span()))
11861187
}
1188+
AlterTableOperation::SetLogged => Span::empty(),
1189+
AlterTableOperation::SetUnlogged => Span::empty(),
11871190
AlterTableOperation::OwnerTo { .. } => Span::empty(),
11881191
AlterTableOperation::ClusterBy { exprs } => union_spans(exprs.iter().map(|e| e.span())),
11891192
AlterTableOperation::DropClusteringKey => Span::empty(),

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ define_keywords!(
587587
LOCK,
588588
LOCKED,
589589
LOG,
590+
LOGGED,
590591
LOGIN,
591592
LOGS,
592593
LONG,

src/parser/mod.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5097,12 +5097,16 @@ impl<'a> Parser<'a> {
50975097
let temporary = self
50985098
.parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
50995099
.is_some();
5100+
let unlogged = dialect_of!(self is PostgreSqlDialect | GenericDialect)
5101+
&& self.parse_keyword(Keyword::UNLOGGED);
51005102
let persistent = dialect_of!(self is DuckDbDialect)
51015103
&& self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some();
51025104
let create_view_params = self.parse_create_view_params()?;
51035105
if self.parse_keyword(Keyword::TABLE) {
5104-
self.parse_create_table(or_replace, temporary, global, transient)
5106+
self.parse_create_table(or_replace, temporary, unlogged, global, transient)
51055107
.map(Into::into)
5108+
} else if unlogged {
5109+
self.expected_ref("TABLE after UNLOGGED", self.peek_token_ref())
51065110
} else if self.peek_keyword(Keyword::MATERIALIZED)
51075111
|| self.peek_keyword(Keyword::VIEW)
51085112
|| self.peek_keywords(&[Keyword::SECURE, Keyword::MATERIALIZED, Keyword::VIEW])
@@ -8264,6 +8268,7 @@ impl<'a> Parser<'a> {
82648268
&mut self,
82658269
or_replace: bool,
82668270
temporary: bool,
8271+
unlogged: bool,
82678272
global: Option<bool>,
82688273
transient: bool,
82698274
) -> Result<CreateTable, ParserError> {
@@ -8382,6 +8387,7 @@ impl<'a> Parser<'a> {
83828387

83838388
Ok(CreateTableBuilder::new(table_name)
83848389
.temporary(temporary)
8390+
.unlogged(unlogged)
83858391
.columns(columns)
83868392
.constraints(constraints)
83878393
.or_replace(or_replace)
@@ -10377,21 +10383,31 @@ impl<'a> Parser<'a> {
1037710383
let name = self.parse_identifier()?;
1037810384
AlterTableOperation::ValidateConstraint { name }
1037910385
} else {
10380-
let mut options =
10381-
self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?;
10382-
if !options.is_empty() {
10383-
AlterTableOperation::SetTblProperties {
10384-
table_properties: options,
10385-
}
10386+
if dialect_of!(self is PostgreSqlDialect | GenericDialect)
10387+
&& self.parse_keywords(&[Keyword::SET, Keyword::LOGGED])
10388+
{
10389+
AlterTableOperation::SetLogged
10390+
} else if dialect_of!(self is PostgreSqlDialect | GenericDialect)
10391+
&& self.parse_keywords(&[Keyword::SET, Keyword::UNLOGGED])
10392+
{
10393+
AlterTableOperation::SetUnlogged
1038610394
} else {
10387-
options = self.parse_options(Keyword::SET)?;
10395+
let mut options =
10396+
self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?;
1038810397
if !options.is_empty() {
10389-
AlterTableOperation::SetOptionsParens { options }
10398+
AlterTableOperation::SetTblProperties {
10399+
table_properties: options,
10400+
}
1039010401
} else {
10391-
return self.expected_ref(
10392-
"ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, SET, or SET TBLPROPERTIES after ALTER TABLE",
10393-
self.peek_token_ref(),
10394-
);
10402+
options = self.parse_options(Keyword::SET)?;
10403+
if !options.is_empty() {
10404+
AlterTableOperation::SetOptionsParens { options }
10405+
} else {
10406+
return self.expected_ref(
10407+
"ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, SET, or SET TBLPROPERTIES after ALTER TABLE",
10408+
self.peek_token_ref(),
10409+
);
10410+
}
1039510411
}
1039610412
}
1039710413
};

0 commit comments

Comments
 (0)