Skip to content

Commit 29d49fa

Browse files
parser: support PostgreSQL CREATE/ALTER TEXT SEARCH DDL
Add parser support for CREATE/ALTER TEXT SEARCH DICTIONARY,\nCONFIGURATION, TEMPLATE, and PARSER forms, including operation-specific\nALTER clauses and strict CREATE option parsing.\n\nRegister text-search object names as parser keywords and reject unsupported\nCREATE modifiers for text-search objects.
1 parent cb2aeb8 commit 29d49fa

2 files changed

Lines changed: 155 additions & 1 deletion

File tree

src/keywords.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ define_keywords!(
246246
COMPUTE,
247247
CONCURRENTLY,
248248
CONDITION,
249+
CONFIGURATION,
249250
CONFLICT,
250251
CONNECT,
251252
CONNECTION,
@@ -333,6 +334,7 @@ define_keywords!(
333334
DETACH,
334335
DETAIL,
335336
DETERMINISTIC,
337+
DICTIONARY,
336338
DIMENSIONS,
337339
DIRECTORY,
338340
DISABLE,
@@ -766,6 +768,7 @@ define_keywords!(
766768
PARALLEL,
767769
PARAMETER,
768770
PARQUET,
771+
PARSER,
769772
PART,
770773
PARTIAL,
771774
PARTITION,
@@ -1036,6 +1039,7 @@ define_keywords!(
10361039
TASK,
10371040
TBLPROPERTIES,
10381041
TEMP,
1042+
TEMPLATE,
10391043
TEMPORARY,
10401044
TEMPTABLE,
10411045
TERMINATED,

src/parser/mod.rs

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5144,6 +5144,13 @@ impl<'a> Parser<'a> {
51445144
let create_view_params = self.parse_create_view_params()?;
51455145
if self.peek_keywords(&[Keyword::SNAPSHOT, Keyword::TABLE]) {
51465146
self.parse_create_snapshot_table().map(Into::into)
5147+
} else if self.parse_keywords(&[Keyword::TEXT, Keyword::SEARCH]) {
5148+
if or_replace || or_alter || temporary || global.is_some() || transient || persistent {
5149+
return Err(ParserError::ParserError(
5150+
"CREATE TEXT SEARCH does not support CREATE modifiers".to_string(),
5151+
));
5152+
}
5153+
self.parse_create_text_search().map(Into::into)
51475154
} else if self.parse_keyword(Keyword::TABLE) {
51485155
self.parse_create_table(or_replace, temporary, global, transient, volatile, multiset)
51495156
.map(Into::into)
@@ -5219,6 +5226,145 @@ impl<'a> Parser<'a> {
52195226
}
52205227
}
52215228

5229+
fn parse_text_search_object_type(&mut self) -> Result<TextSearchObjectType, ParserError> {
5230+
match self.expect_one_of_keywords(&[
5231+
Keyword::DICTIONARY,
5232+
Keyword::CONFIGURATION,
5233+
Keyword::TEMPLATE,
5234+
Keyword::PARSER,
5235+
])? {
5236+
Keyword::DICTIONARY => Ok(TextSearchObjectType::Dictionary),
5237+
Keyword::CONFIGURATION => Ok(TextSearchObjectType::Configuration),
5238+
Keyword::TEMPLATE => Ok(TextSearchObjectType::Template),
5239+
Keyword::PARSER => Ok(TextSearchObjectType::Parser),
5240+
// unreachable because expect_one_of_keywords used above
5241+
unexpected_keyword => Err(ParserError::ParserError(format!(
5242+
"Internal parser error: expected any of {{DICTIONARY, CONFIGURATION, TEMPLATE, PARSER}}, got {unexpected_keyword:?}"
5243+
))),
5244+
}
5245+
}
5246+
5247+
fn parse_text_search_option(&mut self) -> Result<SqlOption, ParserError> {
5248+
let key = self.parse_identifier()?;
5249+
self.expect_token(&Token::Eq)?;
5250+
let value = self.parse_expr()?;
5251+
Ok(SqlOption::KeyValue { key, value })
5252+
}
5253+
5254+
/// Parse a PostgreSQL `CREATE TEXT SEARCH ...` statement.
5255+
pub fn parse_create_text_search(&mut self) -> Result<CreateTextSearch, ParserError> {
5256+
let object_type = self.parse_text_search_object_type()?;
5257+
let name = self.parse_object_name(false)?;
5258+
self.expect_token(&Token::LParen)?;
5259+
let options = self.parse_comma_separated(Parser::parse_text_search_option)?;
5260+
self.expect_token(&Token::RParen)?;
5261+
Ok(CreateTextSearch {
5262+
object_type,
5263+
name,
5264+
options,
5265+
})
5266+
}
5267+
5268+
fn parse_alter_text_search_dictionary_option(
5269+
&mut self,
5270+
) -> Result<AlterTextSearchDictionaryOption, ParserError> {
5271+
let key = self.parse_identifier()?;
5272+
let value = if self.consume_token(&Token::Eq) {
5273+
Some(self.parse_expr()?)
5274+
} else {
5275+
None
5276+
};
5277+
Ok(AlterTextSearchDictionaryOption { key, value })
5278+
}
5279+
5280+
/// Parse a PostgreSQL `ALTER TEXT SEARCH ...` statement.
5281+
pub fn parse_alter_text_search(&mut self) -> Result<AlterTextSearch, ParserError> {
5282+
let object_type = self.parse_text_search_object_type()?;
5283+
let name = self.parse_object_name(false)?;
5284+
5285+
let operation = match object_type {
5286+
TextSearchObjectType::Dictionary => {
5287+
if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
5288+
AlterTextSearchOperation::RenameTo {
5289+
new_name: self.parse_identifier()?,
5290+
}
5291+
} else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
5292+
AlterTextSearchOperation::OwnerTo(self.parse_owner()?)
5293+
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
5294+
AlterTextSearchOperation::SetSchema {
5295+
schema_name: self.parse_object_name(false)?,
5296+
}
5297+
} else if self.consume_token(&Token::LParen) {
5298+
let options = self
5299+
.parse_comma_separated(Parser::parse_alter_text_search_dictionary_option)?;
5300+
self.expect_token(&Token::RParen)?;
5301+
AlterTextSearchOperation::SetOptions { options }
5302+
} else {
5303+
return self.expected_ref(
5304+
"RENAME TO, OWNER TO, SET SCHEMA, or (...) after ALTER TEXT SEARCH DICTIONARY",
5305+
self.peek_token_ref(),
5306+
);
5307+
}
5308+
}
5309+
TextSearchObjectType::Configuration => {
5310+
if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
5311+
AlterTextSearchOperation::RenameTo {
5312+
new_name: self.parse_identifier()?,
5313+
}
5314+
} else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
5315+
AlterTextSearchOperation::OwnerTo(self.parse_owner()?)
5316+
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
5317+
AlterTextSearchOperation::SetSchema {
5318+
schema_name: self.parse_object_name(false)?,
5319+
}
5320+
} else {
5321+
return self.expected_ref(
5322+
"RENAME TO, OWNER TO, or SET SCHEMA after ALTER TEXT SEARCH CONFIGURATION",
5323+
self.peek_token_ref(),
5324+
);
5325+
}
5326+
}
5327+
TextSearchObjectType::Template => {
5328+
if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
5329+
AlterTextSearchOperation::RenameTo {
5330+
new_name: self.parse_identifier()?,
5331+
}
5332+
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
5333+
AlterTextSearchOperation::SetSchema {
5334+
schema_name: self.parse_object_name(false)?,
5335+
}
5336+
} else {
5337+
return self.expected_ref(
5338+
"RENAME TO or SET SCHEMA after ALTER TEXT SEARCH TEMPLATE",
5339+
self.peek_token_ref(),
5340+
);
5341+
}
5342+
}
5343+
TextSearchObjectType::Parser => {
5344+
if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
5345+
AlterTextSearchOperation::RenameTo {
5346+
new_name: self.parse_identifier()?,
5347+
}
5348+
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
5349+
AlterTextSearchOperation::SetSchema {
5350+
schema_name: self.parse_object_name(false)?,
5351+
}
5352+
} else {
5353+
return self.expected_ref(
5354+
"RENAME TO or SET SCHEMA after ALTER TEXT SEARCH PARSER",
5355+
self.peek_token_ref(),
5356+
);
5357+
}
5358+
}
5359+
};
5360+
5361+
Ok(AlterTextSearch {
5362+
object_type,
5363+
name,
5364+
operation,
5365+
})
5366+
}
5367+
52225368
fn parse_create_user(&mut self, or_replace: bool) -> Result<CreateUser, ParserError> {
52235369
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
52245370
let name = self.parse_identifier()?;
@@ -10797,6 +10943,10 @@ impl<'a> Parser<'a> {
1079710943

1079810944
/// Parse an `ALTER <object>` statement and dispatch to the appropriate alter handler.
1079910945
pub fn parse_alter(&mut self) -> Result<Statement, ParserError> {
10946+
if self.parse_keywords(&[Keyword::TEXT, Keyword::SEARCH]) {
10947+
return self.parse_alter_text_search().map(Into::into);
10948+
}
10949+
1080010950
let object_type = self.expect_one_of_keywords(&[
1080110951
Keyword::VIEW,
1080210952
Keyword::TYPE,
@@ -10862,7 +11012,7 @@ impl<'a> Parser<'a> {
1086211012
Keyword::USER => self.parse_alter_user().map(Into::into),
1086311013
// unreachable because expect_one_of_keywords used above
1086411014
unexpected_keyword => Err(ParserError::ParserError(
10865-
format!("Internal parser error: expected any of {{VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR}}, got {unexpected_keyword:?}"),
11015+
format!("Internal parser error: expected any of {{TEXT SEARCH, VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR}}, got {unexpected_keyword:?}"),
1086611016
)),
1086711017
}
1086811018
}

0 commit comments

Comments
 (0)