Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
| **PostgreSQL ownership** | `DROP OWNED` · `REASSIGN OWNED` |
| **MySQL table maintenance** | `ANALYZE` · `CHECK` · `OPTIMIZE` · `REPAIR TABLE` |
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |
| **Routine changes** | `ALTER FUNCTION` · `ALTER PROCEDURE` · `ALTER ROUTINE` · PostgreSQL `ALTER TRIGGER` |
| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` |
| **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` |

Expand Down
24 changes: 23 additions & 1 deletion src/main/java/net/sf/jsqlparser/parser/RoutineBodyBoundary.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ private enum Block {
private boolean handlerAction;
private boolean statementStart = true;

/**
* Tracks a compound statement without a routine header, such as a MySQL trigger body. Its
* tokens belong to the body from the start, as after a routine's parameter list.
*/
static RoutineBodyBoundary forCompoundStatement() {
RoutineBodyBoundary boundary = new RoutineBodyBoundary();
boundary.hasParameters = true;
return boundary;
}

boolean endsAt(Token token) throws ParseException {
if (token.kind == EOF) {
if (!blocks.isEmpty()) {
Expand All @@ -50,6 +60,15 @@ boolean endsAt(Token token) throws ParseException {
&& !declarations && (hasParameters || quotedBody || completed);
}

/**
* Whether the routine header, including a return type, is complete and the body has not
* started, which is where MySQL routine characteristics appear.
*/
boolean isBeforeBody() {
return hasParameters && parentheses == 0 && !bodyStarted && !returnTypeName
&& !declarations && !quotedBody && !completed;
}

boolean startsQuotedBody(Token token, Token next) {
return parentheses == 0 && !compoundBody && !quotedBody && token.kind == K_AS
&& next.kind == S_CHAR_LITERAL
Expand Down Expand Up @@ -218,7 +237,10 @@ private void acceptStatementToken(Token token, Token next) {
}
if (!bodyStarted) {
// SET may also be a PostgreSQL header option; AS remains recognizable above.
if (!isSimpleStatementStart(kind)) {
// CHARACTER SET and CHAR SET belong to a MySQL return type.
boolean characterSet =
kind == K_SET && (previousKind == K_CHARACTER || previousKind == K_CHAR);
if (!isSimpleStatementStart(kind) || characterSet) {
return;
}
bodyStarted = true;
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,18 @@ public enum Feature {
functionalStatement,

alterFunction, alterProcedure, createOrAlterRoutine,
/**
* PostgreSQL "ALTER ROUTINE" statement is allowed
*
* @see net.sf.jsqlparser.statement.alter.AlterRoutine
*/
alterRoutine,
/**
* PostgreSQL "ALTER TRIGGER" statement is allowed
*
* @see net.sf.jsqlparser.statement.alter.AlterTrigger
*/
alterTrigger,
/**
* SQL block starting with "BEGIN" and ends with "END" statement is allowed
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

/**
* A PostgreSQL {@code SET} or {@code RESET} of a configuration parameter, shared by statements that
* store parameter defaults, such as {@code ALTER ROLE} and {@code ALTER DATABASE}:
* store parameter defaults: {@code ALTER ROLE}, {@code ALTER DATABASE} and the routine
* characteristics of {@code ALTER FUNCTION}, {@code ALTER PROCEDURE} and {@code ALTER ROUTINE}:
*
* <pre>
* SET parameter { TO | = } { value [, ...] | DEFAULT }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ public enum Operation {
}

private Operation operation = Operation.CREATE;
private MySqlDefiner definer;
private FunctionReturnType returnType;
private List<String> routineBodyParts;
private List<RoutineCharacteristic> characteristics = Collections.emptyList();

public Operation getOperation() {
return operation;
Expand All @@ -42,6 +44,19 @@ public void setOperation(Operation operation) {
this.operation = operation;
}

/**
* The MySQL {@code DEFINER} account, rendered after {@code CREATE [OR REPLACE]}.
*
* @return the definer, or {@code null}
*/
public MySqlDefiner getDefiner() {
return definer;
}

public void setDefiner(MySqlDefiner definer) {
this.definer = definer;
}

public FunctionReturnType getReturnType() {
return returnType;
}
Expand Down Expand Up @@ -85,8 +100,38 @@ public List<String> getFunctionDeclarationParts() {
return functionDeclarationParts;
}

/**
* Replaces the declaration parts. The {@link #getCharacteristics() characteristics} described
* the replaced parts, so they are cleared.
*
* @param functionDeclarationParts the new parts
*/
public void setFunctionDeclarationParts(List<String> functionDeclarationParts) {
setFunctionDeclarationParts(functionDeclarationParts, Collections.emptyList());
}

/**
* Replaces the declaration parts together with the characteristics written in them.
*
* @param functionDeclarationParts the new parts, which keep rendering the statement
* @param characteristics the characteristics among the parts, in source order
*/
public void setFunctionDeclarationParts(List<String> functionDeclarationParts,
List<RoutineCharacteristic> characteristics) {
this.functionDeclarationParts = functionDeclarationParts;
this.characteristics = List.copyOf(characteristics);
}

/**
* The MySQL characteristics of {@code CREATE FUNCTION|PROCEDURE}, such as {@code COMMENT},
* {@code DETERMINISTIC} or {@code SQL SECURITY}, parsed with a MySQL or MariaDB dialect. They
* describe tokens of {@link #getFunctionDeclarationParts()}, which still render the statement,
* so the list is read-only.
*
* @return the characteristics in source order, empty if none were parsed
*/
public List<RoutineCharacteristic> getCharacteristics() {
return characteristics;
}

/**
Expand Down Expand Up @@ -139,6 +184,9 @@ public StringBuilder appendTo(StringBuilder builder, Consumer<TableElement> prin
if (orReplace && operation == Operation.CREATE) {
builder.append("OR REPLACE ");
}
if (definer != null) {
builder.append("DEFINER = ").append(definer).append(' ');
}
builder.append(kind).append(' ');
return appendDeclarationTo(builder, printer);
}
Expand All @@ -160,19 +208,23 @@ public CreateFunctionalStatement withFunctionDeclarationParts(
return this;
}

/** Appends declaration parts and keeps the {@link #getCharacteristics() characteristics}. */
public CreateFunctionalStatement addFunctionDeclarationParts(
String... functionDeclarationParts) {
List<String> collection =
Optional.ofNullable(getFunctionDeclarationParts()).orElseGet(ArrayList::new);
Collections.addAll(collection, functionDeclarationParts);
return this.withFunctionDeclarationParts(collection);
setFunctionDeclarationParts(collection, characteristics);
return this;
}

/** Appends declaration parts and keeps the {@link #getCharacteristics() characteristics}. */
public CreateFunctionalStatement addFunctionDeclarationParts(
Collection<String> functionDeclarationParts) {
List<String> collection =
Optional.ofNullable(getFunctionDeclarationParts()).orElseGet(ArrayList::new);
collection.addAll(functionDeclarationParts);
return this.withFunctionDeclarationParts(collection);
setFunctionDeclarationParts(collection, characteristics);
return this;
}
}
Loading
Loading