@@ -1420,6 +1420,20 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
14201420 return Dialect.MYSQL.name().equals(dialect) || Dialect.MARIADB.name().equals(dialect);
14211421 }
14221422
1423+ private boolean isMySqlSelectIntoAhead() {
1424+ if (getToken(1).kind != K_INTO) { return false; }
1425+ int kind = getToken(2).kind;
1426+ return kind == K_OUTFILE || kind == K_DUMPFILE
1427+ || isMySqlDialect() && (kind == S_AT_IDENTIFIER || kind == K_AT_SIGN);
1428+ }
1429+
1430+ private static UserVariable requireMySqlIntoVariable(UserVariable variable) throws ParseException {
1431+ if (variable.isDoubleAdd()) {
1432+ throw new ParseException("INTO requires a user variable with a single @ prefix");
1433+ }
1434+ return variable;
1435+ }
1436+
14231437 private ExplainStatement.OptionType postgresqlExplainOptionType(String name) throws ParseException {
14241438 try {
14251439 return ExplainStatement.OptionType.from(name);
@@ -6190,12 +6204,14 @@ PlainSelect PlainSelect() #PlainSelect:
61906204
61916205 selectItems=SelectItemsList()
61926206
6193- [ LOOKAHEAD(<K_INTO> (<K_OUTFILE> | <K_DUMPFILE>))
6194- mySqlSelectIntoClause = MySqlSelectIntoClause(MySqlSelectIntoClause.Position.BEFORE_FROM)
6195- { plainSelect.setMySqlSelectIntoClause(mySqlSelectIntoClause); }
6196- ]
61976207 [ LOOKAHEAD(<K_INTO>)
6198- intoTables = IntoClause() { plainSelect.setIntoTables(intoTables); }
6208+ (
6209+ LOOKAHEAD({ isMySqlSelectIntoAhead() })
6210+ mySqlSelectIntoClause = MySqlSelectIntoClause(MySqlSelectIntoClause.Position.BEFORE_FROM)
6211+ { plainSelect.setMySqlSelectIntoClause(mySqlSelectIntoClause); }
6212+ |
6213+ intoTables = IntoClause() { plainSelect.setIntoTables(intoTables); }
6214+ )
61996215 ]
62006216 [ LOOKAHEAD(2) <K_FROM> fromItem=FromItem()
62016217 [ LOOKAHEAD(2) lateralViews=LateralViews() ]
@@ -6287,7 +6303,12 @@ PlainSelect PlainSelect() #PlainSelect:
62876303 ]
62886304 [ LOOKAHEAD(2) interpolateElements = InterpolateClause() { plainSelect.setInterpolate(interpolateElements); } ]
62896305 ]
6290- [ LOOKAHEAD(<K_INTO> (<K_OUTFILE> | <K_DUMPFILE>))
6306+ [ LOOKAHEAD({ isMySqlSelectIntoAhead() })
6307+ {
6308+ if (mySqlSelectIntoClause != null || intoTables != null) {
6309+ throw new ParseException("Only one INTO clause is allowed per SELECT");
6310+ }
6311+ }
62916312 mySqlSelectIntoClause = MySqlSelectIntoClause(MySqlSelectIntoClause.Position.TRAILING)
62926313 { plainSelect.setMySqlSelectIntoClause(mySqlSelectIntoClause); }
62936314 ]
@@ -6941,6 +6962,8 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
69416962{
69426963 MySqlSelectIntoClause intoClause = new MySqlSelectIntoClause().withPosition(position);
69436964 Token token;
6965+ ExpressionList<UserVariable> variables = new ExpressionList<UserVariable>();
6966+ UserVariable variable;
69446967}
69456968{
69466969 <K_INTO>
@@ -6951,12 +6974,32 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
69516974 |
69526975 <K_DUMPFILE> { intoClause.setType(MySqlSelectIntoClause.Type.DUMPFILE); }
69536976 token=<S_CHAR_LITERAL> { intoClause.setFileName(new StringValue(token.image)); }
6977+ |
6978+ LOOKAHEAD({ isMySqlDialect() })
6979+ variable=MySqlIntoVariable() { variables.add(variable); }
6980+ ( "," variable=MySqlIntoVariable() { variables.add(variable); } )*
6981+ { intoClause.setType(MySqlSelectIntoClause.Type.VARIABLES); intoClause.setVariables(variables); }
69546982 )
69556983 {
69566984 return intoClause;
69576985 }
69586986}
69596987
6988+ UserVariable MySqlIntoVariable():
6989+ {
6990+ UserVariable variable;
6991+ Token name;
6992+ }
6993+ {
6994+ (
6995+ variable=UserVariable()
6996+ |
6997+ <K_AT_SIGN> (name=<S_CHAR_LITERAL> | name=<S_QUOTED_IDENTIFIER>)
6998+ { variable = new UserVariable("@" + name.image); }
6999+ )
7000+ { return requireMySqlIntoVariable(variable); }
7001+ }
7002+
69607003MySqlProcedureAnalyse MySqlProcedureAnalyse():
69617004{
69627005 MySqlProcedureAnalyse procedureAnalyse = new MySqlProcedureAnalyse();
0 commit comments