@@ -435,6 +435,12 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
435435 }
436436 }
437437
438+ /** Whether a MySQL characteristic of CREATE FUNCTION or PROCEDURE starts here. */
439+ private boolean isMySqlCreateRoutineCharacteristicAhead() {
440+ return isMySqlRoutineCharacteristicAhead(1) || isWordSequenceAhead(1, "DETERMINISTIC")
441+ || isWordSequenceAhead(1, "NOT", "DETERMINISTIC");
442+ }
443+
438444 /** Whether a MySQL characteristic of ALTER FUNCTION or PROCEDURE starts at the offset. */
439445 private boolean isMySqlRoutineCharacteristicAhead(int offset) {
440446 if (getToken(offset).kind == K_COMMENT) {
@@ -21479,12 +21485,14 @@ Statement Create():
2147921485 }
2148021486}
2148121487
21488+ /** With a MySQL dialect, the characteristics are also parsed; the tokens still render the statement. */
2148221489CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2148321490{
2148421491 CreateFunctionalStatement type = null;
2148521492 List<String> tokens = new LinkedList<String>();
2148621493 String statementType = null;
2148721494 MySqlDefiner definer = null;
21495+ List<RoutineCharacteristic> characteristics = new ArrayList<RoutineCharacteristic>();
2148821496}
2148921497{
2149021498 [ definer=MySqlRoutineDefiner() ]
@@ -21493,14 +21501,15 @@ CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2149321501 |
2149421502 <K_PROCEDURE> { statementType = "PROCEDURE"; }
2149521503 )
21496- tokens=captureFunctionBody()
21504+ tokens=captureFunctionBody(isMySqlDialect() ? characteristics : null )
2149721505 {
2149821506 if(statementType.equals("FUNCTION")) {
2149921507 type = new CreateFunction(isUsingOrReplace, tokens);
2150021508 }
2150121509 if(statementType.equals("PROCEDURE")) {
2150221510 type = new CreateProcedure(isUsingOrReplace, tokens);
2150321511 }
21512+ type.setFunctionDeclarationParts(tokens, characteristics);
2150421513 type.setDefiner(definer);
2150521514 return type;
2150621515 }
@@ -21546,7 +21555,7 @@ AlterRoutine AlterRoutine():
2154621555 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
2154721556 (
2154821557 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
21549- characteristic=MySqlRoutineCharacteristic() { result.getCharacteristics().add(characteristic); }
21558+ characteristic=MySqlRoutineCharacteristic(false ) { result.getCharacteristics().add(characteristic); }
2155021559 )+
2155121560 {
2155221561 requireDdlSyntax(result.getRoutineType() != AlterRoutine.RoutineType.ROUTINE
@@ -21624,7 +21633,8 @@ RoutineCharacteristic PostgreSqlRoutineCharacteristic():
2162421633 }
2162521634}
2162621635
21627- RoutineCharacteristic MySqlRoutineCharacteristic():
21636+ /** MySQL routine characteristic; only CREATE accepts [NOT] DETERMINISTIC. */
21637+ RoutineCharacteristic MySqlRoutineCharacteristic(boolean create):
2162821638{
2162921639 RoutineCharacteristic.Kind kind;
2163021640 RoutineCharacteristic.SecurityClause clause = null;
@@ -21644,6 +21654,12 @@ RoutineCharacteristic MySqlRoutineCharacteristic():
2164421654 |
2164521655 <K_NO> ContextualKeyword("SQL") { kind = RoutineCharacteristic.Kind.NO_SQL; }
2164621656 |
21657+ LOOKAHEAD({ create && isWordSequenceAhead(1, "DETERMINISTIC") }) ContextualKeyword("DETERMINISTIC")
21658+ { kind = RoutineCharacteristic.Kind.DETERMINISTIC; }
21659+ |
21660+ LOOKAHEAD({ create && isWordSequenceAhead(1, "NOT", "DETERMINISTIC") })
21661+ <K_NOT> ContextualKeyword("DETERMINISTIC") { kind = RoutineCharacteristic.Kind.NOT_DETERMINISTIC; }
21662+ |
2164721663 LOOKAHEAD({ isKeywordAhead("READS") }) ContextualKeyword("READS") ContextualKeyword("SQL") <K_DATA>
2164821664 { kind = RoutineCharacteristic.Kind.READS_SQL_DATA; }
2164921665 |
@@ -22444,16 +22460,29 @@ List<String> captureRest() {
2244422460 * PostgreSQL AS string bodies can use ordinary, escape or dollar quotes.
2244522461 * The boundary tracker distinguishes simple statements, declaration sections,
2244622462 * compound bodies, and expression CASE from procedural END qualifiers.
22463+ * With a list, MySQL characteristics between the header and the body are also
22464+ * parsed into it; their tokens are captured as well.
2244722465 */
2244822466
2244922467JAVACODE
22450- List<String> captureFunctionBody() {
22468+ List<String> captureFunctionBody(List<RoutineCharacteristic> characteristics ) {
2245122469 List<String> tokens = new LinkedList<String>();
2245222470 Token tok;
2245322471 RoutineBodyBoundary boundary = new RoutineBodyBoundary();
2245422472 while(true) {
2245522473 tok = getToken(1);
2245622474 int l = tokens.size();
22475+ if (characteristics != null && boundary.isBeforeBody() && isMySqlCreateRoutineCharacteristicAhead()) {
22476+ characteristics.add(MySqlRoutineCharacteristic(true));
22477+ for (Token consumed = tok; ; consumed = consumed.next) {
22478+ tokens.add(consumed.image);
22479+ boundary.accept(consumed, consumed == token ? getToken(1) : consumed.next);
22480+ if (consumed == token) {
22481+ break;
22482+ }
22483+ }
22484+ continue;
22485+ }
2245722486 if (boundary.endsAt(tok)) {
2245822487 if (tok.kind == ST_SEMICOLON) {
2245922488 tokens.add(tok.image);
0 commit comments