mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +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::{
|
||||
display_comma_separated, display_separated, CommentDef, CreateFunctionBody,
|
||||
display_comma_separated, display_separated, ArgMode, CommentDef, CreateFunctionBody,
|
||||
CreateFunctionUsing, DataType, Expr, FunctionBehavior, FunctionCalledOnNull,
|
||||
FunctionDeterminismSpecifier, FunctionParallel, Ident, MySQLColumnPosition, ObjectName,
|
||||
OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag, Value,
|
||||
ValueWithSpan,
|
||||
FunctionDeterminismSpecifier, FunctionParallel, Ident, IndexColumn, MySQLColumnPosition,
|
||||
ObjectName, OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag,
|
||||
Value, ValueWithSpan,
|
||||
};
|
||||
use crate::keywords::Keyword;
|
||||
use crate::tokenizer::Token;
|
||||
|
@ -979,7 +979,7 @@ pub enum TableConstraint {
|
|||
/// [1]: IndexType
|
||||
index_type: Option<IndexType>,
|
||||
/// Identifiers of the columns that are unique.
|
||||
columns: Vec<Ident>,
|
||||
columns: Vec<IndexColumn>,
|
||||
index_options: Vec<IndexOption>,
|
||||
characteristics: Option<ConstraintCharacteristics>,
|
||||
/// Optional Postgres nulls handling: `[ NULLS [ NOT ] DISTINCT ]`
|
||||
|
@ -1015,7 +1015,7 @@ pub enum TableConstraint {
|
|||
/// [1]: IndexType
|
||||
index_type: Option<IndexType>,
|
||||
/// Identifiers of the columns that form the primary key.
|
||||
columns: Vec<Ident>,
|
||||
columns: Vec<IndexColumn>,
|
||||
index_options: Vec<IndexOption>,
|
||||
characteristics: Option<ConstraintCharacteristics>,
|
||||
},
|
||||
|
@ -1060,7 +1060,7 @@ pub enum TableConstraint {
|
|||
/// [1]: IndexType
|
||||
index_type: Option<IndexType>,
|
||||
/// Referred column identifier list.
|
||||
columns: Vec<Ident>,
|
||||
columns: Vec<IndexColumn>,
|
||||
},
|
||||
/// 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.
|
||||
|
@ -1083,7 +1083,7 @@ pub enum TableConstraint {
|
|||
/// Optional index name.
|
||||
opt_index_name: Option<Ident>,
|
||||
/// Referred column identifier list.
|
||||
columns: Vec<Ident>,
|
||||
columns: Vec<IndexColumn>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1367,11 +1367,16 @@ impl fmt::Display for NullsDistinctOption {
|
|||
pub struct ProcedureParam {
|
||||
pub name: Ident,
|
||||
pub data_type: DataType,
|
||||
pub mode: Option<ArgMode>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ProcedureParam {
|
||||
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,
|
||||
Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable,
|
||||
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
|
||||
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, Insert, Interpolate,
|
||||
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
|
||||
LimitClause, MatchRecognizePattern, Measure, NamedParenthesizedList, NamedWindowDefinition,
|
||||
ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement,
|
||||
OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource, ProjectionSelect, Query,
|
||||
RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
|
||||
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
|
||||
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
|
||||
TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use, Value, Values, ViewColumnDef,
|
||||
WhileStatement, WildcardAdditionalOptions, With, WithFill,
|
||||
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert,
|
||||
Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem,
|
||||
LateralView, LimitClause, MatchRecognizePattern, Measure, NamedParenthesizedList,
|
||||
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
|
||||
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource,
|
||||
ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction,
|
||||
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
|
||||
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
|
||||
TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins,
|
||||
UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement,
|
||||
WildcardAdditionalOptions, With, WithFill,
|
||||
};
|
||||
|
||||
/// Given an iterator of spans, return the [Span::union] of all spans.
|
||||
|
@ -650,7 +651,7 @@ impl Spanned for TableConstraint {
|
|||
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())),
|
||||
),
|
||||
TableConstraint::PrimaryKey {
|
||||
|
@ -664,7 +665,7 @@ impl Spanned for TableConstraint {
|
|||
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())),
|
||||
),
|
||||
TableConstraint::ForeignKey {
|
||||
|
@ -700,7 +701,7 @@ impl Spanned for TableConstraint {
|
|||
} => union_spans(
|
||||
name.iter()
|
||||
.map(|i| i.span)
|
||||
.chain(columns.iter().map(|i| i.span)),
|
||||
.chain(columns.iter().map(|i| i.span())),
|
||||
),
|
||||
TableConstraint::FulltextOrSpatial {
|
||||
fulltext: _,
|
||||
|
@ -711,7 +712,7 @@ impl Spanned for TableConstraint {
|
|||
opt_index_name
|
||||
.iter()
|
||||
.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 {
|
||||
fn span(&self) -> Span {
|
||||
let CaseStatement {
|
||||
|
|
|
@ -6868,9 +6868,7 @@ impl<'a> Parser<'a> {
|
|||
None
|
||||
};
|
||||
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let columns = self.parse_comma_separated(Parser::parse_create_index_expr)?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
let columns = self.parse_parenthesized_index_column_list()?;
|
||||
|
||||
let include = if self.parse_keyword(Keyword::INCLUDE) {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
|
@ -7626,9 +7624,22 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
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 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> {
|
||||
|
@ -8070,7 +8081,7 @@ impl<'a> Parser<'a> {
|
|||
let index_name = self.parse_optional_ident()?;
|
||||
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 characteristics = self.parse_constraint_characteristics()?;
|
||||
Ok(Some(TableConstraint::Unique {
|
||||
|
@ -8092,7 +8103,7 @@ impl<'a> Parser<'a> {
|
|||
let index_name = self.parse_optional_ident()?;
|
||||
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 characteristics = self.parse_constraint_characteristics()?;
|
||||
Ok(Some(TableConstraint::PrimaryKey {
|
||||
|
@ -8170,7 +8181,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
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 {
|
||||
display_as_key,
|
||||
|
@ -8199,7 +8210,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
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 {
|
||||
fulltext,
|
||||
|
@ -10601,6 +10612,14 @@ impl<'a> Parser<'a> {
|
|||
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.
|
||||
/// For example: `(db1.sc1.tbl1.col1, db1.sc1.tbl1."col 2", ...)`
|
||||
pub fn parse_parenthesized_qualified_column_list(
|
||||
|
@ -15017,7 +15036,8 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parse a FETCH clause
|
||||
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
|
||||
.parse_one_of_keywords(&[Keyword::ROW, Keyword::ROWS])
|
||||
.is_some()
|
||||
|
@ -15026,16 +15046,16 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
let quantity = Expr::Value(self.parse_value()?);
|
||||
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)
|
||||
};
|
||||
|
||||
let with_ties = if self.parse_keyword(Keyword::ONLY) {
|
||||
false
|
||||
} else if self.parse_keywords(&[Keyword::WITH, Keyword::TIES]) {
|
||||
true
|
||||
} else {
|
||||
return self.expected("one of ONLY or WITH TIES", self.peek_token());
|
||||
self.parse_keywords(&[Keyword::WITH, Keyword::TIES])
|
||||
};
|
||||
|
||||
Ok(Fetch {
|
||||
with_ties,
|
||||
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 =
|
||||
TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(MySqlDialect {})]);
|
||||
|
||||
|
@ -16537,7 +16571,7 @@ mod tests {
|
|||
display_as_key: false,
|
||||
name: 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,
|
||||
name: 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,
|
||||
name: Some(Ident::with_quote('\'', "index")),
|
||||
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,
|
||||
name: None,
|
||||
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,
|
||||
name: None,
|
||||
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,
|
||||
name: Some(Ident::new("idx_name")),
|
||||
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,
|
||||
name: Some(Ident::new("idx_name")),
|
||||
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![],
|
||||
})
|
||||
}
|
||||
|
||||
/// 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",
|
||||
);
|
||||
}
|
||||
|
||||
#[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,
|
||||
span: Span::empty(),
|
||||
},
|
||||
data_type: DataType::Int(None)
|
||||
data_type: DataType::Int(None),
|
||||
mode: None,
|
||||
},
|
||||
ProcedureParam {
|
||||
name: Ident {
|
||||
|
@ -164,7 +165,8 @@ fn parse_create_procedure() {
|
|||
data_type: DataType::Varchar(Some(CharacterLength::IntegerLength {
|
||||
length: 256,
|
||||
unit: None
|
||||
}))
|
||||
})),
|
||||
mode: None,
|
||||
}
|
||||
]),
|
||||
name: ObjectName::from(vec![Ident {
|
||||
|
|
|
@ -670,6 +670,20 @@ fn table_constraint_unique_primary_ctor(
|
|||
characteristics: Option<ConstraintCharacteristics>,
|
||||
unique_index_type_display: Option<KeyOrIndexDisplay>,
|
||||
) -> 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 {
|
||||
Some(index_type_display) => TableConstraint::Unique {
|
||||
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]
|
||||
fn parse_create_table_primary_and_unique_key_with_index_type() {
|
||||
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].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