Skip to content

Commit

Permalink
feat: add FULLTEXT option on create table for MySQL and Generic dialects
Browse files Browse the repository at this point in the history
  • Loading branch information
AugustoFKL committed Nov 9, 2022
1 parent 0f7e144 commit f633d73
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 6 deletions.
80 changes: 80 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,28 @@ pub enum TableConstraint {
/// Referred column identifier list.
columns: Vec<Ident>,
},
/// MySQLs [fulltext][1] definition. Since the [`SPATIAL`][2] definition is exactly the same,
/// and MySQL displays both the same way, it is part of this definition as well.
///
/// Supported syntax:
///
/// ```markdown
/// {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] (key_part,...)
///
/// key_part: col_name
/// ```
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
FulltextOrSpatial {
/// Whether this is a `FULLTEXT` (true) or `SPATIAL` (false) definition.
fulltext: bool,
/// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
index_type_display: KeyOrIndexDisplay,
/// Optional index name.
opt_index_name: Option<Ident>,
/// Referred column identifier list.
columns: Vec<Ident>,
},
}

impl fmt::Display for TableConstraint {
Expand Down Expand Up @@ -330,6 +352,64 @@ impl fmt::Display for TableConstraint {

Ok(())
}
Self::FulltextOrSpatial {
fulltext,
index_type_display,
opt_index_name,
columns,
} => {
if *fulltext {
write!(f, "FULLTEXT")?;
} else {
write!(f, "SPATIAL")?;
}

if !matches!(index_type_display, KeyOrIndexDisplay::None) {
write!(f, " {}", index_type_display)?;
}

if let Some(name) = opt_index_name {
write!(f, " {}", name)?;
}

write!(f, " ({})", display_comma_separated(columns))?;

Ok(())
}
}
}
}

/// Representation whether a definition can can contains the KEY or INDEX keywords with the same
/// meaning.
///
/// This enum initially is directed to `FULLTEXT`,`SPATIAL`, and `UNIQUE` indexes on create table
/// statements of `MySQL` [(1)].
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum KeyOrIndexDisplay {
/// Nothing to display
None,
/// Display the KEY keyword
Key,
/// Display the INDEX keyword
Index,
}

impl fmt::Display for KeyOrIndexDisplay {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
KeyOrIndexDisplay::None => {
write!(f, "")
}
KeyOrIndexDisplay::Key => {
write!(f, "KEY")
}
KeyOrIndexDisplay::Index => {
write!(f, "INDEX")
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub use self::data_type::{
};
pub use self::ddl::{
AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, IndexType,
ReferentialAction, TableConstraint,
KeyOrIndexDisplay, ReferentialAction, TableConstraint,
};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ define_keywords!(
FREEZE,
FROM,
FULL,
FULLTEXT,
FUNCTION,
FUNCTIONS,
FUSION,
Expand Down Expand Up @@ -498,6 +499,7 @@ define_keywords!(
SNAPSHOT,
SOME,
SORT,
SPATIAL,
SPECIFIC,
SPECIFICTYPE,
SQL,
Expand Down
32 changes: 32 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3036,6 +3036,38 @@ impl<'a> Parser<'a> {
columns,
}))
}
Token::Word(w)
if (w.keyword == Keyword::FULLTEXT || w.keyword == Keyword::SPATIAL)
&& dialect_of!(self is GenericDialect | MySqlDialect) =>
{
if let Some(name) = name {
return self.expected(
"FULLTEXT or SPATIAL option without constraint name",
Token::make_keyword(&name.to_string()),
);
}

let fulltext = w.keyword == Keyword::FULLTEXT;

let index_type_display = if self.parse_keyword(Keyword::KEY) {
KeyOrIndexDisplay::Key
} else if self.parse_keyword(Keyword::INDEX) {
KeyOrIndexDisplay::Index
} else {
KeyOrIndexDisplay::None
};

let opt_index_name = self.maybe_parse(|parser| parser.parse_identifier());

let columns = self.parse_parenthesized_column_list(Mandatory)?;

Ok(Some(TableConstraint::FulltextOrSpatial {
fulltext,
index_type_display,
opt_index_name,
columns,
}))
}
unexpected => {
if name.is_some() {
self.expected("PRIMARY, UNIQUE, FOREIGN, or CHECK", unexpected)
Expand Down
51 changes: 46 additions & 5 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@
//! Test SQL syntax specific to MySQL. The parser based on the generic dialect
//! is also tested (on the inputs it can handle).

#[macro_use]
mod test_utils;

use test_utils::*;

use sqlparser::ast::Expr;
use sqlparser::ast::Value;
use sqlparser::ast::*;
use sqlparser::dialect::{GenericDialect, MySqlDialect};
use sqlparser::tokenizer::Token;
use test_utils::*;

#[macro_use]
mod test_utils;

#[test]
fn parse_identifiers() {
Expand Down Expand Up @@ -1129,6 +1128,48 @@ fn parse_create_table_with_index_definition() {
);
}

#[test]
fn parse_create_table_with_fulltext_definition() {
mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT INDEX (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT KEY (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT potato (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT INDEX potato (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, FULLTEXT KEY potato (id))");

mysql_and_generic()
.verified_stmt("CREATE TABLE tb (c1 INT, c2 INT, FULLTEXT KEY potato (c1, c2))");
}

#[test]
fn parse_create_table_with_spatial_definition() {
mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, SPATIAL (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, SPATIAL INDEX (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, SPATIAL KEY (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, SPATIAL potato (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, SPATIAL INDEX potato (id))");

mysql_and_generic().verified_stmt("CREATE TABLE tb (id INT, SPATIAL KEY potato (id))");

mysql_and_generic()
.verified_stmt("CREATE TABLE tb (c1 INT, c2 INT, SPATIAL KEY potato (c1, c2))");
}

#[test]
#[should_panic = "Expected FULLTEXT or SPATIAL option without constraint name, found: cons"]
fn parse_create_table_with_fulltext_definition_should_not_accept_constraint_name() {
mysql_and_generic().verified_stmt("CREATE TABLE tb (c1 INT, CONSTRAINT cons FULLTEXT (c1))");
}

fn mysql() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
Expand Down

0 comments on commit f633d73

Please sign in to comment.