Skip to content

Commit c08c9c8

Browse files
committed
Add support MySQL SELECT INTO OUTFILE/DUMPFILE
1 parent bd1a146 commit c08c9c8

File tree

7 files changed

+440
-2
lines changed

7 files changed

+440
-2
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.select;
11+
12+
import java.io.Serializable;
13+
import net.sf.jsqlparser.expression.StringValue;
14+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
15+
16+
public class MySqlSelectIntoClause extends ASTNodeAccessImpl implements Serializable {
17+
18+
public enum Position {
19+
BEFORE_FROM, TRAILING
20+
}
21+
22+
public enum Type {
23+
OUTFILE, DUMPFILE
24+
}
25+
26+
public enum FieldsKeyword {
27+
FIELDS, COLUMNS
28+
}
29+
30+
private Position position = Position.TRAILING;
31+
private Type type;
32+
private StringValue fileName;
33+
private String characterSet;
34+
private FieldsKeyword fieldsKeyword;
35+
private StringValue fieldsTerminatedBy;
36+
private boolean fieldsOptionallyEnclosed;
37+
private StringValue fieldsEnclosedBy;
38+
private StringValue fieldsEscapedBy;
39+
private StringValue linesStartingBy;
40+
private StringValue linesTerminatedBy;
41+
42+
public Position getPosition() {
43+
return position;
44+
}
45+
46+
public void setPosition(Position position) {
47+
this.position = position;
48+
}
49+
50+
public MySqlSelectIntoClause withPosition(Position position) {
51+
this.setPosition(position);
52+
return this;
53+
}
54+
55+
public Type getType() {
56+
return type;
57+
}
58+
59+
public void setType(Type type) {
60+
this.type = type;
61+
}
62+
63+
public StringValue getFileName() {
64+
return fileName;
65+
}
66+
67+
public void setFileName(StringValue fileName) {
68+
this.fileName = fileName;
69+
}
70+
71+
public String getCharacterSet() {
72+
return characterSet;
73+
}
74+
75+
public void setCharacterSet(String characterSet) {
76+
this.characterSet = characterSet;
77+
}
78+
79+
public FieldsKeyword getFieldsKeyword() {
80+
return fieldsKeyword;
81+
}
82+
83+
public void setFieldsKeyword(FieldsKeyword fieldsKeyword) {
84+
this.fieldsKeyword = fieldsKeyword;
85+
}
86+
87+
public StringValue getFieldsTerminatedBy() {
88+
return fieldsTerminatedBy;
89+
}
90+
91+
public void setFieldsTerminatedBy(StringValue fieldsTerminatedBy) {
92+
this.fieldsTerminatedBy = fieldsTerminatedBy;
93+
}
94+
95+
public boolean isFieldsOptionallyEnclosed() {
96+
return fieldsOptionallyEnclosed;
97+
}
98+
99+
public void setFieldsOptionallyEnclosed(boolean fieldsOptionallyEnclosed) {
100+
this.fieldsOptionallyEnclosed = fieldsOptionallyEnclosed;
101+
}
102+
103+
public StringValue getFieldsEnclosedBy() {
104+
return fieldsEnclosedBy;
105+
}
106+
107+
public void setFieldsEnclosedBy(StringValue fieldsEnclosedBy) {
108+
this.fieldsEnclosedBy = fieldsEnclosedBy;
109+
}
110+
111+
public StringValue getFieldsEscapedBy() {
112+
return fieldsEscapedBy;
113+
}
114+
115+
public void setFieldsEscapedBy(StringValue fieldsEscapedBy) {
116+
this.fieldsEscapedBy = fieldsEscapedBy;
117+
}
118+
119+
public StringValue getLinesStartingBy() {
120+
return linesStartingBy;
121+
}
122+
123+
public void setLinesStartingBy(StringValue linesStartingBy) {
124+
this.linesStartingBy = linesStartingBy;
125+
}
126+
127+
public StringValue getLinesTerminatedBy() {
128+
return linesTerminatedBy;
129+
}
130+
131+
public void setLinesTerminatedBy(StringValue linesTerminatedBy) {
132+
this.linesTerminatedBy = linesTerminatedBy;
133+
}
134+
135+
public boolean hasFieldsClause() {
136+
return fieldsKeyword != null || fieldsTerminatedBy != null || fieldsEnclosedBy != null
137+
|| fieldsEscapedBy != null;
138+
}
139+
140+
public boolean hasLinesClause() {
141+
return linesStartingBy != null || linesTerminatedBy != null;
142+
}
143+
144+
public StringBuilder appendTo(StringBuilder builder) {
145+
builder.append("INTO ").append(type);
146+
if (fileName != null) {
147+
builder.append(" ").append(fileName);
148+
}
149+
if (characterSet != null) {
150+
builder.append(" CHARACTER SET ").append(characterSet);
151+
}
152+
if (hasFieldsClause()) {
153+
builder.append(" ")
154+
.append(fieldsKeyword != null ? fieldsKeyword : FieldsKeyword.FIELDS);
155+
if (fieldsTerminatedBy != null) {
156+
builder.append(" TERMINATED BY ").append(fieldsTerminatedBy);
157+
}
158+
if (fieldsEnclosedBy != null) {
159+
builder.append(" ");
160+
if (fieldsOptionallyEnclosed) {
161+
builder.append("OPTIONALLY ");
162+
}
163+
builder.append("ENCLOSED BY ").append(fieldsEnclosedBy);
164+
}
165+
if (fieldsEscapedBy != null) {
166+
builder.append(" ESCAPED BY ").append(fieldsEscapedBy);
167+
}
168+
}
169+
if (hasLinesClause()) {
170+
builder.append(" LINES");
171+
if (linesStartingBy != null) {
172+
builder.append(" STARTING BY ").append(linesStartingBy);
173+
}
174+
if (linesTerminatedBy != null) {
175+
builder.append(" TERMINATED BY ").append(linesTerminatedBy);
176+
}
177+
}
178+
return builder;
179+
}
180+
181+
@Override
182+
public String toString() {
183+
return appendTo(new StringBuilder()).toString();
184+
}
185+
}

src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class PlainSelect extends Select {
3434
private BigQuerySelectQualifier bigQuerySelectQualifier = null;
3535
private List<SelectItem<?>> selectItems;
3636
private List<Table> intoTables;
37+
private MySqlSelectIntoClause mySqlSelectIntoClause;
3738
private FromItem fromItem;
3839
private List<LateralView> lateralViews;
3940
private List<Join> joins;
@@ -142,6 +143,14 @@ public void setIntoTables(List<Table> intoTables) {
142143
this.intoTables = intoTables;
143144
}
144145

146+
public MySqlSelectIntoClause getMySqlSelectIntoClause() {
147+
return mySqlSelectIntoClause;
148+
}
149+
150+
public void setMySqlSelectIntoClause(MySqlSelectIntoClause mySqlSelectIntoClause) {
151+
this.mySqlSelectIntoClause = mySqlSelectIntoClause;
152+
}
153+
145154
public List<SelectItem<?>> getSelectItems() {
146155
return selectItems;
147156
}
@@ -564,6 +573,12 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) {
564573
}
565574
}
566575

576+
if (mySqlSelectIntoClause != null
577+
&& mySqlSelectIntoClause
578+
.getPosition() == MySqlSelectIntoClause.Position.BEFORE_FROM) {
579+
builder.append(" ").append(mySqlSelectIntoClause);
580+
}
581+
567582
if (fromItem != null) {
568583
builder.append(" FROM ");
569584
if (isUsingOnly) {
@@ -646,6 +661,11 @@ public String toString() {
646661
StringBuilder builder = new StringBuilder();
647662
super.appendTo(builder);
648663

664+
if (mySqlSelectIntoClause != null
665+
&& mySqlSelectIntoClause.getPosition() == MySqlSelectIntoClause.Position.TRAILING) {
666+
builder.append(" ").append(mySqlSelectIntoClause);
667+
}
668+
649669
if (settings != null && !settings.isEmpty()) {
650670
builder.append(" SETTINGS ");
651671
UpdateSet.appendUpdateSetsTo(builder, settings);
@@ -698,6 +718,11 @@ public PlainSelect withIntoTables(List<Table> intoTables) {
698718
return this;
699719
}
700720

721+
public PlainSelect withMySqlSelectIntoClause(MySqlSelectIntoClause mySqlSelectIntoClause) {
722+
this.setMySqlSelectIntoClause(mySqlSelectIntoClause);
723+
return this;
724+
}
725+
701726
public PlainSelect withWhere(Expression where) {
702727
this.setWhere(where);
703728
return this;

src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,18 @@ public <S> T visit(PlainSelect plainSelect, S context) {
138138
selectItem.accept(selectItemVisitor, context);
139139
}
140140

141+
if (plainSelect.getMySqlSelectIntoClause() != null) {
142+
MySqlSelectIntoClause mySqlSelectIntoClause = plainSelect.getMySqlSelectIntoClause();
143+
expressionVisitor.visitExpression(mySqlSelectIntoClause.getFileName(), context);
144+
expressionVisitor.visitExpression(mySqlSelectIntoClause.getFieldsTerminatedBy(),
145+
context);
146+
expressionVisitor.visitExpression(mySqlSelectIntoClause.getFieldsEnclosedBy(), context);
147+
expressionVisitor.visitExpression(mySqlSelectIntoClause.getFieldsEscapedBy(), context);
148+
expressionVisitor.visitExpression(mySqlSelectIntoClause.getLinesStartingBy(), context);
149+
expressionVisitor.visitExpression(mySqlSelectIntoClause.getLinesTerminatedBy(),
150+
context);
151+
}
152+
141153
fromItemVisitor.visitTables(plainSelect.getIntoTables(), context);
142154
fromItemVisitor.visitFromItem(plainSelect.getFromItem(), context);
143155

src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import net.sf.jsqlparser.statement.select.Join;
5555
import net.sf.jsqlparser.statement.select.LateralSubSelect;
5656
import net.sf.jsqlparser.statement.select.LateralView;
57+
import net.sf.jsqlparser.statement.select.MySqlSelectIntoClause;
5758
import net.sf.jsqlparser.statement.select.Offset;
5859
import net.sf.jsqlparser.statement.select.OptimizeFor;
5960
import net.sf.jsqlparser.statement.select.OrderByElement;
@@ -250,6 +251,12 @@ public <S> StringBuilder visit(PlainSelect plainSelect, S context) {
250251
}
251252
}
252253

254+
if (plainSelect.getMySqlSelectIntoClause() != null
255+
&& plainSelect.getMySqlSelectIntoClause()
256+
.getPosition() == MySqlSelectIntoClause.Position.BEFORE_FROM) {
257+
builder.append(" ").append(plainSelect.getMySqlSelectIntoClause());
258+
}
259+
253260
if (plainSelect.getFromItem() != null) {
254261
builder.append(" FROM ");
255262
if (plainSelect.isUsingOnly()) {
@@ -369,6 +376,11 @@ public <S> StringBuilder visit(PlainSelect plainSelect, S context) {
369376
builder.append(" SKIP LOCKED");
370377
}
371378
}
379+
if (plainSelect.getMySqlSelectIntoClause() != null
380+
&& plainSelect.getMySqlSelectIntoClause()
381+
.getPosition() == MySqlSelectIntoClause.Position.TRAILING) {
382+
builder.append(" ").append(plainSelect.getMySqlSelectIntoClause());
383+
}
372384
if (plainSelect.getSettings() != null && !plainSelect.getSettings().isEmpty()) {
373385
builder.append(" SETTINGS ");
374386
deparseUpdateSets(plainSelect.getSettings(), builder, expressionVisitor);

src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
package net.sf.jsqlparser.util.validation.validator;
1111

1212
import java.util.List;
13-
1413
import net.sf.jsqlparser.expression.Expression;
1514
import net.sf.jsqlparser.expression.MySQLIndexHint;
1615
import net.sf.jsqlparser.expression.SQLServerHints;
@@ -26,6 +25,7 @@
2625
import net.sf.jsqlparser.statement.select.Join;
2726
import net.sf.jsqlparser.statement.select.LateralSubSelect;
2827
import net.sf.jsqlparser.statement.select.MinusOp;
28+
import net.sf.jsqlparser.statement.select.MySqlSelectIntoClause;
2929
import net.sf.jsqlparser.statement.select.Offset;
3030
import net.sf.jsqlparser.statement.select.ParenthesedFromItem;
3131
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
@@ -122,6 +122,8 @@ public <S> Void visit(PlainSelect plainSelect, S context) {
122122
validateOptionalExpression(plainSelect.getPreWhere());
123123
validateOptionalExpression(plainSelect.getWhere());
124124
validateOptionalExpression(plainSelect.getOracleHierarchical());
125+
validateOptional(plainSelect.getMySqlSelectIntoClause(),
126+
this::validateMySqlSelectIntoClause);
125127

126128
if (plainSelect.getGroupBy() != null) {
127129
plainSelect.getGroupBy().accept(getValidator(GroupByValidator.class), context);
@@ -147,6 +149,15 @@ public <S> Void visit(PlainSelect plainSelect, S context) {
147149
return null;
148150
}
149151

152+
private void validateMySqlSelectIntoClause(MySqlSelectIntoClause mySqlSelectIntoClause) {
153+
validateOptionalExpression(mySqlSelectIntoClause.getFileName());
154+
validateOptionalExpression(mySqlSelectIntoClause.getFieldsTerminatedBy());
155+
validateOptionalExpression(mySqlSelectIntoClause.getFieldsEnclosedBy());
156+
validateOptionalExpression(mySqlSelectIntoClause.getFieldsEscapedBy());
157+
validateOptionalExpression(mySqlSelectIntoClause.getLinesStartingBy());
158+
validateOptionalExpression(mySqlSelectIntoClause.getLinesTerminatedBy());
159+
}
160+
150161
@Override
151162
public <S> Void visit(SelectItem<?> selectExpressionItem, S context) {
152163
selectExpressionItem.getExpression().accept(getValidator(ExpressionValidator.class),

0 commit comments

Comments
 (0)