mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-22 05:32:29 +00:00
feat: add FULLTEXT option on create table for MySQL and Generic dialects (#702)
This commit is contained in:
parent
87b4a168cb
commit
cdf4447065
5 changed files with 161 additions and 6 deletions
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -255,6 +255,7 @@ define_keywords!(
|
|||
FREEZE,
|
||||
FROM,
|
||||
FULL,
|
||||
FULLTEXT,
|
||||
FUNCTION,
|
||||
FUNCTIONS,
|
||||
FUSION,
|
||||
|
@ -498,6 +499,7 @@ define_keywords!(
|
|||
SNAPSHOT,
|
||||
SOME,
|
||||
SORT,
|
||||
SPATIAL,
|
||||
SPECIFIC,
|
||||
SPECIFICTYPE,
|
||||
SQL,
|
||||
|
|
|
@ -3085,6 +3085,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)
|
||||
|
|
|
@ -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() {
|
||||
|
@ -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 {})],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue