@@ -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) {
@@ -22574,12 +22580,14 @@ Statement Create():
2257422580 }
2257522581}
2257622582
22583+ /** With a MySQL dialect, the characteristics are also parsed; the tokens still render the statement. */
2257722584CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2257822585{
2257922586 CreateFunctionalStatement type = null;
2258022587 List<String> tokens = new LinkedList<String>();
2258122588 String statementType = null;
2258222589 MySqlDefiner definer = null;
22590+ List<RoutineCharacteristic> characteristics = new ArrayList<RoutineCharacteristic>();
2258322591}
2258422592{
2258522593 [ definer=MySqlRoutineDefiner() ]
@@ -22588,14 +22596,15 @@ CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2258822596 |
2258922597 <K_PROCEDURE> { statementType = "PROCEDURE"; }
2259022598 )
22591- tokens=captureFunctionBody()
22599+ tokens=captureFunctionBody(isMySqlDialect() ? characteristics : null )
2259222600 {
2259322601 if(statementType.equals("FUNCTION")) {
2259422602 type = new CreateFunction(isUsingOrReplace, tokens);
2259522603 }
2259622604 if(statementType.equals("PROCEDURE")) {
2259722605 type = new CreateProcedure(isUsingOrReplace, tokens);
2259822606 }
22607+ type.setFunctionDeclarationParts(tokens, characteristics);
2259922608 type.setDefiner(definer);
2260022609 return type;
2260122610 }
@@ -22641,7 +22650,7 @@ AlterRoutine AlterRoutine():
2264122650 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
2264222651 (
2264322652 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
22644- characteristic=MySqlRoutineCharacteristic() { result.getCharacteristics().add(characteristic); }
22653+ characteristic=MySqlRoutineCharacteristic(false ) { result.getCharacteristics().add(characteristic); }
2264522654 )+
2264622655 {
2264722656 requireDdlSyntax(result.getRoutineType() != AlterRoutine.RoutineType.ROUTINE
@@ -22719,7 +22728,8 @@ RoutineCharacteristic PostgreSqlRoutineCharacteristic():
2271922728 }
2272022729}
2272122730
22722- RoutineCharacteristic MySqlRoutineCharacteristic():
22731+ /** MySQL routine characteristic; only CREATE accepts [NOT] DETERMINISTIC. */
22732+ RoutineCharacteristic MySqlRoutineCharacteristic(boolean create):
2272322733{
2272422734 RoutineCharacteristic.Kind kind;
2272522735 RoutineCharacteristic.SecurityClause clause = null;
@@ -22739,6 +22749,12 @@ RoutineCharacteristic MySqlRoutineCharacteristic():
2273922749 |
2274022750 <K_NO> ContextualKeyword("SQL") { kind = RoutineCharacteristic.Kind.NO_SQL; }
2274122751 |
22752+ LOOKAHEAD({ create && isWordSequenceAhead(1, "DETERMINISTIC") }) ContextualKeyword("DETERMINISTIC")
22753+ { kind = RoutineCharacteristic.Kind.DETERMINISTIC; }
22754+ |
22755+ LOOKAHEAD({ create && isWordSequenceAhead(1, "NOT", "DETERMINISTIC") })
22756+ <K_NOT> ContextualKeyword("DETERMINISTIC") { kind = RoutineCharacteristic.Kind.NOT_DETERMINISTIC; }
22757+ |
2274222758 LOOKAHEAD({ isKeywordAhead("READS") }) ContextualKeyword("READS") ContextualKeyword("SQL") <K_DATA>
2274322759 { kind = RoutineCharacteristic.Kind.READS_SQL_DATA; }
2274422760 |
@@ -23541,16 +23557,29 @@ List<String> captureRest() {
2354123557 * PostgreSQL AS string bodies can use ordinary, escape or dollar quotes.
2354223558 * The boundary tracker distinguishes simple statements, declaration sections,
2354323559 * compound bodies, and expression CASE from procedural END qualifiers.
23560+ * With a list, MySQL characteristics between the header and the body are also
23561+ * parsed into it; their tokens are captured as well.
2354423562 */
2354523563
2354623564JAVACODE
23547- List<String> captureFunctionBody() {
23565+ List<String> captureFunctionBody(List<RoutineCharacteristic> characteristics ) {
2354823566 List<String> tokens = new LinkedList<String>();
2354923567 Token tok;
2355023568 RoutineBodyBoundary boundary = new RoutineBodyBoundary();
2355123569 while(true) {
2355223570 tok = getToken(1);
2355323571 int l = tokens.size();
23572+ if (characteristics != null && boundary.isBeforeBody() && isMySqlCreateRoutineCharacteristicAhead()) {
23573+ characteristics.add(MySqlRoutineCharacteristic(true));
23574+ for (Token consumed = tok; ; consumed = consumed.next) {
23575+ tokens.add(consumed.image);
23576+ boundary.accept(consumed, consumed == token ? getToken(1) : consumed.next);
23577+ if (consumed == token) {
23578+ break;
23579+ }
23580+ }
23581+ continue;
23582+ }
2355423583 if (boundary.endsAt(tok)) {
2355523584 if (tok.kind == ST_SEMICOLON) {
2355623585 tokens.add(tok.image);
0 commit comments