@@ -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 ]
@@ -6921,6 +6942,8 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
69216942{
69226943 MySqlSelectIntoClause intoClause = new MySqlSelectIntoClause().withPosition(position);
69236944 Token token;
6945+ ExpressionList<UserVariable> variables = new ExpressionList<UserVariable>();
6946+ UserVariable variable;
69246947}
69256948{
69266949 <K_INTO>
@@ -6931,12 +6954,32 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
69316954 |
69326955 <K_DUMPFILE> { intoClause.setType(MySqlSelectIntoClause.Type.DUMPFILE); }
69336956 token=<S_CHAR_LITERAL> { intoClause.setFileName(new StringValue(token.image)); }
6957+ |
6958+ LOOKAHEAD({ isMySqlDialect() })
6959+ variable=MySqlIntoVariable() { variables.add(variable); }
6960+ ( "," variable=MySqlIntoVariable() { variables.add(variable); } )*
6961+ { intoClause.setType(MySqlSelectIntoClause.Type.VARIABLES); intoClause.setVariables(variables); }
69346962 )
69356963 {
69366964 return intoClause;
69376965 }
69386966}
69396967
6968+ UserVariable MySqlIntoVariable():
6969+ {
6970+ UserVariable variable;
6971+ Token name;
6972+ }
6973+ {
6974+ (
6975+ variable=UserVariable()
6976+ |
6977+ <K_AT_SIGN> (name=<S_CHAR_LITERAL> | name=<S_QUOTED_IDENTIFIER>)
6978+ { variable = new UserVariable("@" + name.image); }
6979+ )
6980+ { return requireMySqlIntoVariable(variable); }
6981+ }
6982+
69406983MySqlProcedureAnalyse MySqlProcedureAnalyse():
69416984{
69426985 MySqlProcedureAnalyse procedureAnalyse = new MySqlProcedureAnalyse();
0 commit comments