mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-12-23 11:12:51 +00:00
Merge branch 'main' into no-whitespace-parser
This commit is contained in:
commit
df0b9f2c1a
10 changed files with 401 additions and 105 deletions
|
|
@ -3099,8 +3099,12 @@ impl fmt::Display for CreateFunction {
|
|||
if let Some(remote_connection) = &self.remote_connection {
|
||||
write!(f, " REMOTE WITH CONNECTION {remote_connection}")?;
|
||||
}
|
||||
if let Some(CreateFunctionBody::AsBeforeOptions(function_body)) = &self.function_body {
|
||||
write!(f, " AS {function_body}")?;
|
||||
if let Some(CreateFunctionBody::AsBeforeOptions { body, link_symbol }) = &self.function_body
|
||||
{
|
||||
write!(f, " AS {body}")?;
|
||||
if let Some(link_symbol) = link_symbol {
|
||||
write!(f, ", {link_symbol}")?;
|
||||
}
|
||||
}
|
||||
if let Some(CreateFunctionBody::Return(function_body)) = &self.function_body {
|
||||
write!(f, " RETURN {function_body}")?;
|
||||
|
|
@ -4188,3 +4192,62 @@ impl fmt::Display for OperatorPurpose {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `DROP OPERATOR` statement
|
||||
/// See <https://www.postgresql.org/docs/current/sql-dropoperator.html>
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct DropOperator {
|
||||
/// `IF EXISTS` clause
|
||||
pub if_exists: bool,
|
||||
/// One or more operators to drop with their signatures
|
||||
pub operators: Vec<DropOperatorSignature>,
|
||||
/// `CASCADE or RESTRICT`
|
||||
pub drop_behavior: Option<DropBehavior>,
|
||||
}
|
||||
|
||||
/// Operator signature for a `DROP OPERATOR` statement
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct DropOperatorSignature {
|
||||
/// Operator name
|
||||
pub name: ObjectName,
|
||||
/// Left operand type
|
||||
pub left_type: Option<DataType>,
|
||||
/// Right operand type
|
||||
pub right_type: DataType,
|
||||
}
|
||||
|
||||
impl fmt::Display for DropOperatorSignature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} (", self.name)?;
|
||||
if let Some(left_type) = &self.left_type {
|
||||
write!(f, "{}", left_type)?;
|
||||
} else {
|
||||
write!(f, "NONE")?;
|
||||
}
|
||||
write!(f, ", {})", self.right_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DropOperator {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "DROP OPERATOR")?;
|
||||
if self.if_exists {
|
||||
write!(f, " IF EXISTS")?;
|
||||
}
|
||||
write!(f, " {}", display_comma_separated(&self.operators))?;
|
||||
if let Some(drop_behavior) = &self.drop_behavior {
|
||||
write!(f, " {}", drop_behavior)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Spanned for DropOperator {
|
||||
fn span(&self) -> Span {
|
||||
Span::empty()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,14 +67,15 @@ pub use self::ddl::{
|
|||
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
|
||||
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
|
||||
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
|
||||
DropBehavior, DropExtension, DropFunction, DropTrigger, GeneratedAs, GeneratedExpressionMode,
|
||||
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
|
||||
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
|
||||
NullsDistinctOption, OperatorArgTypes, OperatorClassItem, OperatorPurpose, Owner, Partition,
|
||||
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
|
||||
TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
|
||||
UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
|
||||
UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
|
||||
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorSignature, DropTrigger,
|
||||
GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
|
||||
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
|
||||
IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes,
|
||||
OperatorClassItem, OperatorPurpose, Owner, Partition, ProcedureParam, ReferentialAction,
|
||||
RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind, Truncate,
|
||||
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
|
||||
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
|
||||
UserDefinedTypeStorage, ViewColumnDef,
|
||||
};
|
||||
pub use self::dml::{Copy, Delete, Insert, Update};
|
||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||
|
|
@ -3560,6 +3561,12 @@ pub enum Statement {
|
|||
/// <https://www.postgresql.org/docs/current/sql-dropextension.html>
|
||||
DropExtension(DropExtension),
|
||||
/// ```sql
|
||||
/// DROP OPERATOR [ IF EXISTS ] name ( { left_type | NONE } , right_type ) [, ...] [ CASCADE | RESTRICT ]
|
||||
/// ```
|
||||
/// Note: this is a PostgreSQL-specific statement.
|
||||
/// <https://www.postgresql.org/docs/current/sql-dropoperator.html>
|
||||
DropOperator(DropOperator),
|
||||
/// ```sql
|
||||
/// FETCH
|
||||
/// ```
|
||||
/// Retrieve rows from a query using a cursor
|
||||
|
|
@ -4786,6 +4793,7 @@ impl fmt::Display for Statement {
|
|||
Statement::CreateIndex(create_index) => create_index.fmt(f),
|
||||
Statement::CreateExtension(create_extension) => write!(f, "{create_extension}"),
|
||||
Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
|
||||
Statement::DropOperator(drop_operator) => write!(f, "{drop_operator}"),
|
||||
Statement::CreateRole(create_role) => write!(f, "{create_role}"),
|
||||
Statement::CreateSecret {
|
||||
or_replace,
|
||||
|
|
@ -9050,7 +9058,20 @@ pub enum CreateFunctionBody {
|
|||
/// ```
|
||||
///
|
||||
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
|
||||
AsBeforeOptions(Expr),
|
||||
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
|
||||
AsBeforeOptions {
|
||||
/// The primary expression.
|
||||
body: Expr,
|
||||
/// Link symbol if the primary expression contains the name of shared library file.
|
||||
///
|
||||
/// Example:
|
||||
/// ```sql
|
||||
/// CREATE FUNCTION cas_in(input cstring) RETURNS cas
|
||||
/// AS 'MODULE_PATHNAME', 'cas_in_wrapper'
|
||||
/// ```
|
||||
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
|
||||
link_symbol: Option<Expr>,
|
||||
},
|
||||
/// A function body expression using the 'AS' keyword and shows up
|
||||
/// after any `OPTIONS` clause.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ impl Spanned for Statement {
|
|||
Statement::CreateRole(create_role) => create_role.span(),
|
||||
Statement::CreateExtension(create_extension) => create_extension.span(),
|
||||
Statement::DropExtension(drop_extension) => drop_extension.span(),
|
||||
Statement::DropOperator(drop_operator) => drop_operator.span(),
|
||||
Statement::CreateSecret { .. } => Span::empty(),
|
||||
Statement::CreateServer { .. } => Span::empty(),
|
||||
Statement::CreateConnector { .. } => Span::empty(),
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ impl Dialect for SnowflakeDialect {
|
|||
|
||||
fn is_column_alias(&self, kw: &Keyword, parser: &mut Parser) -> bool {
|
||||
match kw {
|
||||
// The following keywords can be considered an alias as long as
|
||||
// The following keywords can be considered an alias as long as
|
||||
// they are not followed by other tokens that may change their meaning
|
||||
// e.g. `SELECT * EXCEPT (col1) FROM tbl`
|
||||
Keyword::EXCEPT
|
||||
|
|
@ -412,7 +412,7 @@ impl Dialect for SnowflakeDialect {
|
|||
Keyword::LIMIT | Keyword::OFFSET if peek_for_limit_options(parser) => false,
|
||||
|
||||
// `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
|
||||
// which would give it a different meanings, for example:
|
||||
// which would give it a different meanings, for example:
|
||||
// `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
|
||||
// `SELECT 1 FETCH 10` - not an alias
|
||||
Keyword::FETCH if parser.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT]).is_some()
|
||||
|
|
@ -421,8 +421,8 @@ impl Dialect for SnowflakeDialect {
|
|||
false
|
||||
}
|
||||
|
||||
// Reserved keywords by the Snowflake dialect, which seem to be less strictive
|
||||
// than what is listed in `keywords::RESERVED_FOR_COLUMN_ALIAS`. The following
|
||||
// Reserved keywords by the Snowflake dialect, which seem to be less strictive
|
||||
// than what is listed in `keywords::RESERVED_FOR_COLUMN_ALIAS`. The following
|
||||
// keywords were tested with the this statement: `SELECT 1 <KW>`.
|
||||
Keyword::FROM
|
||||
| Keyword::GROUP
|
||||
|
|
@ -692,7 +692,7 @@ pub fn parse_create_table(
|
|||
.iceberg(iceberg)
|
||||
.global(global)
|
||||
.dynamic(dynamic)
|
||||
.hive_formats(Some(Default::default()));
|
||||
.hive_formats(None);
|
||||
|
||||
// Snowflake does not enforce order of the parameters in the statement. The parser needs to
|
||||
// parse the statement in a loop.
|
||||
|
|
|
|||
|
|
@ -5140,9 +5140,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
if self.parse_keyword(Keyword::AS) {
|
||||
ensure_not_set(&body.function_body, "AS")?;
|
||||
body.function_body = Some(CreateFunctionBody::AsBeforeOptions(
|
||||
self.parse_create_function_body_string()?,
|
||||
));
|
||||
body.function_body = Some(self.parse_create_function_body_string()?);
|
||||
} else if self.parse_keyword(Keyword::LANGUAGE) {
|
||||
ensure_not_set(&body.language, "LANGUAGE")?;
|
||||
body.language = Some(self.parse_identifier()?);
|
||||
|
|
@ -5234,7 +5232,7 @@ impl<'a> Parser<'a> {
|
|||
let name = self.parse_object_name(false)?;
|
||||
self.expect_keyword_is(Keyword::AS)?;
|
||||
|
||||
let as_ = self.parse_create_function_body_string()?;
|
||||
let body = self.parse_create_function_body_string()?;
|
||||
let using = self.parse_optional_create_function_using()?;
|
||||
|
||||
Ok(Statement::CreateFunction(CreateFunction {
|
||||
|
|
@ -5242,7 +5240,7 @@ impl<'a> Parser<'a> {
|
|||
or_replace,
|
||||
temporary,
|
||||
name,
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(as_)),
|
||||
function_body: Some(body),
|
||||
using,
|
||||
if_not_exists: false,
|
||||
args: None,
|
||||
|
|
@ -5304,7 +5302,10 @@ impl<'a> Parser<'a> {
|
|||
let expr = self.parse_expr()?;
|
||||
if options.is_none() {
|
||||
options = self.maybe_parse_options(Keyword::OPTIONS)?;
|
||||
Some(CreateFunctionBody::AsBeforeOptions(expr))
|
||||
Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: expr,
|
||||
link_symbol: None,
|
||||
})
|
||||
} else {
|
||||
Some(CreateFunctionBody::AsAfterOptions(expr))
|
||||
}
|
||||
|
|
@ -5767,15 +5768,19 @@ impl<'a> Parser<'a> {
|
|||
let hive_distribution = self.parse_hive_distribution()?;
|
||||
let hive_formats = self.parse_hive_formats()?;
|
||||
|
||||
let file_format = if let Some(ff) = &hive_formats.storage {
|
||||
match ff {
|
||||
HiveIOFormat::FileFormat { format } => Some(*format),
|
||||
_ => None,
|
||||
let file_format = if let Some(ref hf) = hive_formats {
|
||||
if let Some(ref ff) = hf.storage {
|
||||
match ff {
|
||||
HiveIOFormat::FileFormat { format } => Some(*format),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let location = hive_formats.location.clone();
|
||||
let location = hive_formats.as_ref().and_then(|hf| hf.location.clone());
|
||||
let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
|
||||
let table_options = if !table_properties.is_empty() {
|
||||
CreateTableOptions::TableProperties(table_properties)
|
||||
|
|
@ -5786,7 +5791,7 @@ impl<'a> Parser<'a> {
|
|||
.columns(columns)
|
||||
.constraints(constraints)
|
||||
.hive_distribution(hive_distribution)
|
||||
.hive_formats(Some(hive_formats))
|
||||
.hive_formats(hive_formats)
|
||||
.table_options(table_options)
|
||||
.or_replace(or_replace)
|
||||
.if_not_exists(if_not_exists)
|
||||
|
|
@ -6703,9 +6708,11 @@ impl<'a> Parser<'a> {
|
|||
return self.parse_drop_trigger();
|
||||
} else if self.parse_keyword(Keyword::EXTENSION) {
|
||||
return self.parse_drop_extension();
|
||||
} else if self.parse_keyword(Keyword::OPERATOR) {
|
||||
return self.parse_drop_operator();
|
||||
} else {
|
||||
return self.expected(
|
||||
"CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW or USER after DROP",
|
||||
"CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW or USER after DROP",
|
||||
self.peek_token(),
|
||||
);
|
||||
};
|
||||
|
|
@ -7461,6 +7468,46 @@ impl<'a> Parser<'a> {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Parse a[Statement::DropOperator] statement.
|
||||
///
|
||||
pub fn parse_drop_operator(&mut self) -> Result<Statement, ParserError> {
|
||||
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
|
||||
let operators = self.parse_comma_separated(|p| p.parse_drop_operator_signature())?;
|
||||
let drop_behavior = self.parse_optional_drop_behavior();
|
||||
Ok(Statement::DropOperator(DropOperator {
|
||||
if_exists,
|
||||
operators,
|
||||
drop_behavior,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parse an operator signature for a [Statement::DropOperator]
|
||||
/// Format: `name ( { left_type | NONE } , right_type )`
|
||||
fn parse_drop_operator_signature(&mut self) -> Result<DropOperatorSignature, ParserError> {
|
||||
let name = self.parse_operator_name()?;
|
||||
self.expect_token(&Token::LParen)?;
|
||||
|
||||
// Parse left operand type (or NONE for prefix operators)
|
||||
let left_type = if self.parse_keyword(Keyword::NONE) {
|
||||
None
|
||||
} else {
|
||||
Some(self.parse_data_type()?)
|
||||
};
|
||||
|
||||
self.expect_token(&Token::Comma)?;
|
||||
|
||||
// Parse right operand type (always required)
|
||||
let right_type = self.parse_data_type()?;
|
||||
|
||||
self.expect_token(&Token::RParen)?;
|
||||
|
||||
Ok(DropOperatorSignature {
|
||||
name,
|
||||
left_type,
|
||||
right_type,
|
||||
})
|
||||
}
|
||||
|
||||
//TODO: Implement parsing for Skewed
|
||||
pub fn parse_hive_distribution(&mut self) -> Result<HiveDistributionStyle, ParserError> {
|
||||
if self.parse_keywords(&[Keyword::PARTITIONED, Keyword::BY]) {
|
||||
|
|
@ -7473,8 +7520,8 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_hive_formats(&mut self) -> Result<HiveFormat, ParserError> {
|
||||
let mut hive_format = HiveFormat::default();
|
||||
pub fn parse_hive_formats(&mut self) -> Result<Option<HiveFormat>, ParserError> {
|
||||
let mut hive_format: Option<HiveFormat> = None;
|
||||
loop {
|
||||
match self.parse_one_of_keywords(&[
|
||||
Keyword::ROW,
|
||||
|
|
@ -7483,7 +7530,9 @@ impl<'a> Parser<'a> {
|
|||
Keyword::WITH,
|
||||
]) {
|
||||
Some(Keyword::ROW) => {
|
||||
hive_format.row_format = Some(self.parse_row_format()?);
|
||||
hive_format
|
||||
.get_or_insert_with(HiveFormat::default)
|
||||
.row_format = Some(self.parse_row_format()?);
|
||||
}
|
||||
Some(Keyword::STORED) => {
|
||||
self.expect_keyword_is(Keyword::AS)?;
|
||||
|
|
@ -7491,24 +7540,29 @@ impl<'a> Parser<'a> {
|
|||
let input_format = self.parse_expr()?;
|
||||
self.expect_keyword_is(Keyword::OUTPUTFORMAT)?;
|
||||
let output_format = self.parse_expr()?;
|
||||
hive_format.storage = Some(HiveIOFormat::IOF {
|
||||
input_format,
|
||||
output_format,
|
||||
});
|
||||
hive_format.get_or_insert_with(HiveFormat::default).storage =
|
||||
Some(HiveIOFormat::IOF {
|
||||
input_format,
|
||||
output_format,
|
||||
});
|
||||
} else {
|
||||
let format = self.parse_file_format()?;
|
||||
hive_format.storage = Some(HiveIOFormat::FileFormat { format });
|
||||
hive_format.get_or_insert_with(HiveFormat::default).storage =
|
||||
Some(HiveIOFormat::FileFormat { format });
|
||||
}
|
||||
}
|
||||
Some(Keyword::LOCATION) => {
|
||||
hive_format.location = Some(self.parse_literal_string()?);
|
||||
hive_format.get_or_insert_with(HiveFormat::default).location =
|
||||
Some(self.parse_literal_string()?);
|
||||
}
|
||||
Some(Keyword::WITH) => {
|
||||
self.prev_token();
|
||||
let properties = self
|
||||
.parse_options_with_keywords(&[Keyword::WITH, Keyword::SERDEPROPERTIES])?;
|
||||
if !properties.is_empty() {
|
||||
hive_format.serde_properties = Some(properties);
|
||||
hive_format
|
||||
.get_or_insert_with(HiveFormat::default)
|
||||
.serde_properties = Some(properties);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -7723,7 +7777,7 @@ impl<'a> Parser<'a> {
|
|||
.if_not_exists(if_not_exists)
|
||||
.transient(transient)
|
||||
.hive_distribution(hive_distribution)
|
||||
.hive_formats(Some(hive_formats))
|
||||
.hive_formats(hive_formats)
|
||||
.global(global)
|
||||
.query(query)
|
||||
.without_rowid(without_rowid)
|
||||
|
|
@ -10423,19 +10477,30 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parse the body of a `CREATE FUNCTION` specified as a string.
|
||||
/// e.g. `CREATE FUNCTION ... AS $$ body $$`.
|
||||
fn parse_create_function_body_string(&mut self) -> Result<Expr, ParserError> {
|
||||
let peek_token = self.peek_token();
|
||||
let span = peek_token.span;
|
||||
match peek_token.token {
|
||||
Token::DollarQuotedString(s) if dialect_of!(self is PostgreSqlDialect | GenericDialect) =>
|
||||
{
|
||||
self.next_token();
|
||||
Ok(Expr::Value(Value::DollarQuotedString(s).with_span(span)))
|
||||
fn parse_create_function_body_string(&mut self) -> Result<CreateFunctionBody, ParserError> {
|
||||
let parse_string_expr = |parser: &mut Parser| -> Result<Expr, ParserError> {
|
||||
let peek_token = parser.peek_token();
|
||||
let span = peek_token.span;
|
||||
match peek_token.token {
|
||||
Token::DollarQuotedString(s) if dialect_of!(parser is PostgreSqlDialect | GenericDialect) =>
|
||||
{
|
||||
parser.next_token();
|
||||
Ok(Expr::Value(Value::DollarQuotedString(s).with_span(span)))
|
||||
}
|
||||
_ => Ok(Expr::Value(
|
||||
Value::SingleQuotedString(parser.parse_literal_string()?).with_span(span),
|
||||
)),
|
||||
}
|
||||
_ => Ok(Expr::Value(
|
||||
Value::SingleQuotedString(self.parse_literal_string()?).with_span(span),
|
||||
)),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(CreateFunctionBody::AsBeforeOptions {
|
||||
body: parse_string_expr(self)?,
|
||||
link_symbol: if self.consume_token(&Token::Comma) {
|
||||
Some(parse_string_expr(self)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a literal string
|
||||
|
|
|
|||
|
|
@ -4724,6 +4724,17 @@ fn parse_create_external_table_lowercase() {
|
|||
assert_matches!(ast, Statement::CreateTable(CreateTable { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_hive_formats_none_when_no_options() {
|
||||
let sql = "CREATE TABLE simple_table (id INT, name VARCHAR(100))";
|
||||
match verified_stmt(sql) {
|
||||
Statement::CreateTable(CreateTable { hive_formats, .. }) => {
|
||||
assert_eq!(hive_formats, None);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_alter_table() {
|
||||
let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT;";
|
||||
|
|
|
|||
|
|
@ -739,12 +739,7 @@ fn test_duckdb_union_datatype() {
|
|||
],
|
||||
constraints: Default::default(),
|
||||
hive_distribution: HiveDistributionStyle::NONE,
|
||||
hive_formats: Some(HiveFormat {
|
||||
row_format: Default::default(),
|
||||
serde_properties: Default::default(),
|
||||
storage: Default::default(),
|
||||
location: Default::default()
|
||||
}),
|
||||
hive_formats: None,
|
||||
file_format: Default::default(),
|
||||
location: Default::default(),
|
||||
query: Default::default(),
|
||||
|
|
|
|||
|
|
@ -408,10 +408,13 @@ fn parse_create_function() {
|
|||
assert_eq!(name.to_string(), "mydb.myfunc");
|
||||
assert_eq!(
|
||||
function_body,
|
||||
Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::SingleQuotedString("org.random.class.Name".to_string()))
|
||||
.with_empty_span()
|
||||
)))
|
||||
Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::SingleQuotedString("org.random.class.Name".to_string()))
|
||||
.with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
using,
|
||||
|
|
|
|||
|
|
@ -1881,12 +1881,7 @@ fn parse_create_table_with_valid_options() {
|
|||
],
|
||||
constraints: vec![],
|
||||
hive_distribution: HiveDistributionStyle::NONE,
|
||||
hive_formats: Some(HiveFormat {
|
||||
row_format: None,
|
||||
serde_properties: None,
|
||||
storage: None,
|
||||
location: None,
|
||||
},),
|
||||
hive_formats: None,
|
||||
file_format: None,
|
||||
location: None,
|
||||
query: None,
|
||||
|
|
@ -2053,12 +2048,7 @@ fn parse_create_table_with_identity_column() {
|
|||
},],
|
||||
constraints: vec![],
|
||||
hive_distribution: HiveDistributionStyle::NONE,
|
||||
hive_formats: Some(HiveFormat {
|
||||
row_format: None,
|
||||
serde_properties: None,
|
||||
storage: None,
|
||||
location: None,
|
||||
},),
|
||||
hive_formats: None,
|
||||
file_format: None,
|
||||
location: None,
|
||||
query: None,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
mod test_utils;
|
||||
|
||||
use helpers::attached_token::AttachedToken;
|
||||
use sqlparser::ast::{DataType, DropBehavior, DropOperator, DropOperatorSignature};
|
||||
use sqlparser::tokenizer::Span;
|
||||
use test_utils::*;
|
||||
|
||||
|
|
@ -4281,9 +4282,12 @@ $$"#;
|
|||
behavior: None,
|
||||
called_on_null: None,
|
||||
parallel: None,
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF str1 <> str2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
))),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF str1 <> str2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
|
|
@ -4319,9 +4323,12 @@ $$"#;
|
|||
behavior: None,
|
||||
called_on_null: None,
|
||||
parallel: None,
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> 0 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
))),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> 0 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
|
|
@ -4361,9 +4368,12 @@ $$"#;
|
|||
behavior: None,
|
||||
called_on_null: None,
|
||||
parallel: None,
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF a <> b THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
))),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF a <> b THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
|
|
@ -4403,9 +4413,12 @@ $$"#;
|
|||
behavior: None,
|
||||
called_on_null: None,
|
||||
parallel: None,
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> int2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
))),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> int2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
|
|
@ -4438,13 +4451,16 @@ $$"#;
|
|||
behavior: None,
|
||||
called_on_null: None,
|
||||
parallel: None,
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {
|
||||
value: "\n BEGIN\n RETURN TRUE;\n END;\n ".to_owned(),
|
||||
tag: None
|
||||
}))
|
||||
.with_empty_span()
|
||||
))),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::DollarQuotedString(DollarQuotedString {
|
||||
value: "\n BEGIN\n RETURN TRUE;\n END;\n ".to_owned(),
|
||||
tag: None
|
||||
}))
|
||||
.with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
|
|
@ -4476,9 +4492,12 @@ fn parse_create_function() {
|
|||
behavior: Some(FunctionBehavior::Immutable),
|
||||
called_on_null: Some(FunctionCalledOnNull::Strict),
|
||||
parallel: Some(FunctionParallel::Safe),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
|
||||
(Value::SingleQuotedString("select $1 + $2;".into())).with_empty_span()
|
||||
))),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::SingleQuotedString("select $1 + $2;".into())).with_empty_span()
|
||||
),
|
||||
link_symbol: None,
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
|
|
@ -4509,6 +4528,52 @@ fn parse_incorrect_create_function_parallel() {
|
|||
assert!(pg().parse_sql_statements(sql).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_function_c_with_module_pathname() {
|
||||
let sql = "CREATE FUNCTION cas_in(input cstring) RETURNS cas LANGUAGE c IMMUTABLE PARALLEL SAFE AS 'MODULE_PATHNAME', 'cas_in_wrapper'";
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(sql),
|
||||
Statement::CreateFunction(CreateFunction {
|
||||
or_alter: false,
|
||||
or_replace: false,
|
||||
temporary: false,
|
||||
name: ObjectName::from(vec![Ident::new("cas_in")]),
|
||||
args: Some(vec![OperateFunctionArg::with_name(
|
||||
"input",
|
||||
DataType::Custom(ObjectName::from(vec![Ident::new("cstring")]), vec![]),
|
||||
),]),
|
||||
return_type: Some(DataType::Custom(
|
||||
ObjectName::from(vec![Ident::new("cas")]),
|
||||
vec![]
|
||||
)),
|
||||
language: Some("c".into()),
|
||||
behavior: Some(FunctionBehavior::Immutable),
|
||||
called_on_null: None,
|
||||
parallel: Some(FunctionParallel::Safe),
|
||||
function_body: Some(CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value(
|
||||
(Value::SingleQuotedString("MODULE_PATHNAME".into())).with_empty_span()
|
||||
),
|
||||
link_symbol: Some(Expr::Value(
|
||||
(Value::SingleQuotedString("cas_in_wrapper".into())).with_empty_span()
|
||||
)),
|
||||
}),
|
||||
if_not_exists: false,
|
||||
using: None,
|
||||
determinism_specifier: None,
|
||||
options: None,
|
||||
remote_connection: None,
|
||||
})
|
||||
);
|
||||
|
||||
// Test that attribute order flexibility works (IMMUTABLE before LANGUAGE)
|
||||
let sql_alt_order = "CREATE FUNCTION cas_in(input cstring) RETURNS cas IMMUTABLE PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', 'cas_in_wrapper'";
|
||||
pg_and_generic().one_statement_parses_to(
|
||||
sql_alt_order,
|
||||
"CREATE FUNCTION cas_in(input cstring) RETURNS cas LANGUAGE c IMMUTABLE PARALLEL SAFE AS 'MODULE_PATHNAME', 'cas_in_wrapper'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_drop_function() {
|
||||
let sql = "DROP FUNCTION IF EXISTS test_func";
|
||||
|
|
@ -6037,12 +6102,7 @@ fn parse_trigger_related_functions() {
|
|||
],
|
||||
constraints: vec![],
|
||||
hive_distribution: HiveDistributionStyle::NONE,
|
||||
hive_formats: Some(HiveFormat {
|
||||
row_format: None,
|
||||
serde_properties: None,
|
||||
storage: None,
|
||||
location: None
|
||||
}),
|
||||
hive_formats: None,
|
||||
file_format: None,
|
||||
location: None,
|
||||
query: None,
|
||||
|
|
@ -6096,8 +6156,8 @@ fn parse_trigger_related_functions() {
|
|||
args: Some(vec![]),
|
||||
return_type: Some(DataType::Trigger),
|
||||
function_body: Some(
|
||||
CreateFunctionBody::AsBeforeOptions(
|
||||
Expr::Value((
|
||||
CreateFunctionBody::AsBeforeOptions {
|
||||
body: Expr::Value((
|
||||
Value::DollarQuotedString(
|
||||
DollarQuotedString {
|
||||
value: "\n BEGIN\n -- Check that empname and salary are given\n IF NEW.empname IS NULL THEN\n RAISE EXCEPTION 'empname cannot be null';\n END IF;\n IF NEW.salary IS NULL THEN\n RAISE EXCEPTION '% cannot have null salary', NEW.empname;\n END IF;\n\n -- Who works for us when they must pay for it?\n IF NEW.salary < 0 THEN\n RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;\n END IF;\n\n -- Remember who changed the payroll when\n NEW.last_date := current_timestamp;\n NEW.last_user := current_user;\n RETURN NEW;\n END;\n ".to_owned(),
|
||||
|
|
@ -6107,7 +6167,8 @@ fn parse_trigger_related_functions() {
|
|||
},
|
||||
)
|
||||
).with_empty_span()),
|
||||
),
|
||||
link_symbol: None,
|
||||
},
|
||||
),
|
||||
behavior: None,
|
||||
called_on_null: None,
|
||||
|
|
@ -6785,6 +6846,92 @@ fn parse_create_operator() {
|
|||
assert!(pg().parse_sql_statements("CREATE OPERATOR > ())").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_drop_operator() {
|
||||
// Test DROP OPERATOR with NONE for prefix operator
|
||||
let sql = "DROP OPERATOR ~ (NONE, BIT)";
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(sql),
|
||||
Statement::DropOperator(DropOperator {
|
||||
if_exists: false,
|
||||
operators: vec![DropOperatorSignature {
|
||||
name: ObjectName::from(vec![Ident::new("~")]),
|
||||
left_type: None,
|
||||
right_type: DataType::Bit(None),
|
||||
}],
|
||||
drop_behavior: None,
|
||||
})
|
||||
);
|
||||
|
||||
for if_exist in [true, false] {
|
||||
for cascading in [
|
||||
None,
|
||||
Some(DropBehavior::Cascade),
|
||||
Some(DropBehavior::Restrict),
|
||||
] {
|
||||
for op in &["<", ">", "<=", ">=", "<>", "||", "&&", "<<", ">>"] {
|
||||
let sql = format!(
|
||||
"DROP OPERATOR{} {op} (INTEGER, INTEGER){}",
|
||||
if if_exist { " IF EXISTS" } else { "" },
|
||||
match cascading {
|
||||
Some(cascading) => format!(" {cascading}"),
|
||||
None => String::new(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(&sql),
|
||||
Statement::DropOperator(DropOperator {
|
||||
if_exists: if_exist,
|
||||
operators: vec![DropOperatorSignature {
|
||||
name: ObjectName::from(vec![Ident::new(*op)]),
|
||||
left_type: Some(DataType::Integer(None)),
|
||||
right_type: DataType::Integer(None),
|
||||
}],
|
||||
drop_behavior: cascading,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test DROP OPERATOR with schema-qualified operator name
|
||||
let sql = "DROP OPERATOR myschema.@@ (TEXT, TEXT)";
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(sql),
|
||||
Statement::DropOperator(DropOperator {
|
||||
if_exists: false,
|
||||
operators: vec![DropOperatorSignature {
|
||||
name: ObjectName::from(vec![Ident::new("myschema"), Ident::new("@@")]),
|
||||
left_type: Some(DataType::Text),
|
||||
right_type: DataType::Text,
|
||||
}],
|
||||
drop_behavior: None,
|
||||
})
|
||||
);
|
||||
|
||||
// Test DROP OPERATOR with multiple operators, IF EXISTS and CASCADE
|
||||
let sql = "DROP OPERATOR IF EXISTS + (INTEGER, INTEGER), - (INTEGER, INTEGER) CASCADE";
|
||||
assert_eq!(
|
||||
pg_and_generic().verified_stmt(sql),
|
||||
Statement::DropOperator(DropOperator {
|
||||
if_exists: true,
|
||||
operators: vec![
|
||||
DropOperatorSignature {
|
||||
name: ObjectName::from(vec![Ident::new("+")]),
|
||||
left_type: Some(DataType::Integer(None)),
|
||||
right_type: DataType::Integer(None),
|
||||
},
|
||||
DropOperatorSignature {
|
||||
name: ObjectName::from(vec![Ident::new("-")]),
|
||||
left_type: Some(DataType::Integer(None)),
|
||||
right_type: DataType::Integer(None),
|
||||
}
|
||||
],
|
||||
drop_behavior: Some(DropBehavior::Cascade),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_operator_family() {
|
||||
for index_method in &["btree", "hash", "gist", "gin", "spgist", "brin"] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue