Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -16382,19 +16382,8 @@ PrivilegeTarget.Kind AccessObjectKind(boolean plural):
}

RoutineReference.Argument AccessRoutineArgument():
{ RoutineReference.Argument result = new RoutineReference.Argument(); String name; Token mode; ColDataType type; }
{
[
LOOKAHEAD({ isAccessKeywordAhead("IN") || isAccessKeywordAhead("OUT") || isAccessKeywordAhead("INOUT") || isAccessKeywordAhead("VARIADIC") })
( <K_IN> { result.setMode(RoutineReference.Argument.Mode.IN); }
| mode=<S_IDENTIFIER> { result.setMode(accessEnum(RoutineReference.Argument.Mode.class, mode.image)); } )
]
(
LOOKAHEAD(ColDataType() ("," | ")")) type=ColDataType()
| name=RelObjectName() { result.setName(name); } type=ColDataType()
)
{ result.setDataType(type); return result; }
}
{ RoutineReference.Argument argument; }
{ argument=RoutineArgument(false) { return argument; } }

RoutineReference AccessRoutine():
{ RoutineReference result = new RoutineReference(); String name; RoutineReference.Argument argument; List<RoutineReference.Argument> arguments; }
Expand Down Expand Up @@ -17171,18 +17160,30 @@ CreateExtension CreateExtension():
}

RoutineReference.Argument RoutineSignatureArgument():
{ RoutineReference.Argument argument; }
{ argument=RoutineArgument(true) { return argument; } }

/** Common argument AST; only extension aggregate signatures may end a type at ORDER BY. */
RoutineReference.Argument RoutineArgument(boolean allowOrderBy):
{ RoutineReference.Argument result = new RoutineReference.Argument(); String name; Token mode; ColDataType type; }
{
[
LOOKAHEAD({ isKeywordAhead("IN") || isKeywordAhead("OUT") || isKeywordAhead("INOUT") || isKeywordAhead("VARIADIC") })
( <K_IN> { result.setMode(RoutineReference.Argument.Mode.IN); }
| mode=<S_IDENTIFIER> { result.setMode(typeDdlEnum(RoutineReference.Argument.Mode.class, mode.image)); } )
| mode=<S_IDENTIFIER> { result.setMode(allowOrderBy
? typeDdlEnum(RoutineReference.Argument.Mode.class, mode.image)
: accessEnum(RoutineReference.Argument.Mode.class, mode.image)); } )
]
(
LOOKAHEAD(ColDataType() ("," | ")" | <K_ORDER>)) type=ColDataType()
| name=RelObjectName() { result.setName(name); } type=ColDataType()
)
{ result.setDataType(type); return result; }
{
requireAccessSyntax(allowOrderBy || getToken(1).kind != K_ORDER,
"ORDER BY requires an aggregate signature");
result.setDataType(type);
return result;
}
}

RoutineReference RoutineReference(boolean aggregate):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.create;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.alter.AlterExtension;
import net.sf.jsqlparser.statement.grant.Grant;
import net.sf.jsqlparser.statement.RoutineReference;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class PostgreSqlRoutineArgumentTest {
@ParameterizedTest
@ValueSource(strings = {"integer", "text[]", "IN value integer", "OUT result text",
"INOUT state app.custom_type", "VARIADIC items text[]",
"\"argument name\" double precision"})
void privilegeAndExtensionArgumentsHaveEquivalentAst(String argument)
throws JSQLParserException {
Grant grant = (Grant) CCJSqlParserUtil.parse(
"GRANT EXECUTE ON FUNCTION app.f(" + argument + ") TO reader");
AlterExtension extension = (AlterExtension) CCJSqlParserUtil.parse(
"ALTER EXTENSION example ADD FUNCTION app.f(" + argument + ")");
RoutineReference.Argument access =
grant.getTarget().getRoutines().get(0).getArguments().get(0);
RoutineReference.Argument member = extension.getMember().getRoutine().getArguments().get(0);
assertEquals(access.getMode(), member.getMode());
assertEquals(access.getName(), member.getName());
assertEquals(access.getDataType().toString(), member.getDataType().toString());
assertRoundTrip(grant);
assertRoundTrip(extension);
}

@Test
void aggregateKeepsOrderByArgumentBoundary() throws JSQLParserException {
AlterExtension extension = (AlterExtension) CCJSqlParserUtil.parse(
"ALTER EXTENSION example ADD AGGREGATE app.percentile(double precision ORDER BY integer)");
RoutineReference routine = extension.getMember().getRoutine();
assertEquals(1, routine.getArguments().size());
assertEquals(1, routine.getOrderByArguments().size());
assertEquals("double precision", routine.getArguments().get(0).getDataType().toString());
assertRoundTrip(extension);
}

@ParameterizedTest
@ValueSource(strings = {
"GRANT EXECUTE ON FUNCTION app.f(integer ORDER BY integer) TO reader",
"GRANT EXECUTE ON FUNCTION app.f(*) TO reader",
"ALTER EXTENSION example ADD FUNCTION app.f(integer ORDER BY integer)",
"ALTER EXTENSION example ADD FUNCTION app.f(*)",
"GRANT EXECUTE ON FUNCTION app.f(IN) TO reader"
})
void aggregateSyntaxDoesNotLeakIntoOtherRoutineContexts(String sql) {
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql));
}

private static void assertRoundTrip(Statement statement) throws JSQLParserException {
assertEquals(statement.toString(), CCJSqlParserUtil.parse(statement.toString()).toString());
}
}
Loading