mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-08 01:15:00 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
b42f58dfec
8 changed files with 293 additions and 45 deletions
|
@ -30,11 +30,11 @@ use sqlparser_derive::{Visit, VisitMut};
|
||||||
|
|
||||||
use crate::ast::value::escape_single_quote_string;
|
use crate::ast::value::escape_single_quote_string;
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
display_comma_separated, display_separated, CommentDef, CreateFunctionBody,
|
display_comma_separated, display_separated, ArgMode, CommentDef, CreateFunctionBody,
|
||||||
CreateFunctionUsing, DataType, Expr, FunctionBehavior, FunctionCalledOnNull,
|
CreateFunctionUsing, DataType, Expr, FunctionBehavior, FunctionCalledOnNull,
|
||||||
FunctionDeterminismSpecifier, FunctionParallel, Ident, MySQLColumnPosition, ObjectName,
|
FunctionDeterminismSpecifier, FunctionParallel, Ident, IndexColumn, MySQLColumnPosition,
|
||||||
OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag, Value,
|
ObjectName, OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag,
|
||||||
ValueWithSpan,
|
Value, ValueWithSpan,
|
||||||
};
|
};
|
||||||
use crate::keywords::Keyword;
|
use crate::keywords::Keyword;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
|
@ -979,7 +979,7 @@ pub enum TableConstraint {
|
||||||
/// [1]: IndexType
|
/// [1]: IndexType
|
||||||
index_type: Option<IndexType>,
|
index_type: Option<IndexType>,
|
||||||
/// Identifiers of the columns that are unique.
|
/// Identifiers of the columns that are unique.
|
||||||
columns: Vec<Ident>,
|
columns: Vec<IndexColumn>,
|
||||||
index_options: Vec<IndexOption>,
|
index_options: Vec<IndexOption>,
|
||||||
characteristics: Option<ConstraintCharacteristics>,
|
characteristics: Option<ConstraintCharacteristics>,
|
||||||
/// Optional Postgres nulls handling: `[ NULLS [ NOT ] DISTINCT ]`
|
/// Optional Postgres nulls handling: `[ NULLS [ NOT ] DISTINCT ]`
|
||||||
|
@ -1015,7 +1015,7 @@ pub enum TableConstraint {
|
||||||
/// [1]: IndexType
|
/// [1]: IndexType
|
||||||
index_type: Option<IndexType>,
|
index_type: Option<IndexType>,
|
||||||
/// Identifiers of the columns that form the primary key.
|
/// Identifiers of the columns that form the primary key.
|
||||||
columns: Vec<Ident>,
|
columns: Vec<IndexColumn>,
|
||||||
index_options: Vec<IndexOption>,
|
index_options: Vec<IndexOption>,
|
||||||
characteristics: Option<ConstraintCharacteristics>,
|
characteristics: Option<ConstraintCharacteristics>,
|
||||||
},
|
},
|
||||||
|
@ -1060,7 +1060,7 @@ pub enum TableConstraint {
|
||||||
/// [1]: IndexType
|
/// [1]: IndexType
|
||||||
index_type: Option<IndexType>,
|
index_type: Option<IndexType>,
|
||||||
/// Referred column identifier list.
|
/// Referred column identifier list.
|
||||||
columns: Vec<Ident>,
|
columns: Vec<IndexColumn>,
|
||||||
},
|
},
|
||||||
/// MySQLs [fulltext][1] definition. Since the [`SPATIAL`][2] definition is exactly the same,
|
/// 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.
|
/// and MySQL displays both the same way, it is part of this definition as well.
|
||||||
|
@ -1083,7 +1083,7 @@ pub enum TableConstraint {
|
||||||
/// Optional index name.
|
/// Optional index name.
|
||||||
opt_index_name: Option<Ident>,
|
opt_index_name: Option<Ident>,
|
||||||
/// Referred column identifier list.
|
/// Referred column identifier list.
|
||||||
columns: Vec<Ident>,
|
columns: Vec<IndexColumn>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1367,11 +1367,16 @@ impl fmt::Display for NullsDistinctOption {
|
||||||
pub struct ProcedureParam {
|
pub struct ProcedureParam {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub data_type: DataType,
|
pub data_type: DataType,
|
||||||
|
pub mode: Option<ArgMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ProcedureParam {
|
impl fmt::Display for ProcedureParam {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{} {}", self.name, self.data_type)
|
if let Some(mode) = &self.mode {
|
||||||
|
write!(f, "{mode} {} {}", self.name, self.data_type)
|
||||||
|
} else {
|
||||||
|
write!(f, "{} {}", self.name, self.data_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,16 +28,17 @@ use super::{
|
||||||
ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte,
|
ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte,
|
||||||
Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable,
|
Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable,
|
||||||
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
|
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
|
||||||
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, Insert, Interpolate,
|
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert,
|
||||||
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
|
Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem,
|
||||||
LimitClause, MatchRecognizePattern, Measure, NamedParenthesizedList, NamedWindowDefinition,
|
LateralView, LimitClause, MatchRecognizePattern, Measure, NamedParenthesizedList,
|
||||||
ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement,
|
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
|
||||||
OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource, ProjectionSelect, Query,
|
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource,
|
||||||
RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
|
ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction,
|
||||||
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
|
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
|
||||||
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
|
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
|
||||||
TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use, Value, Values, ViewColumnDef,
|
TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins,
|
||||||
WhileStatement, WildcardAdditionalOptions, With, WithFill,
|
UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement,
|
||||||
|
WildcardAdditionalOptions, With, WithFill,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Given an iterator of spans, return the [Span::union] of all spans.
|
/// Given an iterator of spans, return the [Span::union] of all spans.
|
||||||
|
@ -650,7 +651,7 @@ impl Spanned for TableConstraint {
|
||||||
name.iter()
|
name.iter()
|
||||||
.map(|i| i.span)
|
.map(|i| i.span)
|
||||||
.chain(index_name.iter().map(|i| i.span))
|
.chain(index_name.iter().map(|i| i.span))
|
||||||
.chain(columns.iter().map(|i| i.span))
|
.chain(columns.iter().map(|i| i.span()))
|
||||||
.chain(characteristics.iter().map(|i| i.span())),
|
.chain(characteristics.iter().map(|i| i.span())),
|
||||||
),
|
),
|
||||||
TableConstraint::PrimaryKey {
|
TableConstraint::PrimaryKey {
|
||||||
|
@ -664,7 +665,7 @@ impl Spanned for TableConstraint {
|
||||||
name.iter()
|
name.iter()
|
||||||
.map(|i| i.span)
|
.map(|i| i.span)
|
||||||
.chain(index_name.iter().map(|i| i.span))
|
.chain(index_name.iter().map(|i| i.span))
|
||||||
.chain(columns.iter().map(|i| i.span))
|
.chain(columns.iter().map(|i| i.span()))
|
||||||
.chain(characteristics.iter().map(|i| i.span())),
|
.chain(characteristics.iter().map(|i| i.span())),
|
||||||
),
|
),
|
||||||
TableConstraint::ForeignKey {
|
TableConstraint::ForeignKey {
|
||||||
|
@ -700,7 +701,7 @@ impl Spanned for TableConstraint {
|
||||||
} => union_spans(
|
} => union_spans(
|
||||||
name.iter()
|
name.iter()
|
||||||
.map(|i| i.span)
|
.map(|i| i.span)
|
||||||
.chain(columns.iter().map(|i| i.span)),
|
.chain(columns.iter().map(|i| i.span())),
|
||||||
),
|
),
|
||||||
TableConstraint::FulltextOrSpatial {
|
TableConstraint::FulltextOrSpatial {
|
||||||
fulltext: _,
|
fulltext: _,
|
||||||
|
@ -711,7 +712,7 @@ impl Spanned for TableConstraint {
|
||||||
opt_index_name
|
opt_index_name
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| i.span)
|
.map(|i| i.span)
|
||||||
.chain(columns.iter().map(|i| i.span)),
|
.chain(columns.iter().map(|i| i.span())),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -745,6 +746,12 @@ impl Spanned for CreateIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Spanned for IndexColumn {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.column.span()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Spanned for CaseStatement {
|
impl Spanned for CaseStatement {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
let CaseStatement {
|
let CaseStatement {
|
||||||
|
|
|
@ -6868,9 +6868,7 @@ impl<'a> Parser<'a> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
self.expect_token(&Token::LParen)?;
|
let columns = self.parse_parenthesized_index_column_list()?;
|
||||||
let columns = self.parse_comma_separated(Parser::parse_create_index_expr)?;
|
|
||||||
self.expect_token(&Token::RParen)?;
|
|
||||||
|
|
||||||
let include = if self.parse_keyword(Keyword::INCLUDE) {
|
let include = if self.parse_keyword(Keyword::INCLUDE) {
|
||||||
self.expect_token(&Token::LParen)?;
|
self.expect_token(&Token::LParen)?;
|
||||||
|
@ -7626,9 +7624,22 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_procedure_param(&mut self) -> Result<ProcedureParam, ParserError> {
|
pub fn parse_procedure_param(&mut self) -> Result<ProcedureParam, ParserError> {
|
||||||
|
let mode = if self.parse_keyword(Keyword::IN) {
|
||||||
|
Some(ArgMode::In)
|
||||||
|
} else if self.parse_keyword(Keyword::OUT) {
|
||||||
|
Some(ArgMode::Out)
|
||||||
|
} else if self.parse_keyword(Keyword::INOUT) {
|
||||||
|
Some(ArgMode::InOut)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let name = self.parse_identifier()?;
|
let name = self.parse_identifier()?;
|
||||||
let data_type = self.parse_data_type()?;
|
let data_type = self.parse_data_type()?;
|
||||||
Ok(ProcedureParam { name, data_type })
|
Ok(ProcedureParam {
|
||||||
|
name,
|
||||||
|
data_type,
|
||||||
|
mode,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_column_def(&mut self) -> Result<ColumnDef, ParserError> {
|
pub fn parse_column_def(&mut self) -> Result<ColumnDef, ParserError> {
|
||||||
|
@ -8070,7 +8081,7 @@ impl<'a> Parser<'a> {
|
||||||
let index_name = self.parse_optional_ident()?;
|
let index_name = self.parse_optional_ident()?;
|
||||||
let index_type = self.parse_optional_using_then_index_type()?;
|
let index_type = self.parse_optional_using_then_index_type()?;
|
||||||
|
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
let columns = self.parse_parenthesized_index_column_list()?;
|
||||||
let index_options = self.parse_index_options()?;
|
let index_options = self.parse_index_options()?;
|
||||||
let characteristics = self.parse_constraint_characteristics()?;
|
let characteristics = self.parse_constraint_characteristics()?;
|
||||||
Ok(Some(TableConstraint::Unique {
|
Ok(Some(TableConstraint::Unique {
|
||||||
|
@ -8092,7 +8103,7 @@ impl<'a> Parser<'a> {
|
||||||
let index_name = self.parse_optional_ident()?;
|
let index_name = self.parse_optional_ident()?;
|
||||||
let index_type = self.parse_optional_using_then_index_type()?;
|
let index_type = self.parse_optional_using_then_index_type()?;
|
||||||
|
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
let columns = self.parse_parenthesized_index_column_list()?;
|
||||||
let index_options = self.parse_index_options()?;
|
let index_options = self.parse_index_options()?;
|
||||||
let characteristics = self.parse_constraint_characteristics()?;
|
let characteristics = self.parse_constraint_characteristics()?;
|
||||||
Ok(Some(TableConstraint::PrimaryKey {
|
Ok(Some(TableConstraint::PrimaryKey {
|
||||||
|
@ -8170,7 +8181,7 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let index_type = self.parse_optional_using_then_index_type()?;
|
let index_type = self.parse_optional_using_then_index_type()?;
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
let columns = self.parse_parenthesized_index_column_list()?;
|
||||||
|
|
||||||
Ok(Some(TableConstraint::Index {
|
Ok(Some(TableConstraint::Index {
|
||||||
display_as_key,
|
display_as_key,
|
||||||
|
@ -8199,7 +8210,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
let opt_index_name = self.parse_optional_ident()?;
|
let opt_index_name = self.parse_optional_ident()?;
|
||||||
|
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
let columns = self.parse_parenthesized_index_column_list()?;
|
||||||
|
|
||||||
Ok(Some(TableConstraint::FulltextOrSpatial {
|
Ok(Some(TableConstraint::FulltextOrSpatial {
|
||||||
fulltext,
|
fulltext,
|
||||||
|
@ -10601,6 +10612,14 @@ impl<'a> Parser<'a> {
|
||||||
self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| p.parse_identifier())
|
self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| p.parse_identifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a parenthesized comma-separated list of index columns, which can be arbitrary
|
||||||
|
/// expressions with ordering information (and an opclass in some dialects).
|
||||||
|
fn parse_parenthesized_index_column_list(&mut self) -> Result<Vec<IndexColumn>, ParserError> {
|
||||||
|
self.parse_parenthesized_column_list_inner(Mandatory, false, |p| {
|
||||||
|
p.parse_create_index_expr()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a parenthesized comma-separated list of qualified, possibly quoted identifiers.
|
/// Parses a parenthesized comma-separated list of qualified, possibly quoted identifiers.
|
||||||
/// For example: `(db1.sc1.tbl1.col1, db1.sc1.tbl1."col 2", ...)`
|
/// For example: `(db1.sc1.tbl1.col1, db1.sc1.tbl1."col 2", ...)`
|
||||||
pub fn parse_parenthesized_qualified_column_list(
|
pub fn parse_parenthesized_qualified_column_list(
|
||||||
|
@ -15017,7 +15036,8 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
/// Parse a FETCH clause
|
/// Parse a FETCH clause
|
||||||
pub fn parse_fetch(&mut self) -> Result<Fetch, ParserError> {
|
pub fn parse_fetch(&mut self) -> Result<Fetch, ParserError> {
|
||||||
self.expect_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT])?;
|
let _ = self.parse_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT]);
|
||||||
|
|
||||||
let (quantity, percent) = if self
|
let (quantity, percent) = if self
|
||||||
.parse_one_of_keywords(&[Keyword::ROW, Keyword::ROWS])
|
.parse_one_of_keywords(&[Keyword::ROW, Keyword::ROWS])
|
||||||
.is_some()
|
.is_some()
|
||||||
|
@ -15026,16 +15046,16 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
let quantity = Expr::Value(self.parse_value()?);
|
let quantity = Expr::Value(self.parse_value()?);
|
||||||
let percent = self.parse_keyword(Keyword::PERCENT);
|
let percent = self.parse_keyword(Keyword::PERCENT);
|
||||||
self.expect_one_of_keywords(&[Keyword::ROW, Keyword::ROWS])?;
|
let _ = self.parse_one_of_keywords(&[Keyword::ROW, Keyword::ROWS]);
|
||||||
(Some(quantity), percent)
|
(Some(quantity), percent)
|
||||||
};
|
};
|
||||||
|
|
||||||
let with_ties = if self.parse_keyword(Keyword::ONLY) {
|
let with_ties = if self.parse_keyword(Keyword::ONLY) {
|
||||||
false
|
false
|
||||||
} else if self.parse_keywords(&[Keyword::WITH, Keyword::TIES]) {
|
|
||||||
true
|
|
||||||
} else {
|
} else {
|
||||||
return self.expected("one of ONLY or WITH TIES", self.peek_token());
|
self.parse_keywords(&[Keyword::WITH, Keyword::TIES])
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Fetch {
|
Ok(Fetch {
|
||||||
with_ties,
|
with_ties,
|
||||||
percent,
|
percent,
|
||||||
|
@ -16527,6 +16547,20 @@ mod tests {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mk_expected_col(name: &str) -> IndexColumn {
|
||||||
|
IndexColumn {
|
||||||
|
column: OrderByExpr {
|
||||||
|
expr: Expr::Identifier(name.into()),
|
||||||
|
options: OrderByOptions {
|
||||||
|
asc: None,
|
||||||
|
nulls_first: None,
|
||||||
|
},
|
||||||
|
with_fill: None,
|
||||||
|
},
|
||||||
|
operator_class: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let dialect =
|
let dialect =
|
||||||
TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(MySqlDialect {})]);
|
TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(MySqlDialect {})]);
|
||||||
|
|
||||||
|
@ -16537,7 +16571,7 @@ mod tests {
|
||||||
display_as_key: false,
|
display_as_key: false,
|
||||||
name: None,
|
name: None,
|
||||||
index_type: None,
|
index_type: None,
|
||||||
columns: vec![Ident::new("c1")],
|
columns: vec![mk_expected_col("c1")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16548,7 +16582,7 @@ mod tests {
|
||||||
display_as_key: true,
|
display_as_key: true,
|
||||||
name: None,
|
name: None,
|
||||||
index_type: None,
|
index_type: None,
|
||||||
columns: vec![Ident::new("c1")],
|
columns: vec![mk_expected_col("c1")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16559,7 +16593,7 @@ mod tests {
|
||||||
display_as_key: false,
|
display_as_key: false,
|
||||||
name: Some(Ident::with_quote('\'', "index")),
|
name: Some(Ident::with_quote('\'', "index")),
|
||||||
index_type: None,
|
index_type: None,
|
||||||
columns: vec![Ident::new("c1"), Ident::new("c2")],
|
columns: vec![mk_expected_col("c1"), mk_expected_col("c2")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16570,7 +16604,7 @@ mod tests {
|
||||||
display_as_key: false,
|
display_as_key: false,
|
||||||
name: None,
|
name: None,
|
||||||
index_type: Some(IndexType::BTree),
|
index_type: Some(IndexType::BTree),
|
||||||
columns: vec![Ident::new("c1")],
|
columns: vec![mk_expected_col("c1")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16581,7 +16615,7 @@ mod tests {
|
||||||
display_as_key: false,
|
display_as_key: false,
|
||||||
name: None,
|
name: None,
|
||||||
index_type: Some(IndexType::Hash),
|
index_type: Some(IndexType::Hash),
|
||||||
columns: vec![Ident::new("c1")],
|
columns: vec![mk_expected_col("c1")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16592,7 +16626,7 @@ mod tests {
|
||||||
display_as_key: false,
|
display_as_key: false,
|
||||||
name: Some(Ident::new("idx_name")),
|
name: Some(Ident::new("idx_name")),
|
||||||
index_type: Some(IndexType::BTree),
|
index_type: Some(IndexType::BTree),
|
||||||
columns: vec![Ident::new("c1")],
|
columns: vec![mk_expected_col("c1")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16603,7 +16637,7 @@ mod tests {
|
||||||
display_as_key: false,
|
display_as_key: false,
|
||||||
name: Some(Ident::new("idx_name")),
|
name: Some(Ident::new("idx_name")),
|
||||||
index_type: Some(IndexType::Hash),
|
index_type: Some(IndexType::Hash),
|
||||||
columns: vec![Ident::new("c1")],
|
columns: vec![mk_expected_col("c1")],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,3 +448,47 @@ pub fn call(function: &str, args: impl IntoIterator<Item = Expr>) -> Expr {
|
||||||
within_group: vec![],
|
within_group: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the first index column (mysql calls it a key part) of the first index found in a
|
||||||
|
/// [`Statement::CreateIndex`], [`Statement::CreateTable`], or [`Statement::AlterTable`].
|
||||||
|
pub fn index_column(stmt: Statement) -> Expr {
|
||||||
|
match stmt {
|
||||||
|
Statement::CreateIndex(CreateIndex { columns, .. }) => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
Statement::CreateTable(CreateTable { constraints, .. }) => {
|
||||||
|
match constraints.first().unwrap() {
|
||||||
|
TableConstraint::Index { columns, .. } => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
TableConstraint::Unique { columns, .. } => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
TableConstraint::PrimaryKey { columns, .. } => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
TableConstraint::FulltextOrSpatial { columns, .. } => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Statement::AlterTable { operations, .. } => match operations.first().unwrap() {
|
||||||
|
AlterTableOperation::AddConstraint(TableConstraint::Index { columns, .. }) => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
AlterTableOperation::AddConstraint(TableConstraint::Unique { columns, .. }) => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
AlterTableOperation::AddConstraint(TableConstraint::PrimaryKey { columns, .. }) => {
|
||||||
|
columns.first().unwrap().column.expr.clone()
|
||||||
|
}
|
||||||
|
AlterTableOperation::AddConstraint(TableConstraint::FulltextOrSpatial {
|
||||||
|
columns,
|
||||||
|
..
|
||||||
|
}) => columns.first().unwrap().column.expr.clone(),
|
||||||
|
_ => panic!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"),
|
||||||
|
},
|
||||||
|
_ => panic!("Expected CREATE INDEX, ALTER TABLE, or CREATE TABLE, got: {stmt:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15378,3 +15378,65 @@ fn join_precedence() {
|
||||||
"SELECT * FROM t1 NATURAL JOIN (t5 INNER JOIN t0 ON (t0.v1 + t5.v0) > 0) WHERE t0.v1 = t1.v0",
|
"SELECT * FROM t1 NATURAL JOIN (t5 INNER JOIN t0 ON (t0.v1 + t5.v0) > 0) WHERE t0.v1 = t1.v0",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_create_procedure_with_parameter_modes() {
|
||||||
|
let sql = r#"CREATE PROCEDURE test_proc (IN a INTEGER, OUT b TEXT, INOUT c TIMESTAMP, d BOOL) AS BEGIN SELECT 1; END"#;
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
Statement::CreateProcedure {
|
||||||
|
or_alter,
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(or_alter, false);
|
||||||
|
assert_eq!(name.to_string(), "test_proc");
|
||||||
|
let fake_span = Span {
|
||||||
|
start: Location { line: 0, column: 0 },
|
||||||
|
end: Location { line: 0, column: 0 },
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
params,
|
||||||
|
Some(vec![
|
||||||
|
ProcedureParam {
|
||||||
|
name: Ident {
|
||||||
|
value: "a".into(),
|
||||||
|
quote_style: None,
|
||||||
|
span: fake_span,
|
||||||
|
},
|
||||||
|
data_type: DataType::Integer(None),
|
||||||
|
mode: Some(ArgMode::In)
|
||||||
|
},
|
||||||
|
ProcedureParam {
|
||||||
|
name: Ident {
|
||||||
|
value: "b".into(),
|
||||||
|
quote_style: None,
|
||||||
|
span: fake_span,
|
||||||
|
},
|
||||||
|
data_type: DataType::Text,
|
||||||
|
mode: Some(ArgMode::Out)
|
||||||
|
},
|
||||||
|
ProcedureParam {
|
||||||
|
name: Ident {
|
||||||
|
value: "c".into(),
|
||||||
|
quote_style: None,
|
||||||
|
span: fake_span,
|
||||||
|
},
|
||||||
|
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
|
mode: Some(ArgMode::InOut)
|
||||||
|
},
|
||||||
|
ProcedureParam {
|
||||||
|
name: Ident {
|
||||||
|
value: "d".into(),
|
||||||
|
quote_style: None,
|
||||||
|
span: fake_span,
|
||||||
|
},
|
||||||
|
data_type: DataType::Bool,
|
||||||
|
mode: None
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -153,7 +153,8 @@ fn parse_create_procedure() {
|
||||||
quote_style: None,
|
quote_style: None,
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
data_type: DataType::Int(None)
|
data_type: DataType::Int(None),
|
||||||
|
mode: None,
|
||||||
},
|
},
|
||||||
ProcedureParam {
|
ProcedureParam {
|
||||||
name: Ident {
|
name: Ident {
|
||||||
|
@ -164,7 +165,8 @@ fn parse_create_procedure() {
|
||||||
data_type: DataType::Varchar(Some(CharacterLength::IntegerLength {
|
data_type: DataType::Varchar(Some(CharacterLength::IntegerLength {
|
||||||
length: 256,
|
length: 256,
|
||||||
unit: None
|
unit: None
|
||||||
}))
|
})),
|
||||||
|
mode: None,
|
||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
name: ObjectName::from(vec![Ident {
|
name: ObjectName::from(vec![Ident {
|
||||||
|
|
|
@ -670,6 +670,20 @@ fn table_constraint_unique_primary_ctor(
|
||||||
characteristics: Option<ConstraintCharacteristics>,
|
characteristics: Option<ConstraintCharacteristics>,
|
||||||
unique_index_type_display: Option<KeyOrIndexDisplay>,
|
unique_index_type_display: Option<KeyOrIndexDisplay>,
|
||||||
) -> TableConstraint {
|
) -> TableConstraint {
|
||||||
|
let columns = columns
|
||||||
|
.into_iter()
|
||||||
|
.map(|ident| IndexColumn {
|
||||||
|
column: OrderByExpr {
|
||||||
|
expr: Expr::Identifier(ident),
|
||||||
|
options: OrderByOptions {
|
||||||
|
asc: None,
|
||||||
|
nulls_first: None,
|
||||||
|
},
|
||||||
|
with_fill: None,
|
||||||
|
},
|
||||||
|
operator_class: None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
match unique_index_type_display {
|
match unique_index_type_display {
|
||||||
Some(index_type_display) => TableConstraint::Unique {
|
Some(index_type_display) => TableConstraint::Unique {
|
||||||
name,
|
name,
|
||||||
|
@ -795,6 +809,67 @@ fn parse_create_table_primary_and_unique_key_with_index_options() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_prefix_key_part() {
|
||||||
|
let expected = vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::value(
|
||||||
|
number("10"),
|
||||||
|
)))];
|
||||||
|
for sql in [
|
||||||
|
"CREATE INDEX idx_index ON t(textcol(10))",
|
||||||
|
"ALTER TABLE tab ADD INDEX idx_index (textcol(10))",
|
||||||
|
"ALTER TABLE tab ADD PRIMARY KEY (textcol(10))",
|
||||||
|
"ALTER TABLE tab ADD UNIQUE KEY (textcol(10))",
|
||||||
|
"ALTER TABLE tab ADD UNIQUE KEY (textcol(10))",
|
||||||
|
"ALTER TABLE tab ADD FULLTEXT INDEX (textcol(10))",
|
||||||
|
"CREATE TABLE t (textcol TEXT, INDEX idx_index (textcol(10)))",
|
||||||
|
] {
|
||||||
|
match index_column(mysql_and_generic().verified_stmt(sql)) {
|
||||||
|
Expr::Function(Function {
|
||||||
|
name,
|
||||||
|
args: FunctionArguments::List(FunctionArgumentList { args, .. }),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
assert_eq!(name.to_string(), "textcol");
|
||||||
|
assert_eq!(args, expected);
|
||||||
|
}
|
||||||
|
expr => panic!("unexpected expression {expr} for {sql}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_functional_key_part() {
|
||||||
|
assert_eq!(
|
||||||
|
index_column(
|
||||||
|
mysql_and_generic()
|
||||||
|
.verified_stmt("CREATE INDEX idx_index ON t((col COLLATE utf8mb4_bin) DESC)")
|
||||||
|
),
|
||||||
|
Expr::Nested(Box::new(Expr::Collate {
|
||||||
|
expr: Box::new(Expr::Identifier("col".into())),
|
||||||
|
collation: ObjectName(vec![sqlparser::ast::ObjectNamePart::Identifier(
|
||||||
|
Ident::new("utf8mb4_bin")
|
||||||
|
)]),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
index_column(mysql_and_generic().verified_stmt(
|
||||||
|
r#"CREATE TABLE t (jsoncol JSON, PRIMARY KEY ((CAST(col ->> '$.id' AS UNSIGNED)) ASC))"#
|
||||||
|
)),
|
||||||
|
Expr::Nested(Box::new(Expr::Cast {
|
||||||
|
kind: CastKind::Cast,
|
||||||
|
expr: Box::new(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::new("col"))),
|
||||||
|
op: BinaryOperator::LongArrow,
|
||||||
|
right: Box::new(Expr::Value(
|
||||||
|
Value::SingleQuotedString("$.id".to_string()).with_empty_span()
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
data_type: DataType::Unsigned,
|
||||||
|
format: None,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_create_table_primary_and_unique_key_with_index_type() {
|
fn parse_create_table_primary_and_unique_key_with_index_type() {
|
||||||
let sqls = ["UNIQUE", "PRIMARY KEY"].map(|key_ty| {
|
let sqls = ["UNIQUE", "PRIMARY KEY"].map(|key_ty| {
|
||||||
|
|
|
@ -4146,3 +4146,22 @@ END
|
||||||
assert_eq!(2, exception[1].idents.len());
|
assert_eq!(2, exception[1].idents.len());
|
||||||
assert_eq!(2, exception[1].statements.len());
|
assert_eq!(2, exception[1].statements.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_fetch_clause_syntax() {
|
||||||
|
let canonical = "SELECT c1 FROM fetch_test FETCH FIRST 2 ROWS ONLY";
|
||||||
|
snowflake().verified_only_select_with_canonical("SELECT c1 FROM fetch_test FETCH 2", canonical);
|
||||||
|
|
||||||
|
snowflake()
|
||||||
|
.verified_only_select_with_canonical("SELECT c1 FROM fetch_test FETCH FIRST 2", canonical);
|
||||||
|
snowflake()
|
||||||
|
.verified_only_select_with_canonical("SELECT c1 FROM fetch_test FETCH NEXT 2", canonical);
|
||||||
|
|
||||||
|
snowflake()
|
||||||
|
.verified_only_select_with_canonical("SELECT c1 FROM fetch_test FETCH 2 ROW", canonical);
|
||||||
|
|
||||||
|
snowflake().verified_only_select_with_canonical(
|
||||||
|
"SELECT c1 FROM fetch_test FETCH FIRST 2 ROWS",
|
||||||
|
canonical,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue