@@ -437,6 +437,12 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
437437 }
438438 }
439439
440+ /** Whether a MySQL characteristic of CREATE FUNCTION or PROCEDURE starts here. */
441+ private boolean isMySqlCreateRoutineCharacteristicAhead() {
442+ return isMySqlRoutineCharacteristicAhead(1) || isWordSequenceAhead(1, "DETERMINISTIC")
443+ || isWordSequenceAhead(1, "NOT", "DETERMINISTIC");
444+ }
445+
440446 /** Whether a MySQL characteristic of ALTER FUNCTION or PROCEDURE starts at the offset. */
441447 private boolean isMySqlRoutineCharacteristicAhead(int offset) {
442448 if (getToken(offset).kind == K_COMMENT) {
@@ -22835,12 +22841,14 @@ Statement Create():
2283522841 }
2283622842}
2283722843
22844+ /** With a MySQL dialect, the characteristics are also parsed; the tokens still render the statement. */
2283822845CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2283922846{
2284022847 CreateFunctionalStatement type = null;
2284122848 List<String> tokens = new LinkedList<String>();
2284222849 String statementType = null;
2284322850 MySqlDefiner definer = null;
22851+ List<RoutineCharacteristic> characteristics = new ArrayList<RoutineCharacteristic>();
2284422852}
2284522853{
2284622854 [ definer=MySqlRoutineDefiner() ]
@@ -22849,14 +22857,15 @@ CreateFunctionalStatement CreateFunctionStatement(boolean isUsingOrReplace):
2284922857 |
2285022858 <K_PROCEDURE> { statementType = "PROCEDURE"; }
2285122859 )
22852- tokens=captureFunctionBody()
22860+ tokens=captureFunctionBody(isMySqlDialect() ? characteristics : null )
2285322861 {
2285422862 if(statementType.equals("FUNCTION")) {
2285522863 type = new CreateFunction(isUsingOrReplace, tokens);
2285622864 }
2285722865 if(statementType.equals("PROCEDURE")) {
2285822866 type = new CreateProcedure(isUsingOrReplace, tokens);
2285922867 }
22868+ type.setFunctionDeclarationParts(tokens, characteristics);
2286022869 type.setDefiner(definer);
2286122870 return type;
2286222871 }
@@ -22902,7 +22911,7 @@ AlterRoutine AlterRoutine():
2290222911 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
2290322912 (
2290422913 LOOKAHEAD({ isMySqlRoutineCharacteristicAhead(1) })
22905- characteristic=MySqlRoutineCharacteristic() { result.getCharacteristics().add(characteristic); }
22914+ characteristic=MySqlRoutineCharacteristic(false ) { result.getCharacteristics().add(characteristic); }
2290622915 )+
2290722916 {
2290822917 requireDdlSyntax(result.getRoutineType() != AlterRoutine.RoutineType.ROUTINE
@@ -22980,7 +22989,8 @@ RoutineCharacteristic PostgreSqlRoutineCharacteristic():
2298022989 }
2298122990}
2298222991
22983- RoutineCharacteristic MySqlRoutineCharacteristic():
22992+ /** MySQL routine characteristic; only CREATE accepts [NOT] DETERMINISTIC. */
22993+ RoutineCharacteristic MySqlRoutineCharacteristic(boolean create):
2298422994{
2298522995 RoutineCharacteristic.Kind kind;
2298622996 RoutineCharacteristic.SecurityClause clause = null;
@@ -23000,6 +23010,12 @@ RoutineCharacteristic MySqlRoutineCharacteristic():
2300023010 |
2300123011 <K_NO> ContextualKeyword("SQL") { kind = RoutineCharacteristic.Kind.NO_SQL; }
2300223012 |
23013+ LOOKAHEAD({ create && isWordSequenceAhead(1, "DETERMINISTIC") }) ContextualKeyword("DETERMINISTIC")
23014+ { kind = RoutineCharacteristic.Kind.DETERMINISTIC; }
23015+ |
23016+ LOOKAHEAD({ create && isWordSequenceAhead(1, "NOT", "DETERMINISTIC") })
23017+ <K_NOT> ContextualKeyword("DETERMINISTIC") { kind = RoutineCharacteristic.Kind.NOT_DETERMINISTIC; }
23018+ |
2300323019 LOOKAHEAD({ isKeywordAhead("READS") }) ContextualKeyword("READS") ContextualKeyword("SQL") <K_DATA>
2300423020 { kind = RoutineCharacteristic.Kind.READS_SQL_DATA; }
2300523021 |
@@ -23836,16 +23852,29 @@ List<String> captureRest() {
2383623852 * PostgreSQL AS string bodies can use ordinary, escape or dollar quotes.
2383723853 * The boundary tracker distinguishes simple statements, declaration sections,
2383823854 * compound bodies, and expression CASE from procedural END qualifiers.
23855+ * With a list, MySQL characteristics between the header and the body are also
23856+ * parsed into it; their tokens are captured as well.
2383923857 */
2384023858
2384123859JAVACODE
23842- List<String> captureFunctionBody() {
23860+ List<String> captureFunctionBody(List<RoutineCharacteristic> characteristics ) {
2384323861 List<String> tokens = new LinkedList<String>();
2384423862 Token tok;
2384523863 RoutineBodyBoundary boundary = new RoutineBodyBoundary();
2384623864 while(true) {
2384723865 tok = getToken(1);
2384823866 int l = tokens.size();
23867+ if (characteristics != null && boundary.isBeforeBody() && isMySqlCreateRoutineCharacteristicAhead()) {
23868+ characteristics.add(MySqlRoutineCharacteristic(true));
23869+ for (Token consumed = tok; ; consumed = consumed.next) {
23870+ tokens.add(consumed.image);
23871+ boundary.accept(consumed, consumed == token ? getToken(1) : consumed.next);
23872+ if (consumed == token) {
23873+ break;
23874+ }
23875+ }
23876+ continue;
23877+ }
2384923878 if (boundary.endsAt(tok)) {
2385023879 if (tok.kind == ST_SEMICOLON) {
2385123880 tokens.add(tok.image);
0 commit comments