Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import net.sf.jsqlparser.statement.select.PivotVisitor;
import net.sf.jsqlparser.statement.select.PivotVisitorAdapter;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.MySqlSelectIntoClause;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectItemVisitor;
Expand Down Expand Up @@ -795,6 +796,12 @@ public <S> Void visit(PlainSelect plainSelect, S context) {
analysis.certain.remove(StmtFeature.RETURNS_RESULT_SET);
}

MySqlSelectIntoClause mySqlInto = plainSelect.getMySqlSelectIntoClause();
if (mySqlInto != null && mySqlInto.getType() == MySqlSelectIntoClause.Type.VARIABLES) {
analysis.certain(StmtFeature.MODIFIES_SESSION);
analysis.certain.remove(StmtFeature.RETURNS_RESULT_SET);
}

if (plainSelect.getForMode() != null) {
// FOR UPDATE / FOR SHARE take row locks
analysis.certain(StmtFeature.MODIFIES_TRANSACTION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
package net.sf.jsqlparser.statement.select;

import java.io.Serializable;
import java.util.function.Consumer;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.UserVariable;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

public class MySqlSelectIntoClause extends ASTNodeAccessImpl implements Serializable {
Expand All @@ -20,7 +24,7 @@ public enum Position {
}

public enum Type {
OUTFILE, DUMPFILE
OUTFILE, DUMPFILE, VARIABLES
}

public enum FieldsKeyword {
Expand All @@ -30,6 +34,7 @@ public enum FieldsKeyword {
private Position position = Position.TRAILING;
private Type type;
private StringValue fileName;
private ExpressionList<UserVariable> variables;
private String characterSet;
private FieldsKeyword fieldsKeyword;
private StringValue fieldsTerminatedBy;
Expand Down Expand Up @@ -60,6 +65,14 @@ public void setType(Type type) {
this.type = type;
}

public ExpressionList<UserVariable> getVariables() {
return variables;
}

public void setVariables(ExpressionList<UserVariable> variables) {
this.variables = variables;
}

public StringValue getFileName() {
return fileName;
}
Expand Down Expand Up @@ -142,7 +155,19 @@ public boolean hasLinesClause() {
}

public StringBuilder appendTo(StringBuilder builder) {
builder.append("INTO ").append(type);
return appendTo(builder, expression -> builder.append(expression));
}

/** Shares INTO rendering while allowing deparsers to visit variable targets. */
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionRenderer) {
builder.append("INTO ");
if (type == Type.VARIABLES) {
if (variables != null) {
expressionRenderer.accept(variables);
}
return builder;
}
builder.append(type);
appendFileName(builder);
appendCharacterSet(builder);
appendFieldsClause(builder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public <S> T visit(PlainSelect plainSelect, S context) {

if (plainSelect.getMySqlSelectIntoClause() != null) {
MySqlSelectIntoClause mySqlSelectIntoClause = plainSelect.getMySqlSelectIntoClause();
expressionVisitor.visitExpressions(mySqlSelectIntoClause.getVariables(), context);
expressionVisitor.visitExpression(mySqlSelectIntoClause.getFileName(), context);
expressionVisitor.visitExpression(mySqlSelectIntoClause.getFieldsTerminatedBy(),
context);
Expand Down
21 changes: 11 additions & 10 deletions src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,7 @@ public <S> StringBuilder visit(PlainSelect plainSelect, S context) {
}
}

if (plainSelect.getMySqlSelectIntoClause() != null
&& plainSelect.getMySqlSelectIntoClause()
.getPosition() == MySqlSelectIntoClause.Position.BEFORE_FROM) {
builder.append(" ").append(plainSelect.getMySqlSelectIntoClause());
}
deparseMySqlSelectInto(plainSelect, MySqlSelectIntoClause.Position.BEFORE_FROM, context);

if (plainSelect.getFromItem() != null) {
builder.append(" FROM ");
Expand Down Expand Up @@ -401,11 +397,7 @@ public <S> StringBuilder visit(PlainSelect plainSelect, S context) {
if (plainSelect.isForUpdateBeforeOrderBy()) {
deparseOrderByElementsClause(plainSelect, plainSelect.getOrderByElements());
}
if (plainSelect.getMySqlSelectIntoClause() != null
&& plainSelect.getMySqlSelectIntoClause()
.getPosition() == MySqlSelectIntoClause.Position.TRAILING) {
builder.append(" ").append(plainSelect.getMySqlSelectIntoClause());
}
deparseMySqlSelectInto(plainSelect, MySqlSelectIntoClause.Position.TRAILING, context);
if (plainSelect.getSettings() != null && !plainSelect.getSettings().isEmpty()) {
builder.append(" SETTINGS ");
deparseUpdateSets(plainSelect.getSettings(), builder, expressionVisitor);
Expand Down Expand Up @@ -720,6 +712,15 @@ public void setExpressionVisitor(ExpressionVisitor<StringBuilder> visitor) {
expressionVisitor = visitor;
}

private <S> void deparseMySqlSelectInto(PlainSelect select,
MySqlSelectIntoClause.Position position, S context) {
MySqlSelectIntoClause into = select.getMySqlSelectIntoClause();
if (into != null && into.getPosition() == position) {
builder.append(' ');
into.appendTo(builder, expression -> expression.accept(expressionVisitor, context));
}
}

@SuppressWarnings({"PMD.CyclomaticComplexity"})
public void deparseJoin(Join join) {
if (join.isGlobal()) {
Expand Down
55 changes: 49 additions & 6 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,20 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
return Dialect.MYSQL.name().equals(dialect) || Dialect.MARIADB.name().equals(dialect);
}

private boolean isMySqlSelectIntoAhead() {
if (getToken(1).kind != K_INTO) { return false; }
int kind = getToken(2).kind;
return kind == K_OUTFILE || kind == K_DUMPFILE
|| isMySqlDialect() && (kind == S_AT_IDENTIFIER || kind == K_AT_SIGN);
}

private static UserVariable requireMySqlIntoVariable(UserVariable variable) throws ParseException {
if (variable.isDoubleAdd()) {
throw new ParseException("INTO requires a user variable with a single @ prefix");
}
return variable;
}

private ExplainStatement.OptionType postgresqlExplainOptionType(String name) throws ParseException {
try {
return ExplainStatement.OptionType.from(name);
Expand Down Expand Up @@ -6190,12 +6204,14 @@ PlainSelect PlainSelect() #PlainSelect:

selectItems=SelectItemsList()

[ LOOKAHEAD(<K_INTO> (<K_OUTFILE> | <K_DUMPFILE>))
mySqlSelectIntoClause = MySqlSelectIntoClause(MySqlSelectIntoClause.Position.BEFORE_FROM)
{ plainSelect.setMySqlSelectIntoClause(mySqlSelectIntoClause); }
]
[ LOOKAHEAD(<K_INTO>)
intoTables = IntoClause() { plainSelect.setIntoTables(intoTables); }
(
LOOKAHEAD({ isMySqlSelectIntoAhead() })
mySqlSelectIntoClause = MySqlSelectIntoClause(MySqlSelectIntoClause.Position.BEFORE_FROM)
{ plainSelect.setMySqlSelectIntoClause(mySqlSelectIntoClause); }
|
intoTables = IntoClause() { plainSelect.setIntoTables(intoTables); }
)
]
[ LOOKAHEAD(2) <K_FROM> fromItem=FromItem()
[ LOOKAHEAD(2) lateralViews=LateralViews() ]
Expand Down Expand Up @@ -6287,7 +6303,12 @@ PlainSelect PlainSelect() #PlainSelect:
]
[ LOOKAHEAD(2) interpolateElements = InterpolateClause() { plainSelect.setInterpolate(interpolateElements); } ]
]
[ LOOKAHEAD(<K_INTO> (<K_OUTFILE> | <K_DUMPFILE>))
[ LOOKAHEAD({ isMySqlSelectIntoAhead() })
{
if (mySqlSelectIntoClause != null || intoTables != null) {
throw new ParseException("Only one INTO clause is allowed per SELECT");
}
}
mySqlSelectIntoClause = MySqlSelectIntoClause(MySqlSelectIntoClause.Position.TRAILING)
{ plainSelect.setMySqlSelectIntoClause(mySqlSelectIntoClause); }
]
Expand Down Expand Up @@ -6941,6 +6962,8 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
{
MySqlSelectIntoClause intoClause = new MySqlSelectIntoClause().withPosition(position);
Token token;
ExpressionList<UserVariable> variables = new ExpressionList<UserVariable>();
UserVariable variable;
}
{
<K_INTO>
Expand All @@ -6951,12 +6974,32 @@ MySqlSelectIntoClause MySqlSelectIntoClause(MySqlSelectIntoClause.Position posit
|
<K_DUMPFILE> { intoClause.setType(MySqlSelectIntoClause.Type.DUMPFILE); }
token=<S_CHAR_LITERAL> { intoClause.setFileName(new StringValue(token.image)); }
|
LOOKAHEAD({ isMySqlDialect() })
variable=MySqlIntoVariable() { variables.add(variable); }
( "," variable=MySqlIntoVariable() { variables.add(variable); } )*
{ intoClause.setType(MySqlSelectIntoClause.Type.VARIABLES); intoClause.setVariables(variables); }
)
{
return intoClause;
}
}

UserVariable MySqlIntoVariable():
{
UserVariable variable;
Token name;
}
{
(
variable=UserVariable()
|
<K_AT_SIGN> (name=<S_CHAR_LITERAL> | name=<S_QUOTED_IDENTIFIER>)
{ variable = new UserVariable("@" + name.image); }
)
{ return requireMySqlIntoVariable(variable); }
}

MySqlProcedureAnalyse MySqlProcedureAnalyse():
{
MySqlProcedureAnalyse procedureAnalyse = new MySqlProcedureAnalyse();
Expand Down
6 changes: 6 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,12 @@ One grammar covers every supported RDBMS, but a few pieces of syntax mean differ

Features set explicitly *after* the preset win over it.

MySQL user-variable targets in ``SELECT ... INTO @variable`` require
``Dialect.MYSQL`` or ``Dialect.MARIADB``. They are stored in
``PlainSelect.getMySqlSelectIntoClause().getVariables()`` as ``UserVariable``
expressions, with the clause position preserved before ``FROM`` or at the end
of the query. They are not table targets in ``getIntoTables()``.

With ``Dialect.SQLSERVER``, ``PRIMARY KEY NONCLUSTERED (id)`` and
``UNIQUE CLUSTERED (id)`` store their clustering option in ``Index.getClustering()``
for both ``CREATE TABLE`` and ``ALTER TABLE``. Without that dialect, these words
Expand Down
Loading
Loading