diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
index 3d5825f5d..b59191844 100644
--- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
+++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
@@ -483,6 +483,12 @@ public enum Feature {
* SQL "CREATE MATERIALIZED VIEW" statement is allowed
*/
createViewMaterialized,
+
+ /**
+ * SQL "CREATE VIEW(x comment 'x', y comment 'y') comment 'view'" statement is allowed
+ */
+ createViewWithComment,
+
/**
* SQL "CREATE TABLE" statement is allowed
*
diff --git a/src/main/java/net/sf/jsqlparser/schema/Column.java b/src/main/java/net/sf/jsqlparser/schema/Column.java
index 684e0faf5..3f7721ca3 100644
--- a/src/main/java/net/sf/jsqlparser/schema/Column.java
+++ b/src/main/java/net/sf/jsqlparser/schema/Column.java
@@ -9,13 +9,12 @@
*/
package net.sf.jsqlparser.schema;
+import java.util.List;
import net.sf.jsqlparser.expression.ArrayConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
-import java.util.List;
-
/**
* A column. It can have the table name it belongs to.
*/
@@ -23,6 +22,7 @@ public class Column extends ASTNodeAccessImpl implements Expression, MultiPartNa
private Table table;
private String columnName;
+ private String commentText;
private ArrayConstructor arrayConstructor;
public Column() {}
@@ -56,19 +56,19 @@ public Column setArrayConstructor(ArrayConstructor arrayConstructor) {
*
* The inference is based only on local information, and not on the whole SQL command. For
* example, consider the following query:
- *
+ *
*
* SELECT x FROM Foo
*
- *
+ *
*
Given the {@code Column} called {@code x}, this method would return
* {@code null}, and not the info about the table {@code Foo}. On the other hand, consider:
*
- *
+ *
*
* SELECT t.x FROM Foo t
*
- *
+ *
*
Here, we will get a {@code Table} object for a table called {@code t}. But
* because the inference is local, such object will not know that {@code t} is just an alias for
* {@code Foo}.
@@ -114,6 +114,11 @@ public String getFullyQualifiedName(boolean aliases) {
fqn.append(columnName);
}
+ if (commentText != null) {
+ fqn.append(" COMMENT ");
+ fqn.append(commentText);
+ }
+
if (arrayConstructor != null) {
fqn.append(arrayConstructor);
}
@@ -146,4 +151,17 @@ public Column withColumnName(String columnName) {
this.setColumnName(columnName);
return this;
}
+
+ public Column withCommentText(String commentText) {
+ this.setCommentText(commentText);
+ return this;
+ }
+
+ public void setCommentText(String commentText) {
+ this.commentText = commentText;
+ }
+
+ public String getCommentText() {
+ return commentText;
+ }
}
diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java
index a623beb41..8381e50af 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java
@@ -9,11 +9,9 @@
*/
package net.sf.jsqlparser.statement.create.view;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.Optional;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;
@@ -25,13 +23,14 @@ public class CreateView implements Statement {
private Table view;
private Select select;
private boolean orReplace = false;
- private List columnNames = null;
+ private ExpressionList columnNames = null;
private boolean materialized = false;
private ForceOption force = ForceOption.NONE;
private TemporaryOption temp = TemporaryOption.NONE;
private AutoRefreshOption autoRefresh = AutoRefreshOption.NONE;
private boolean withReadOnly = false;
private boolean ifNotExists = false;
+ private List viewCommentOptions = null;
@Override
public void accept(StatementVisitor statementVisitor) {
@@ -65,11 +64,11 @@ public void setSelect(Select select) {
this.select = select;
}
- public List getColumnNames() {
+ public ExpressionList getColumnNames() {
return columnNames;
}
- public void setColumnNames(List columnNames) {
+ public void setColumnNames(ExpressionList columnNames) {
this.columnNames = columnNames;
}
@@ -145,7 +144,12 @@ public String toString() {
sql.append(" AUTO REFRESH ").append(autoRefresh.name());
}
if (columnNames != null) {
- sql.append(PlainSelect.getStringList(columnNames, true, true));
+ sql.append("(");
+ sql.append(columnNames);
+ sql.append(")");
+ }
+ if (viewCommentOptions != null) {
+ sql.append(PlainSelect.getStringList(viewCommentOptions, false, false));
}
sql.append(" AS ").append(select);
if (isWithReadOnly()) {
@@ -182,7 +186,7 @@ public CreateView withOrReplace(boolean orReplace) {
return this;
}
- public CreateView withColumnNames(List columnNames) {
+ public CreateView withColumnNames(ExpressionList columnNames) {
this.setColumnNames(columnNames);
return this;
}
@@ -202,15 +206,11 @@ public CreateView withWithReadOnly(boolean withReadOnly) {
return this;
}
- public CreateView addColumnNames(String... columnNames) {
- List collection = Optional.ofNullable(getColumnNames()).orElseGet(ArrayList::new);
- Collections.addAll(collection, columnNames);
- return this.withColumnNames(collection);
+ public List getViewCommentOptions() {
+ return viewCommentOptions;
}
- public CreateView addColumnNames(Collection columnNames) {
- List collection = Optional.ofNullable(getColumnNames()).orElseGet(ArrayList::new);
- collection.addAll(columnNames);
- return this.withColumnNames(collection);
+ public void setViewCommentOptions(List viewCommentOptions) {
+ this.viewCommentOptions = viewCommentOptions;
}
}
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java
index 13973b06e..1f5662e68 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java
@@ -67,7 +67,13 @@ public void deParse(CreateView createView) {
buffer.append(" AUTO REFRESH ").append(createView.getAutoRefresh().name());
}
if (createView.getColumnNames() != null) {
- buffer.append(PlainSelect.getStringList(createView.getColumnNames(), true, true));
+ buffer.append("(");
+ buffer.append(createView.getColumnNames());
+ buffer.append(")");
+ }
+ if (createView.getViewCommentOptions() != null) {
+ buffer.append(
+ PlainSelect.getStringList(createView.getViewCommentOptions(), false, false));
}
buffer.append(" AS ");
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java
index 185da5664..983adb4fd 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java
@@ -9,11 +9,10 @@
*/
package net.sf.jsqlparser.util.validation.feature;
-import net.sf.jsqlparser.parser.feature.Feature;
-
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
+import net.sf.jsqlparser.parser.feature.Feature;
/**
* Please add Features supported and place a link to public documentation
@@ -41,7 +40,8 @@ public enum MariaDbVersion implements Version {
Feature.selectForUpdateSkipLocked,
// https://mariadb.com/kb/en/join-syntax/
- Feature.join, Feature.joinSimple, Feature.joinRight, Feature.joinNatural, Feature.joinLeft,
+ Feature.join, Feature.joinSimple, Feature.joinRight, Feature.joinNatural,
+ Feature.joinLeft,
Feature.joinCross, Feature.joinOuter, Feature.joinInner, Feature.joinStraight,
Feature.joinUsingColumns,
@@ -64,8 +64,10 @@ public enum MariaDbVersion implements Version {
// https://mariadb.com/kb/en/insert/
Feature.insert, Feature.insertValues, Feature.values,
- Feature.insertFromSelect, Feature.insertModifierPriority, Feature.insertModifierIgnore,
- Feature.insertUseSet, Feature.insertUseDuplicateKeyUpdate, Feature.insertReturningExpressionList,
+ Feature.insertFromSelect, Feature.insertModifierPriority,
+ Feature.insertModifierIgnore,
+ Feature.insertUseSet, Feature.insertUseDuplicateKeyUpdate,
+ Feature.insertReturningExpressionList,
// https://mariadb.com/kb/en/update/
Feature.update,
@@ -96,7 +98,8 @@ public enum MariaDbVersion implements Version {
Feature.dropView,
// https://mariadb.com/kb/en/drop-sequence/
Feature.dropSequence, Feature.dropTableIfExists, Feature.dropIndexIfExists,
- Feature.dropViewIfExists, Feature.dropSchemaIfExists, Feature.dropSequenceIfExists,
+ Feature.dropViewIfExists, Feature.dropSchemaIfExists,
+ Feature.dropSequenceIfExists,
// https://mariadb.com/kb/en/replace/
Feature.upsert,
@@ -110,9 +113,11 @@ public enum MariaDbVersion implements Version {
// https://mariadb.com/kb/en/create-view/
Feature.createView,
Feature.createOrReplaceView,
+ Feature.createViewWithComment,
// https://mariadb.com/kb/en/create-table/
- Feature.createTable, Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings,
+ Feature.createTable, Feature.createTableCreateOptionStrings,
+ Feature.createTableTableOptionStrings,
Feature.createTableFromSelect, Feature.createTableIfNotExists,
// https://mariadb.com/kb/en/create-index/
Feature.createIndex,
@@ -143,7 +148,7 @@ public enum MariaDbVersion implements Version {
Feature.commit,
// https://mariadb.com/kb/en/optimizer-hints/
Feature.mySqlHintStraightJoin,
- Feature.mysqlCalcFoundRows,
+ Feature.mysqlCalcFoundRows,
Feature.mysqlSqlCacheFlag)),
ORACLE_MODE("oracle_mode", V10_5_4.copy().add(Feature.selectUnique).getFeatures());
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java
index 01bbce4c1..d631344d8 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java
@@ -9,11 +9,10 @@
*/
package net.sf.jsqlparser.util.validation.feature;
-import net.sf.jsqlparser.parser.feature.Feature;
-
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
+import net.sf.jsqlparser.parser.feature.Feature;
/**
* Please add Features supported and place a link to public documentation
@@ -33,7 +32,8 @@ public enum MySqlVersion implements Version {
// https://dev.mysql.com/doc/refman/8.0/en/select.html
Feature.select,
Feature.selectGroupBy, Feature.selectHaving,
- Feature.limit, Feature.limitOffset, Feature.offset, Feature.offsetParam, Feature.orderBy,
+ Feature.limit, Feature.limitOffset, Feature.offset, Feature.offsetParam,
+ Feature.orderBy,
Feature.selectForUpdate,
Feature.selectForUpdateOfTable,
Feature.selectForUpdateNoWait,
@@ -51,7 +51,8 @@ public enum MySqlVersion implements Version {
Feature.function,
// https://dev.mysql.com/doc/refman/8.0/en/join.html
- Feature.join, Feature.joinSimple, Feature.joinLeft, Feature.joinRight, Feature.joinOuter,
+ Feature.join, Feature.joinSimple, Feature.joinLeft, Feature.joinRight,
+ Feature.joinOuter,
Feature.joinNatural, Feature.joinInner, Feature.joinCross, Feature.joinStraight,
Feature.joinUsingColumns,
@@ -99,9 +100,11 @@ public enum MySqlVersion implements Version {
Feature.createSchema,
// https://dev.mysql.com/doc/refman/8.0/en/create-view.html
Feature.createView,
+ Feature.createViewWithComment,
Feature.createOrReplaceView,
// https://dev.mysql.com/doc/refman/8.0/en/create-table.html
- Feature.createTable, Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings,
+ Feature.createTable, Feature.createTableCreateOptionStrings,
+ Feature.createTableTableOptionStrings,
Feature.createTableFromSelect, Feature.createTableIfNotExists,
// https://dev.mysql.com/doc/refman/8.0/en/create-index.html
Feature.createIndex,
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java
index 2723cfc25..e2910ada3 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java
@@ -33,6 +33,8 @@ public void validate(CreateView createView) {
Feature.createViewTemporary);
validateFeature(c, createView.isMaterialized(), Feature.createViewMaterialized);
validateName(c, NamedObject.view, createView.getView().getFullyQualifiedName(), false);
+ validateFeature(c, createView.getViewCommentOptions() != null,
+ Feature.createViewWithComment);
}
SelectValidator v = getValidator(SelectValidator.class);
Select select = createView.getSelect();
diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
index 2045dfdda..91f4df886 100644
--- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
@@ -1767,16 +1767,18 @@ Column Column() #Column :
{
List data = new ArrayList();
ArrayConstructor arrayConstructor = null;
+ Token tk = null;
}
{
data = RelObjectNameList()
-
+ [ tk= ]
// @todo: we better should return a SEQUENCE instead of a COLUMN
[ "." { data.add("nextval"); } ]
[ LOOKAHEAD(2) arrayConstructor = ArrayConstructor(false) ]
{
Column col = new Column(data);
+ if (tk != null) { col.withCommentText(tk.image); }
if (arrayConstructor!=null) {
col.setArrayConstructor(arrayConstructor);
}
@@ -5705,13 +5707,30 @@ Analyze Analyze():
}
}
+ExpressionList ColumnWithCommentList():
+{
+ ExpressionList expressions = new ExpressionList();
+ Column img = null;
+}
+{
+ "("
+ img=Column() { expressions.add(img); }
+ ( "," img=Column() { expressions.add(img); } )*
+ ")"
+ {
+ return expressions;
+ }
+}
+
+
CreateView CreateView(boolean isUsingOrReplace):
{
CreateView createView = new CreateView();
Table view = null;
Select select = null;
- List columnNames = null;
+ ExpressionList columnNames = null;
Token tk = null;
+ List commentTokens = null;
}
{
{ createView.setOrReplace(isUsingOrReplace);}
@@ -5727,13 +5746,36 @@ CreateView CreateView(boolean isUsingOrReplace):
view=Table() { createView.setView(view); }
[LOOKAHEAD(3) (tk= | tk=) { createView.setAutoRefresh(AutoRefreshOption.from(tk.image)); } ]
[LOOKAHEAD(3) {createView.setIfNotExists(true);}]
- [ columnNames = ColumnsNamesList() { createView.setColumnNames(columnNames); } ]
+ [ columnNames=ColumnWithCommentList( ) { createView.setColumnNames(columnNames); } ]
+ [ commentTokens=CreateViewTailComment( ) { createView.setViewCommentOptions(commentTokens); } ]
select=Select( ) { createView.setSelect(select); }
[ { createView.setWithReadOnly(true); } ]
{ return createView; }
}
+List CreateViewTailComment():
+{
+ Token tk = null;
+ Token tk2 = null;
+ String op = null;
+ List result = new ArrayList();
+}
+{
+ tk=
+ [ "=" { op = "="; } ]
+ tk2 = {
+ result.add("");
+ result.add(tk.image);
+ if (op != null) {
+ result.add(op);
+ }
+ result.add(tk2.image);
+ }
+ { return result;}
+}
+
+
ReferentialAction.Action Action():
{
ReferentialAction.Action action = null;
@@ -5778,7 +5820,6 @@ List CreateParameter():
Token tk = null, tk2 = null;
Expression exp = null;
ColDataType colDataType;
-
List param = new ArrayList();
}
{
diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java
index 3499a83be..240a22fc3 100644
--- a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java
@@ -9,8 +9,13 @@
*/
package net.sf.jsqlparser.statement.create;
-import java.io.StringReader;
+import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.io.StringReader;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
@@ -20,14 +25,7 @@
import net.sf.jsqlparser.statement.create.view.CreateView;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
-import static net.sf.jsqlparser.test.TestUtils.*;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
import org.junit.jupiter.api.Test;
public class CreateViewTest {
@@ -212,4 +210,33 @@ public void testCreateMaterializedViewIfNotExists() throws JSQLParserException {
assertTrue(createView.isIfNotExists());
}
+ @Test
+ public void testCreateViewWithColumnComment() throws JSQLParserException {
+ String stmt =
+ "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') AS SELECT c1, C2 FROM t1 WITH READ ONLY";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+
+ String stmt2 =
+ "CREATE VIEW v14(c1 COMMENT 'comment1', c2) AS SELECT c1, C2 FROM t1 WITH READ ONLY";
+ assertSqlCanBeParsedAndDeparsed(stmt2);
+
+ String stmt3 =
+ "CREATE VIEW v14(c1, c2) COMMENT = 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY";
+ assertSqlCanBeParsedAndDeparsed(stmt3);
+ }
+
+ @Test
+ public void testCreateViewWithTableComment1() throws JSQLParserException {
+ String stmt =
+ "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') COMMENT 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+ }
+
+ @Test
+ public void testCreateViewWithTableComment2() throws JSQLParserException {
+ String stmt =
+ "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') COMMENT = 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+ }
+
}
diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java
index 92d4cd294..4c94bd041 100644
--- a/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java
+++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java
@@ -37,18 +37,21 @@ public void testValidateCreateViewNotAllowed() throws JSQLParserException {
@Test
public void testValidateCreateViewMaterialized() throws JSQLParserException {
- validateNoErrors("CREATE MATERIALIZED VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE);
+ validateNoErrors("CREATE MATERIALIZED VIEW myview AS SELECT * FROM mytab", 1,
+ DatabaseType.ORACLE);
}
@Test
public void testValidateCreateOrReplaceView() throws JSQLParserException {
- validateNoErrors("CREATE OR REPLACE VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE,
+ validateNoErrors("CREATE OR REPLACE VIEW myview AS SELECT * FROM mytab", 1,
+ DatabaseType.ORACLE,
DatabaseType.POSTGRESQL, DatabaseType.MYSQL, DatabaseType.MARIADB, DatabaseType.H2);
}
@Test
public void testValidateCreateForceView() throws JSQLParserException {
- validateNoErrors("CREATE FORCE VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE, DatabaseType.H2);
+ validateNoErrors("CREATE FORCE VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE,
+ DatabaseType.H2);
}
@Test
@@ -66,4 +69,12 @@ public void testValidateCreateViewWith() throws JSQLParserException {
validateNoErrors(sql, 1, DatabaseType.DATABASES);
}
}
+
+ @Test
+ public void testValidateCreateViewWithComment() throws JSQLParserException {
+ validateNoErrors(
+ "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') COMMENT = 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY",
+ 1,
+ DatabaseType.MYSQL, DatabaseType.MARIADB);
+ }
}