@@ -25,8 +25,8 @@ mod test_utils;
2525use helpers:: attached_token:: AttachedToken ;
2626use sqlparser:: ast:: * ;
2727use sqlparser:: dialect:: { Dialect , GenericDialect , MySqlDialect , PostgreSqlDialect , SQLiteDialect } ;
28- use sqlparser:: parser:: ParserError ;
29- use sqlparser:: tokenizer:: Span ;
28+ use sqlparser:: parser:: { Parser , ParserError } ;
29+ use sqlparser:: tokenizer:: { Location , Span } ;
3030use test_utils:: * ;
3131
3232#[ test]
@@ -9678,3 +9678,143 @@ fn parse_right_deep_join_chain() {
96789678 // NATURAL JOIN followed by a constrained join must stay left-associative.
96799679 pg ( ) . verified_stmt ( "SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true" ) ;
96809680}
9681+
9682+ #[ test]
9683+ fn parse_quoted_function_argument_name ( ) {
9684+ let statement = pg_and_generic ( ) . verified_stmt (
9685+ r#"CREATE FUNCTION is_member("Role" TEXT) RETURNS BOOLEAN LANGUAGE SQL AS 'SELECT true'"# ,
9686+ ) ;
9687+ let Statement :: CreateFunction ( function) = statement else {
9688+ panic ! ( "expected a CREATE FUNCTION statement" ) ;
9689+ } ;
9690+ assert_eq ! (
9691+ function. args,
9692+ Some ( vec![ OperateFunctionArg {
9693+ mode: None ,
9694+ name: Some ( Ident :: with_quote( '"' , "Role" ) ) ,
9695+ data_type: DataType :: Text ,
9696+ default_expr: None ,
9697+ } ] )
9698+ ) ;
9699+
9700+ // An embedded quote is doubled in the input and belongs to the name once.
9701+ let statement = pg_and_generic ( ) . verified_stmt (
9702+ r#"CREATE FUNCTION f("we""ird" INT) RETURNS BOOLEAN LANGUAGE SQL RETURN true"# ,
9703+ ) ;
9704+ let Statement :: CreateFunction ( function) = statement else {
9705+ panic ! ( "expected a CREATE FUNCTION statement" ) ;
9706+ } ;
9707+ assert_eq ! (
9708+ function. args,
9709+ Some ( vec![ OperateFunctionArg {
9710+ mode: None ,
9711+ name: Some ( Ident :: with_quote( '"' , r#"we"ird"# ) ) ,
9712+ data_type: DataType :: Int ( None ) ,
9713+ default_expr: None ,
9714+ } ] )
9715+ ) ;
9716+
9717+ // A name outside ASCII is quoted for the same reason and survives the same way.
9718+ pg_and_generic ( ) . verified_stmt (
9719+ r#"CREATE FUNCTION f("Rôle" TEXT) RETURNS BOOLEAN LANGUAGE SQL RETURN true"# ,
9720+ ) ;
9721+ }
9722+
9723+ #[ test]
9724+ fn parse_quoted_function_argument_name_span ( ) {
9725+ let sql =
9726+ r#"CREATE FUNCTION is_member("Role" TEXT) RETURNS BOOLEAN LANGUAGE SQL AS 'SELECT true'"# ;
9727+ // Parsed directly rather than through the test helpers, which tokenize
9728+ // without locations.
9729+ let mut statements = Parser :: parse_sql ( & PostgreSqlDialect { } , sql) . unwrap ( ) ;
9730+ let Some ( Statement :: CreateFunction ( function) ) = statements. pop ( ) else {
9731+ panic ! ( "expected a CREATE FUNCTION statement" ) ;
9732+ } ;
9733+ let name = function. args . as_ref ( ) . unwrap ( ) [ 0 ] . name . as_ref ( ) . unwrap ( ) ;
9734+ assert_eq ! (
9735+ name. span,
9736+ Span :: new( Location :: new( 1 , 27 ) , Location :: new( 1 , 33 ) ) ,
9737+ "the span covers the quoted name in the input"
9738+ ) ;
9739+ }
9740+
9741+ #[ test]
9742+ fn parse_function_argument_modes_and_defaults_keep_quoted_names ( ) {
9743+ let sql = r#"CREATE FUNCTION f(IN "A" INT = 1, OUT "B" TEXT, INOUT "C" BOOLEAN, VARIADIC "D" INT[]) RETURNS INT LANGUAGE SQL AS 'x'"# ;
9744+ let Statement :: CreateFunction ( function) = pg_and_generic ( ) . verified_stmt ( sql) else {
9745+ panic ! ( "expected a CREATE FUNCTION statement" ) ;
9746+ } ;
9747+ assert_eq ! (
9748+ function. args,
9749+ Some ( vec![
9750+ OperateFunctionArg {
9751+ mode: Some ( ArgMode :: In ) ,
9752+ name: Some ( Ident :: with_quote( '"' , "A" ) ) ,
9753+ data_type: DataType :: Int ( None ) ,
9754+ default_expr: Some ( Expr :: Value (
9755+ Value :: Number ( "1" . parse( ) . unwrap( ) , false ) . with_empty_span( )
9756+ ) ) ,
9757+ } ,
9758+ OperateFunctionArg {
9759+ mode: Some ( ArgMode :: Out ) ,
9760+ name: Some ( Ident :: with_quote( '"' , "B" ) ) ,
9761+ data_type: DataType :: Text ,
9762+ default_expr: None ,
9763+ } ,
9764+ OperateFunctionArg {
9765+ mode: Some ( ArgMode :: InOut ) ,
9766+ name: Some ( Ident :: with_quote( '"' , "C" ) ) ,
9767+ data_type: DataType :: Boolean ,
9768+ default_expr: None ,
9769+ } ,
9770+ OperateFunctionArg {
9771+ mode: Some ( ArgMode :: Variadic ) ,
9772+ name: Some ( Ident :: with_quote( '"' , "D" ) ) ,
9773+ data_type: DataType :: Array ( ArrayElemTypeDef :: SquareBracket (
9774+ Box :: new( DataType :: Int ( None ) ) ,
9775+ None
9776+ ) ) ,
9777+ default_expr: None ,
9778+ } ,
9779+ ] )
9780+ ) ;
9781+
9782+ // The `DEFAULT` spelling of the same argument list renders as `=`.
9783+ pg_and_generic ( ) . one_statement_parses_to (
9784+ r#"CREATE FUNCTION f("A" INT DEFAULT 1) RETURNS INT LANGUAGE SQL AS 'x'"# ,
9785+ r#"CREATE FUNCTION f("A" INT = 1) RETURNS INT LANGUAGE SQL AS 'x'"# ,
9786+ ) ;
9787+ }
9788+
9789+ #[ test]
9790+ fn parse_quoted_argument_names_in_function_signatures ( ) {
9791+ pg_and_generic ( ) . verified_stmt ( r#"DROP FUNCTION f("Role" TEXT)"# ) ;
9792+ pg_and_generic ( ) . verified_stmt ( r#"DROP PROCEDURE p("Role" TEXT)"# ) ;
9793+ pg_and_generic ( ) . verified_stmt ( r#"ALTER FUNCTION f("Role" TEXT) RENAME TO g"# ) ;
9794+ pg_and_generic ( ) . verified_stmt ( r#"ALTER AGGREGATE my_agg("Role" TEXT) RENAME TO other_agg"# ) ;
9795+
9796+ // An aggregate's `ORDER BY` arguments are parsed by the same reader as its
9797+ // direct ones.
9798+ let sql = r#"ALTER AGGREGATE my_agg("A" INT ORDER BY "B" INT) OWNER TO some_role"# ;
9799+ let Statement :: AlterFunction ( alter) = pg_and_generic ( ) . verified_stmt ( sql) else {
9800+ panic ! ( "expected an ALTER AGGREGATE statement" ) ;
9801+ } ;
9802+ assert_eq ! (
9803+ alter. function. args,
9804+ Some ( vec![ OperateFunctionArg {
9805+ mode: None ,
9806+ name: Some ( Ident :: with_quote( '"' , "A" ) ) ,
9807+ data_type: DataType :: Int ( None ) ,
9808+ default_expr: None ,
9809+ } ] )
9810+ ) ;
9811+ assert_eq ! (
9812+ alter. aggregate_order_by,
9813+ Some ( vec![ OperateFunctionArg {
9814+ mode: None ,
9815+ name: Some ( Ident :: with_quote( '"' , "B" ) ) ,
9816+ data_type: DataType :: Int ( None ) ,
9817+ default_expr: None ,
9818+ } ] )
9819+ ) ;
9820+ }
0 commit comments