Skip to content

Commit aea528f

Browse files
committed
feat: expose MySQL table and partition comments as string values
Read the table COMMENT of CREATE TABLE, CREATE TABLE ... AS SELECT and ALTER TABLE, and the partition and subpartition COMMENT, with the stringValue() helper so the literal records the dialect's escaping and getNotExcapedValue() returns the stored text. TableOption gains getComment(), setComment(), withComment() and a comment() factory, following the table option state rules: the literal replaces the option's contents, while setValue() and setTokens() store unparsed text. PartitionDefinition gains getCommentValue() and setCommentValue(); getComment() and setComment(String) remain String accessors. getValue(), the token snapshots and the rendered SQL keep the literal as written. Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent f6ac257 commit aea528f

6 files changed

Lines changed: 486 additions & 7 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/create/table/PartitionDefinition.java‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import java.io.Serializable;
1313
import java.util.List;
14+
import net.sf.jsqlparser.expression.StringValue;
1415
import net.sf.jsqlparser.statement.select.PlainSelect;
1516

1617
public class PartitionDefinition implements Serializable {
@@ -39,7 +40,7 @@ public String toString() {
3940
private boolean subPartition;
4041
private boolean storageKeyword;
4142
private boolean storageEngineUseEquals = true;
42-
private String comment;
43+
private StringValue commentValue;
4344
private boolean commentUseEquals;
4445
private String dataDirectory;
4546
private boolean dataDirectoryUseEquals;
@@ -143,12 +144,26 @@ public void setStorageEngineUseEquals(boolean storageEngineUseEquals) {
143144
this.storageEngineUseEquals = storageEngineUseEquals;
144145
}
145146

147+
/** Returns the COMMENT literal's SQL text, for example {@code 'it''s'}, or null. */
146148
public String getComment() {
147-
return comment;
149+
return commentValue != null ? commentValue.toString() : null;
148150
}
149151

152+
/**
153+
* Sets the COMMENT from a string literal's SQL text, read with standard escaping. Use
154+
* {@link #setCommentValue(StringValue)} for another escaping. Null removes the comment.
155+
*/
150156
public void setComment(String comment) {
151-
this.comment = comment;
157+
commentValue = comment != null ? new StringValue(comment) : null;
158+
}
159+
160+
/** Returns the COMMENT string literal; {@link StringValue#getNotExcapedValue()} decodes it. */
161+
public StringValue getCommentValue() {
162+
return commentValue;
163+
}
164+
165+
public void setCommentValue(StringValue commentValue) {
166+
this.commentValue = commentValue;
152167
}
153168

154169
public boolean isCommentUseEquals() {
@@ -266,7 +281,7 @@ public String toString() {
266281
}
267282
appendOption(b, "ENGINE", storageEngine, storageEngineUseEquals);
268283
}
269-
appendOption(b, "COMMENT", comment, commentUseEquals);
284+
appendOption(b, "COMMENT", getComment(), commentUseEquals);
270285
appendOption(b, "DATA DIRECTORY", dataDirectory, dataDirectoryUseEquals);
271286
appendOption(b, "INDEX DIRECTORY", indexDirectory, indexDirectoryUseEquals);
272287
appendOption(b, "MAX_ROWS", maxRows, maxRowsUseEquals);

‎src/main/java/net/sf/jsqlparser/statement/create/table/TableOption.java‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.Arrays;
1515
import java.util.Collections;
1616
import java.util.List;
17+
import net.sf.jsqlparser.expression.StringValue;
1718
import net.sf.jsqlparser.statement.select.PlainSelect;
1819
import net.sf.jsqlparser.schema.Table;
1920

@@ -34,6 +35,7 @@ public class TableOption implements Serializable {
3435
private List<IndexOption> storageParameters;
3536
private ColumnOption.Storage tablespaceStorage;
3637
private OnCommitAction onCommitAction;
38+
private StringValue comment;
3739

3840
public enum Kind {
3941
ENGINE, CHARACTER_SET, COLLATE, COMMENT, AUTO_INCREMENT, STATS_AUTO_RECALC, STATS_PERSISTENT, STATS_SAMPLE_PAGES, UNION, ENCRYPTION, PASSWORD, DATA_DIRECTORY, INDEX_DIRECTORY, SECONDARY_ENGINE, AUTOEXTEND_SIZE, INSERT_METHOD, PACK_KEYS, DELAY_KEY_WRITE, CHECKSUM, CONNECTION, COMPRESSION, STORAGE_PARAMETERS, WITHOUT_OIDS, ENGINE_ATTRIBUTE, SECONDARY_ENGINE_ATTRIBUTE, ROW_FORMAT, AVG_ROW_LENGTH, MAX_ROWS, MIN_ROWS, KEY_BLOCK_SIZE, TABLESPACE, FOREIGN_SERVER, ACCESS_METHOD, ON_COMMIT, OTHER
@@ -129,13 +131,49 @@ public void setStorageParameters(List<IndexOption> storageParameters) {
129131
name = "WITH";
130132
}
131133

134+
/**
135+
* Returns the COMMENT string literal, or null for another kind, opaque tokens or a plain value
136+
* set with {@link #setValue(String)}. {@link StringValue#getNotExcapedValue()} decodes it.
137+
*/
138+
public StringValue getComment() {
139+
return comment;
140+
}
141+
142+
/**
143+
* Replaces the current contents with a COMMENT string literal and keeps the equals spelling; an
144+
* existing COMMENT also keeps its name. A null literal omits the value.
145+
*/
146+
public void setComment(StringValue comment) {
147+
String commentName = kind == Kind.COMMENT && name != null ? name : "COMMENT";
148+
boolean equals = useEquals;
149+
clearContents();
150+
kind = Kind.COMMENT;
151+
name = commentName;
152+
useEquals = equals;
153+
this.comment = comment;
154+
}
155+
156+
public TableOption withComment(StringValue comment) {
157+
setComment(comment);
158+
return this;
159+
}
160+
161+
public static TableOption comment(StringValue comment) {
162+
TableOption option = new TableOption();
163+
option.setComment(comment);
164+
return option;
165+
}
166+
132167
public void appendTo(StringBuilder builder,
133168
java.util.function.Consumer<net.sf.jsqlparser.expression.Expression> expressionPrinter) {
134169
if (foreignTableOptions != null) {
135170
foreignTableOptions.appendTo(builder, expressionPrinter);
136171
} else if (storageParameters != null) {
137172
builder.append(name).append(useEquals ? " = " : " ");
138173
IndexOption.appendListTo(builder, storageParameters, expressionPrinter);
174+
} else if (comment != null) {
175+
builder.append(name).append(useEquals ? " = " : " ");
176+
expressionPrinter.accept(comment);
139177
} else {
140178
builder.append(toString());
141179
}
@@ -183,6 +221,11 @@ public void setKind(Kind kind) {
183221
if (kind != Kind.ON_COMMIT) {
184222
onCommitAction = null;
185223
}
224+
if (kind != Kind.COMMENT && comment != null) {
225+
// keep the literal's SQL text, as for other scalar values
226+
value = comment.toString();
227+
comment = null;
228+
}
186229
}
187230

188231
public String getName() {
@@ -204,16 +247,21 @@ public String getValue() {
204247
if (onCommitAction != null) {
205248
return onCommitAction.toString();
206249
}
250+
if (comment != null) {
251+
return comment.toString();
252+
}
207253
return unionTables == null ? value : PlainSelect.getStringList(unionTables, true, true);
208254
}
209255

256+
/** Sets a plain value; it replaces a COMMENT literal without being parsed. */
210257
public void setValue(String value) {
211258
tokens = null;
212259
this.value = value;
213260
foreignTableOptions = null;
214261
unionTables = null;
215262
storageParameters = null;
216263
onCommitAction = null;
264+
comment = null;
217265
}
218266

219267
/** Returns the mutable MERGE table sources, including an empty UNION list. */
@@ -296,6 +344,7 @@ private void clearContents() {
296344
unionTables = null;
297345
storageParameters = null;
298346
onCommitAction = null;
347+
comment = null;
299348
}
300349

301350
public TableOption withKind(Kind kind) {

‎src/main/java/net/sf/jsqlparser/util/TableDefinitionTraversal.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,12 @@ public static void visit(CreateTable table, Consumer<Expression> expressions,
168168
visit(table.getPartitionBound(), expressions);
169169
}
170170

171-
/** Shared CREATE/ALTER table option traversal. */
171+
/** Shared CREATE/ALTER table option traversal, including COMMENT literals. */
172172
public static void visit(net.sf.jsqlparser.statement.create.table.TableOption option,
173173
Consumer<Expression> expressions, Consumer<Table> tables) {
174174
if (option != null) {
175175
visitOptions(option.getStorageParameters(), expressions);
176+
accept(option.getComment(), expressions);
176177
if (option.getUnionTables() != null) {
177178
option.getUnionTables().forEach(source -> accept(source, tables));
178179
}

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16947,6 +16947,7 @@ TableOption MySqlTableOption(): {
1694716947
TableOption.Kind kind = null;
1694816948
TableOption option;
1694916949
ColumnOption.Storage storage = null;
16950+
StringValue comment = null;
1695016951
} {
1695116952
(
1695216953
tk=<K_UNION> { name = tk.image; kind = TableOption.Kind.UNION;
@@ -17017,10 +17018,16 @@ TableOption MySqlTableOption(): {
1701717018
kind = TableOption.Kind.COMMENT;
1701817019
}
1701917020
[ "=" { useEquals = true; } ]
17020-
value=MySqlTableOptionValue()
17021+
// A string literal keeps its escaping; other values remain plain text
17022+
(
17023+
LOOKAHEAD({ getToken(1).kind == S_CHAR_LITERAL }) tk=<S_CHAR_LITERAL>
17024+
{ comment = stringValue(tk); }
17025+
| value=MySqlTableOptionValue()
17026+
)
1702117027
)
1702217028
{
1702317029
option = new TableOption(kind, name, value, useEquals);
17030+
if (comment != null) { option.setComment(comment); }
1702417031
if (unionTables != null) { option.setUnionTables(unionTables); }
1702517032
option.setTablespaceStorage(storage);
1702617033
return option;
@@ -19048,7 +19055,7 @@ void PartitionDefinitionOption(PartitionDefinition partitionDefinition):
1904819055
|
1904919056
<K_COMMENT> [ "=" { useEquals = true; } ] tk=<S_CHAR_LITERAL>
1905019057
{
19051-
partitionDefinition.setComment(tk.image);
19058+
partitionDefinition.setCommentValue(stringValue(tk));
1905219059
partitionDefinition.setCommentUseEquals(useEquals);
1905319060
}
1905419061
|

‎src/site/sphinx/usage.rst‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,47 @@ Use ``TableOption.onCommit`` and ``TableOption.accessMethod`` to build them.
468468
.setOnCommitAction(TableOption.OnCommitAction.PRESERVE_ROWS);
469469
// CREATE TEMP TABLE t (id INT) USING heap ON COMMIT PRESERVE ROWS
470470
471+
MySQL table and partition comments
472+
----------------------------------
473+
474+
A table ``COMMENT`` in CREATE TABLE, CREATE TABLE … AS SELECT and ALTER TABLE is a
475+
``StringValue`` in ``TableOption.getComment()``. Partition and subpartition comments,
476+
including those of ALTER TABLE ADD and REORGANIZE PARTITION, are in
477+
``PartitionDefinition.getCommentValue()``. The literal records the escaping of the
478+
parser configuration, so ``getNotExcapedValue()`` returns the text MySQL stores.
479+
480+
.. code-block:: java
481+
482+
CreateTable table = (CreateTable) CCJSqlParserUtil.parse(
483+
"CREATE TABLE t (id INT) COMMENT = 'it\\'s' "
484+
+ "PARTITION BY HASH (id) (PARTITION p0 COMMENT 'a\\\\b')",
485+
parser -> parser.withDialect(Dialect.MYSQL));
486+
TableOption option = table.getTableOption(TableOption.Kind.COMMENT).orElseThrow();
487+
option.getComment().getNotExcapedValue(); // it's
488+
option.getValue(); // 'it\'s'
489+
table.getPartitioning().getPartitionDefinitions().get(0)
490+
.getCommentValue().getNotExcapedValue(); // a\b
491+
492+
``getValue()``, ``getTokens()``, ``CreateTable.getTableOptionsStrings()``,
493+
``AlterExpressionTableOption.getCommentText()`` and ``PartitionDefinition.getComment()``
494+
still return the literal as written, and the SQL output is unchanged except that a
495+
string prefix such as ``n'…'`` is printed in upper case, as in other string literals.
496+
The table comment literal is rendered by the expression deparser and visited like a
497+
column COMMENT literal.
498+
499+
``setComment``, ``withComment`` and the ``TableOption.comment`` factory replace the
500+
option's contents with the literal and select COMMENT. They keep the equals spelling,
501+
and an existing COMMENT also keeps its name; a null literal omits the value.
502+
``setValue`` stores a plain value and ``setTokens`` opaque tokens. Neither is parsed,
503+
so ``getComment()`` then returns null. Selecting another kind keeps the literal's SQL
504+
text as a plain value. A COMMENT value that is not a string literal also stays a plain
505+
value, for example a double-quoted string parsed without the MySQL dialect, where it
506+
is a quoted identifier.
507+
508+
``PartitionDefinition.setComment(String)`` reads the SQL text of a literal with
509+
standard escaping; pass a ``StringValue`` with another escaping to
510+
``setCommentValue``.
511+
471512
Inspect PostgreSQL schema statements
472513
------------------------------------
473514

0 commit comments

Comments
 (0)