@@ -436,6 +436,12 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
436436 }
437437 }
438438
439+ /** Whether a MySQL characteristic of CREATE FUNCTION or PROCEDURE starts here. */
440+ private boolean isMySqlCreateRoutineCharacteristicAhead() {
441+ return isMySqlRoutineCharacteristicAhead(1) || isWordSequenceAhead(1, "DETERMINISTIC")
442+ || isWordSequenceAhead(1, "NOT", "DETERMINISTIC");
443+ }
444+
439445 /** Whether a MySQL characteristic of ALTER FUNCTION or PROCEDURE starts at the offset. */
440446 private boolean isMySqlRoutineCharacteristicAhead(int offset) {
441447 if (getToken(offset).kind == K_COMMENT) {
@@ -21683,12 +21689,14 @@ Statement Create():
2168321689 }
2168421690}
2168521691
21692+ /** With a MySQL dialect, the characteristics are also parsed; the tokens still render the statement. */
2168621693CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2168721694{
2168821695 CreateFunctionalStatement type = null;
2168921696 List<String> tokens = new LinkedList<String>();
2169021697 String statementType = null;
2169121698 MySqlDefiner definer = null;
21699+ List<RoutineCharacteristic> characteristics = new ArrayList<RoutineCharacteristic>();
2169221700}
2169321701{
2169421702 [ definer=MySqlRoutineDefiner() ]
@@ -21697,14 +21705,15 @@ CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2169721705 |
2169821706 <K_PROCEDURE> { statementType = "PROCEDURE"; }
2169921707 )
21700- tokens=captureFunctionBody()
21708+ tokens=captureFunctionBody(isMySqlDialect() ? characteristics : null )
2170121709 {
2170221710 if(statementType.equals("FUNCTION")) {
2170321711 type = new CreateFunction(isUsingOrReplace, tokens);
2170421712 }
2170521713 if(statementType.equals("PROCEDURE")) {
2170621714 type = new CreateProcedure(isUsingOrReplace, tokens);
2170721715 }
21716+ type.setFunctionDeclarationParts(tokens, characteristics);
2170821717 type.setDefiner(definer);
2170921718 return type;
2171021719 }
@@ -21750,7 +21759,7 @@ AlterRoutine AlterRoutine():
2175021759 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
2175121760 (
2175221761 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
21753- characteristic=MySqlRoutineCharacteristic() { result.getCharacteristics().add(characteristic); }
21762+ characteristic=MySqlRoutineCharacteristic(false ) { result.getCharacteristics().add(characteristic); }
2175421763 )+
2175521764 {
2175621765 requireDdlSyntax(result.getRoutineType() != AlterRoutine.RoutineType.ROUTINE
@@ -21828,7 +21837,8 @@ RoutineCharacteristic PostgreSqlRoutineCharacteristic():
2182821837 }
2182921838}
2183021839
21831- RoutineCharacteristic MySqlRoutineCharacteristic():
21840+ /** MySQL routine characteristic; only CREATE accepts [NOT] DETERMINISTIC. */
21841+ RoutineCharacteristic MySqlRoutineCharacteristic(boolean create):
2183221842{
2183321843 RoutineCharacteristic.Kind kind;
2183421844 RoutineCharacteristic.SecurityClause clause = null;
@@ -21848,6 +21858,12 @@ RoutineCharacteristic MySqlRoutineCharacteristic():
2184821858 |
2184921859 <K_NO> ContextualKeyword("SQL") { kind = RoutineCharacteristic.Kind.NO_SQL; }
2185021860 |
21861+ LOOKAHEAD({ create && isWordSequenceAhead(1, "DETERMINISTIC") }) ContextualKeyword("DETERMINISTIC")
21862+ { kind = RoutineCharacteristic.Kind.DETERMINISTIC; }
21863+ |
21864+ LOOKAHEAD({ create && isWordSequenceAhead(1, "NOT", "DETERMINISTIC") })
21865+ <K_NOT> ContextualKeyword("DETERMINISTIC") { kind = RoutineCharacteristic.Kind.NOT_DETERMINISTIC; }
21866+ |
2185121867 LOOKAHEAD({ isKeywordAhead("READS") }) ContextualKeyword("READS") ContextualKeyword("SQL") <K_DATA>
2185221868 { kind = RoutineCharacteristic.Kind.READS_SQL_DATA; }
2185321869 |
@@ -22648,16 +22664,29 @@ List<String> captureRest() {
2264822664 * PostgreSQL AS string bodies can use ordinary, escape or dollar quotes.
2264922665 * The boundary tracker distinguishes simple statements, declaration sections,
2265022666 * compound bodies, and expression CASE from procedural END qualifiers.
22667+ * With a list, MySQL characteristics between the header and the body are also
22668+ * parsed into it; their tokens are captured as well.
2265122669 */
2265222670
2265322671JAVACODE
22654- List<String> captureFunctionBody() {
22672+ List<String> captureFunctionBody(List<RoutineCharacteristic> characteristics ) {
2265522673 List<String> tokens = new LinkedList<String>();
2265622674 Token tok;
2265722675 RoutineBodyBoundary boundary = new RoutineBodyBoundary();
2265822676 while(true) {
2265922677 tok = getToken(1);
2266022678 int l = tokens.size();
22679+ if (characteristics != null && boundary.isBeforeBody() && isMySqlCreateRoutineCharacteristicAhead()) {
22680+ characteristics.add(MySqlRoutineCharacteristic(true));
22681+ for (Token consumed = tok; ; consumed = consumed.next) {
22682+ tokens.add(consumed.image);
22683+ boundary.accept(consumed, consumed == token ? getToken(1) : consumed.next);
22684+ if (consumed == token) {
22685+ break;
22686+ }
22687+ }
22688+ continue;
22689+ }
2266122690 if (boundary.endsAt(tok)) {
2266222691 if (tok.kind == ST_SEMICOLON) {
2266322692 tokens.add(tok.image);
0 commit comments