Skip to content

Commit a5729d2

Browse files
committed
Merge latest upstream master into PR #2906
Preserve MySQL replication alongside newly merged load statements and DDL support, retaining both sides of the feature lists and AST coverage. Validation: mvn --batch-mode --no-transfer-progress clean test 16,338 tests; zero failures/errors; 25 existing skips. JavaCC reports no choice conflicts.
2 parents ade260a + f925239 commit a5729d2

48 files changed

Lines changed: 4832 additions & 207 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,12 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
152152
| **PostgreSQL maintenance** | `VACUUM` · `ANALYZE` · `CLUSTER` · `CHECKPOINT` |
153153
| **MySQL locks** | `LOCK TABLES` · `UNLOCK TABLES` · `LOCK INSTANCE FOR BACKUP` · `UNLOCK INSTANCE` |
154154
| **MySQL resource groups** | `CREATE`/`ALTER`/`DROP RESOURCE GROUP` · `SET RESOURCE GROUP` |
155+
| **MySQL loadable functions** | `CREATE [AGGREGATE] FUNCTION … RETURNS … SONAME` |
155156
| **MySQL servers, plugins and components** | `CREATE SERVER` · `ALTER SERVER` with `OPTIONS (… PORT 3306)` · `INSTALL`/`UNINSTALL PLUGIN` · `INSTALL`/`UNINSTALL COMPONENT` |
156157
| **PostgreSQL session statements** | `DISCARD` · `LISTEN` · `UNLISTEN` · `SHOW`/`RESET TIME ZONE`/`TRANSACTION ISOLATION LEVEL`/`SESSION AUTHORIZATION` · `COPY` options from before 9.0 |
157158
| **MySQL server administration** | `FLUSH` · `KILL` · `RESET PERSIST` · `HANDLER … OPEN`/`READ`/`CLOSE` |
158159
| **MySQL replication** | `CHANGE REPLICATION SOURCE TO` · `CHANGE REPLICATION FILTER` · `START`/`STOP REPLICA` · `RESET REPLICA` · `RESET BINARY LOGS AND GTIDS` · `PURGE BINARY LOGS` |
160+
| **MySQL data loading** | `LOAD DATA` · `LOAD XML` · `SELECT … INTO OUTFILE` with the same `FIELDS` and `LINES` clauses |
159161
| **PostgreSQL cursors** | `DECLARE … CURSOR` · `FETCH` · `MOVE` · `CLOSE` |
160162
| **MySQL account management** | `CREATE`/`ALTER`/`RENAME USER` · `CREATE ROLE` · `SET ROLE` · `SET DEFAULT ROLE` · `SET PASSWORD` |
161163
| **Tablespaces** | MySQL and PostgreSQL `CREATE TABLESPACE` · `ALTER TABLESPACE` |
@@ -166,6 +168,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
166168
| **MySQL utility statements** | `EXPLAIN`/`DESCRIBE [ANALYZE] FORMAT = …` · `EXPLAIN FOR CONNECTION` · `DESCRIBE t col` · `PREPARE … FROM` · `EXECUTE … USING` · `DROP PREPARE` · `DO` |
167169
| **PostgreSQL administration** | `CREATE`/`ALTER EVENT TRIGGER` · `REINDEX` · `SECURITY LABEL` |
168170
| **PostgreSQL types and rules** | `CREATE TYPE` (shell, composite, enum, range and base types) · `ALTER TYPE` · `CREATE RULE` · `ALTER RULE` |
171+
| **PostgreSQL casts, transforms and conversions** | `CREATE CAST` · `DROP CAST` · `CREATE [OR REPLACE] TRANSFORM` · `DROP TRANSFORM` · `CREATE [DEFAULT] CONVERSION` · `ALTER CONVERSION` |
169172
| **PostgreSQL operators and aggregates** | `CREATE`/`ALTER OPERATOR` · `CREATE`/`ALTER OPERATOR CLASS` · `CREATE`/`ALTER OPERATOR FAMILY` · `CREATE`/`ALTER AGGREGATE` |
170173
| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` |
171174
| **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` |

‎src/main/java/net/sf/jsqlparser/parser/feature/Feature.java‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,30 @@ public enum Feature {
10721072
* @see net.sf.jsqlparser.statement.alter.AlterRule
10731073
*/
10741074
alterRule,
1075+
/**
1076+
* PostgreSQL "CREATE CAST" statement is allowed
1077+
*
1078+
* @see net.sf.jsqlparser.statement.create.cast.CreateCast
1079+
*/
1080+
createCast,
1081+
/**
1082+
* PostgreSQL "CREATE TRANSFORM" statement is allowed
1083+
*
1084+
* @see net.sf.jsqlparser.statement.create.transform.CreateTransform
1085+
*/
1086+
createTransform,
1087+
/**
1088+
* PostgreSQL "CREATE CONVERSION" statement is allowed
1089+
*
1090+
* @see net.sf.jsqlparser.statement.create.conversion.CreateConversion
1091+
*/
1092+
createConversion,
1093+
/**
1094+
* PostgreSQL "ALTER CONVERSION" statement is allowed
1095+
*
1096+
* @see net.sf.jsqlparser.statement.alter.AlterConversion
1097+
*/
1098+
alterConversion,
10751099
/**
10761100
* PostgreSQL "CREATE OPERATOR" statement is allowed
10771101
*
@@ -1378,10 +1402,12 @@ public enum Feature {
13781402
export,
13791403

13801404
/**
1381-
* PostgreSQL and DuckDB "COPY" and BigQuery "LOAD DATA" statements are allowed
1405+
* PostgreSQL and DuckDB "COPY", BigQuery and MySQL "LOAD DATA" and MySQL "LOAD XML" statements
1406+
* are allowed
13821407
*
13831408
* @see net.sf.jsqlparser.statement.CopyStatement
13841409
* @see net.sf.jsqlparser.statement.load.LoadDataStatement
1410+
* @see net.sf.jsqlparser.statement.load.MySqlLoadDataStatement
13851411
*/
13861412
copy, loadData,
13871413

‎src/main/java/net/sf/jsqlparser/statement/CreateFunctionalStatement.java‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ public String getKind() {
142142
return kind;
143143
}
144144

145+
/**
146+
* @return the keywords that name the kind in the statement, which may carry a modifier of
147+
* {@link #getKind()}
148+
*/
149+
protected String getKindKeywords() {
150+
return kind;
151+
}
152+
145153
public void setOrReplace(boolean orReplace) {
146154
this.orReplace = orReplace;
147155
}
@@ -156,7 +164,7 @@ public String formatDeclaration() {
156164

157165
private StringBuilder appendDeclarationTo(StringBuilder builder,
158166
Consumer<TableElement> printer) {
159-
appendTokens(builder, functionDeclarationParts);
167+
appendTokens(builder, getFunctionDeclarationParts());
160168
if (returnType != null) {
161169
builder.append(' ');
162170
returnType.appendTo(builder, printer);
@@ -188,7 +196,7 @@ public StringBuilder appendTo(StringBuilder builder, Consumer<TableElement> prin
188196
if (definer != null) {
189197
builder.append("DEFINER = ").append(definer).append(' ');
190198
}
191-
builder.append(kind).append(' ');
199+
builder.append(getKindKeywords()).append(' ');
192200
return appendDeclarationTo(builder, printer);
193201
}
194202

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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;
11+
12+
import java.io.Serializable;
13+
import net.sf.jsqlparser.expression.StringValue;
14+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
15+
16+
/**
17+
* The {@code {FIELDS | COLUMNS}} and {@code LINES} clauses that describe the text file of a MySQL
18+
* {@code LOAD DATA} or {@code SELECT ... INTO OUTFILE}:
19+
*
20+
* <pre>
21+
* [{FIELDS | COLUMNS} [TERMINATED BY 'string'] [[OPTIONALLY] ENCLOSED BY 'char'] [ESCAPED BY 'char']]
22+
* [LINES [STARTING BY 'string'] [TERMINATED BY 'string']]
23+
* </pre>
24+
*
25+
* Each clause has at least one subclause. MySQL accepts the subclauses of a clause in any order and
26+
* more than once; like the server, the model keeps the last value of each and prints them in the
27+
* order above. {@code OPTIONALLY} stays set once a subclause has written it.
28+
*
29+
* @see <a href="https://dev.mysql.com/doc/refman/8.4/en/load-data.html">LOAD DATA</a>
30+
*/
31+
public class MySqlFileFormat extends ASTNodeAccessImpl implements Serializable {
32+
33+
/** The spelling of the fields clause. */
34+
public enum FieldsKeyword {
35+
FIELDS, COLUMNS
36+
}
37+
38+
private FieldsKeyword fieldsKeyword;
39+
private StringValue fieldsTerminatedBy;
40+
private boolean fieldsOptionallyEnclosed;
41+
private StringValue fieldsEnclosedBy;
42+
private StringValue fieldsEscapedBy;
43+
private StringValue linesStartingBy;
44+
private StringValue linesTerminatedBy;
45+
46+
/** The spelling of the fields clause; null prints FIELDS when a subclause is set. */
47+
public FieldsKeyword getFieldsKeyword() {
48+
return fieldsKeyword;
49+
}
50+
51+
public void setFieldsKeyword(FieldsKeyword fieldsKeyword) {
52+
this.fieldsKeyword = fieldsKeyword;
53+
}
54+
55+
public MySqlFileFormat withFieldsKeyword(FieldsKeyword fieldsKeyword) {
56+
setFieldsKeyword(fieldsKeyword);
57+
return this;
58+
}
59+
60+
public StringValue getFieldsTerminatedBy() {
61+
return fieldsTerminatedBy;
62+
}
63+
64+
public void setFieldsTerminatedBy(StringValue fieldsTerminatedBy) {
65+
this.fieldsTerminatedBy = fieldsTerminatedBy;
66+
}
67+
68+
public MySqlFileFormat withFieldsTerminatedBy(StringValue fieldsTerminatedBy) {
69+
setFieldsTerminatedBy(fieldsTerminatedBy);
70+
return this;
71+
}
72+
73+
public boolean isFieldsOptionallyEnclosed() {
74+
return fieldsOptionallyEnclosed;
75+
}
76+
77+
public void setFieldsOptionallyEnclosed(boolean fieldsOptionallyEnclosed) {
78+
this.fieldsOptionallyEnclosed = fieldsOptionallyEnclosed;
79+
}
80+
81+
public MySqlFileFormat withFieldsOptionallyEnclosed(boolean fieldsOptionallyEnclosed) {
82+
setFieldsOptionallyEnclosed(fieldsOptionallyEnclosed);
83+
return this;
84+
}
85+
86+
public StringValue getFieldsEnclosedBy() {
87+
return fieldsEnclosedBy;
88+
}
89+
90+
public void setFieldsEnclosedBy(StringValue fieldsEnclosedBy) {
91+
this.fieldsEnclosedBy = fieldsEnclosedBy;
92+
}
93+
94+
public MySqlFileFormat withFieldsEnclosedBy(StringValue fieldsEnclosedBy) {
95+
setFieldsEnclosedBy(fieldsEnclosedBy);
96+
return this;
97+
}
98+
99+
public StringValue getFieldsEscapedBy() {
100+
return fieldsEscapedBy;
101+
}
102+
103+
public void setFieldsEscapedBy(StringValue fieldsEscapedBy) {
104+
this.fieldsEscapedBy = fieldsEscapedBy;
105+
}
106+
107+
public MySqlFileFormat withFieldsEscapedBy(StringValue fieldsEscapedBy) {
108+
setFieldsEscapedBy(fieldsEscapedBy);
109+
return this;
110+
}
111+
112+
public StringValue getLinesStartingBy() {
113+
return linesStartingBy;
114+
}
115+
116+
public void setLinesStartingBy(StringValue linesStartingBy) {
117+
this.linesStartingBy = linesStartingBy;
118+
}
119+
120+
public MySqlFileFormat withLinesStartingBy(StringValue linesStartingBy) {
121+
setLinesStartingBy(linesStartingBy);
122+
return this;
123+
}
124+
125+
public StringValue getLinesTerminatedBy() {
126+
return linesTerminatedBy;
127+
}
128+
129+
public void setLinesTerminatedBy(StringValue linesTerminatedBy) {
130+
this.linesTerminatedBy = linesTerminatedBy;
131+
}
132+
133+
public MySqlFileFormat withLinesTerminatedBy(StringValue linesTerminatedBy) {
134+
setLinesTerminatedBy(linesTerminatedBy);
135+
return this;
136+
}
137+
138+
/** Whether the format prints a fields clause: a spelling or a subclause is set. */
139+
public boolean hasFieldsClause() {
140+
return fieldsKeyword != null || fieldsTerminatedBy != null || fieldsEnclosedBy != null
141+
|| fieldsEscapedBy != null;
142+
}
143+
144+
/** Whether the format prints a lines clause: a subclause is set. */
145+
public boolean hasLinesClause() {
146+
return linesStartingBy != null || linesTerminatedBy != null;
147+
}
148+
149+
/** Whether the format prints nothing. */
150+
public boolean isEmpty() {
151+
return !hasFieldsClause() && !hasLinesClause();
152+
}
153+
154+
/** Appends the fields clause and then the lines clause, separated by a space. */
155+
public StringBuilder appendTo(StringBuilder builder) {
156+
if (hasFieldsClause()) {
157+
appendFieldsClause(builder);
158+
if (hasLinesClause()) {
159+
builder.append(' ');
160+
}
161+
}
162+
if (hasLinesClause()) {
163+
appendLinesClause(builder);
164+
}
165+
return builder;
166+
}
167+
168+
private void appendFieldsClause(StringBuilder builder) {
169+
builder.append(fieldsKeyword != null ? fieldsKeyword : FieldsKeyword.FIELDS);
170+
if (fieldsTerminatedBy != null) {
171+
builder.append(" TERMINATED BY ").append(fieldsTerminatedBy);
172+
}
173+
if (fieldsEnclosedBy != null) {
174+
builder.append(fieldsOptionallyEnclosed ? " OPTIONALLY ENCLOSED BY " : " ENCLOSED BY ")
175+
.append(fieldsEnclosedBy);
176+
}
177+
if (fieldsEscapedBy != null) {
178+
builder.append(" ESCAPED BY ").append(fieldsEscapedBy);
179+
}
180+
}
181+
182+
private void appendLinesClause(StringBuilder builder) {
183+
builder.append("LINES");
184+
if (linesStartingBy != null) {
185+
builder.append(" STARTING BY ").append(linesStartingBy);
186+
}
187+
if (linesTerminatedBy != null) {
188+
builder.append(" TERMINATED BY ").append(linesTerminatedBy);
189+
}
190+
}
191+
192+
@Override
193+
public String toString() {
194+
return appendTo(new StringBuilder()).toString();
195+
}
196+
}

‎src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
import net.sf.jsqlparser.statement.alter.AlterOperatorFamily;
4646
import net.sf.jsqlparser.statement.alter.AlterAggregate;
4747
import net.sf.jsqlparser.statement.alter.AlterRule;
48+
import net.sf.jsqlparser.statement.create.cast.CreateCast;
49+
import net.sf.jsqlparser.statement.create.transform.CreateTransform;
50+
import net.sf.jsqlparser.statement.create.conversion.CreateConversion;
51+
import net.sf.jsqlparser.statement.alter.AlterConversion;
4852
import net.sf.jsqlparser.statement.alter.AlterServer;
4953
import net.sf.jsqlparser.statement.alter.AlterSession;
5054
import net.sf.jsqlparser.statement.alter.AlterStatistics;
@@ -112,6 +116,7 @@
112116
import net.sf.jsqlparser.statement.insert.Insert;
113117
import net.sf.jsqlparser.statement.insert.InsertBulk;
114118
import net.sf.jsqlparser.statement.load.LoadDataStatement;
119+
import net.sf.jsqlparser.statement.load.MySqlLoadDataStatement;
115120
import net.sf.jsqlparser.statement.admin.FlushStatement;
116121
import net.sf.jsqlparser.statement.admin.KillStatement;
117122
import net.sf.jsqlparser.statement.handler.HandlerStatement;
@@ -715,6 +720,13 @@ public <S> Void visit(LoadDataStatement loadDataStatement, S context) {
715720
return null;
716721
}
717722

723+
@Override
724+
public <S> Void visit(MySqlLoadDataStatement loadData, S context) {
725+
analysis.claimTopLevel();
726+
analysis.certain(StmtFeature.MODIFIES_DATA);
727+
return super.visit(loadData, context);
728+
}
729+
718730
@Override
719731
public <S> Void visit(PurgeStatement purgeStatement, S context) {
720732
if (purgeStatement.getLogBound() != null) {
@@ -1658,21 +1670,41 @@ public <S> Void visit(AlterRule statement, S context) {
16581670
return schemaOnly();
16591671
}
16601672

1673+
@Override
1674+
public <S> Void visit(CreateCast statement, S context) {
1675+
return schemaOnly();
1676+
}
1677+
16611678
@Override
16621679
public <S> Void visit(CreateOperator statement, S context) {
16631680
return schemaOnly();
16641681
}
16651682

1683+
@Override
1684+
public <S> Void visit(CreateTransform statement, S context) {
1685+
return schemaOnly();
1686+
}
1687+
16661688
@Override
16671689
public <S> Void visit(CreateOperatorClass statement, S context) {
16681690
return schemaOnly();
16691691
}
16701692

1693+
@Override
1694+
public <S> Void visit(CreateConversion statement, S context) {
1695+
return schemaOnly();
1696+
}
1697+
16711698
@Override
16721699
public <S> Void visit(CreateOperatorFamily statement, S context) {
16731700
return schemaOnly();
16741701
}
16751702

1703+
@Override
1704+
public <S> Void visit(AlterConversion statement, S context) {
1705+
return schemaOnly();
1706+
}
1707+
16761708
@Override
16771709
public <S> Void visit(CreateAggregate statement, S context) {
16781710
return schemaOnly();

0 commit comments

Comments
 (0)