diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index 0174240e0..dadf6181c 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -182,6 +182,11 @@ public static List getReservedKeywords(int restriction) { , { "NEXTVAL", RESTRICTED_JSQLPARSER } + , { "AUTO", RESTRICTED_JSQLPARSER } + , { "REFRESH", RESTRICTED_JSQLPARSER } + , { "YES", RESTRICTED_JSQLPARSER } + , { "NO", RESTRICTED_JSQLPARSER } + //@todo: Object Names should not start with Hex-Prefix, we shall not find that Token , { "0x", RESTRICTED_JSQLPARSER } }; diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/AutoRefreshOption.java b/src/main/java/net/sf/jsqlparser/statement/create/view/AutoRefreshOption.java new file mode 100644 index 000000000..685e0a067 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/AutoRefreshOption.java @@ -0,0 +1,18 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.view; + +public enum AutoRefreshOption { + NONE, + + YES, + + NO +} 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 9926cd008..b31a6f1a9 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 @@ -29,6 +29,7 @@ public class CreateView implements Statement { 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; @@ -96,6 +97,14 @@ public void setTemporary(TemporaryOption temp) { this.temp = temp; } + public AutoRefreshOption getAutoRefresh() { + return autoRefresh; + } + + public void setAutoRefresh(AutoRefreshOption autoRefresh) { + this.autoRefresh = autoRefresh; + } + public boolean isWithReadOnly() { return withReadOnly; } @@ -118,16 +127,7 @@ public String toString() { if (isOrReplace()) { sql.append("OR REPLACE "); } - switch (force) { - case FORCE: - sql.append("FORCE "); - break; - case NO_FORCE: - sql.append("NO FORCE "); - break; - default: - // nothing - } + appendForceOptionIfApplicable(sql); if (temp != TemporaryOption.NONE) { sql.append(temp.name()).append(" "); @@ -141,6 +141,9 @@ public String toString() { if (ifNotExists) { sql.append(" IF NOT EXISTS"); } + if (autoRefresh != AutoRefreshOption.NONE) { + sql.append(" AUTO REFRESH ").append(autoRefresh.name()); + } if (columnNames != null) { sql.append(PlainSelect.getStringList(columnNames, true, true)); } @@ -151,6 +154,19 @@ public String toString() { return sql.toString(); } + private void appendForceOptionIfApplicable(StringBuilder sql) { + switch (force) { + case FORCE: + sql.append("FORCE "); + break; + case NO_FORCE: + sql.append("NO FORCE "); + break; + default: + // nothing + } + } + public CreateView withView(Table view) { this.setView(view); return this; 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 c731ba9f8..053f29658 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java @@ -11,6 +11,7 @@ import net.sf.jsqlparser.statement.create.view.CreateView; import net.sf.jsqlparser.statement.create.view.TemporaryOption; +import net.sf.jsqlparser.statement.create.view.AutoRefreshOption; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectVisitor; @@ -63,6 +64,9 @@ public void deParse(CreateView createView) { if (createView.isIfNotExists()) { buffer.append(" IF NOT EXISTS"); } + if (createView.getAutoRefresh() != AutoRefreshOption.NONE) { + buffer.append(" AUTO REFRESH ").append(createView.getAutoRefresh().name()); + } if (createView.getColumnNames() != null) { buffer.append(PlainSelect.getStringList(createView.getColumnNames(), true, true)); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 587b15718..89f6efca9 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -149,6 +149,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -346,6 +347,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -444,6 +446,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | } @@ -5472,6 +5475,7 @@ CreateView CreateView(): Table view = null; Select select = null; List columnNames = null; + Token tk = null; } { @@ -5486,7 +5490,8 @@ CreateView CreateView(): ] [ { createView.setMaterialized(true);} ] view=Table() { createView.setView(view); } - [ LOOKAHEAD(3) {createView.setIfNotExists(true);} ] + [LOOKAHEAD(3) (tk= | tk=) { createView.setAutoRefresh(AutoRefreshOption.valueOf(tk.image)); } ] + [LOOKAHEAD(3) {createView.setIfNotExists(true);}] [ columnNames = ColumnsNamesList() { createView.setColumnNames(columnNames); } ] select=SelectWithWithItems( ) { createView.setSelect(select); } 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 f6552ff4e..0b9e44402 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java @@ -10,14 +10,21 @@ package net.sf.jsqlparser.statement.create; import java.io.StringReader; + import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserManager; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.parser.ParseException; import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.create.view.AutoRefreshOption; import net.sf.jsqlparser.statement.create.view.CreateView; 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; @@ -124,6 +131,60 @@ public void testCreateWithReadOnlyViewIssue838() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("CREATE VIEW v14(c1, c2) AS SELECT c1, C2 FROM t1 WITH READ ONLY"); } + @Test + public void testCreateViewAutoRefreshNone() throws JSQLParserException { + String stmt = "CREATE VIEW myview AS SELECT * FROM mytab"; + CreateView createView = (CreateView) assertSqlCanBeParsedAndDeparsed(stmt); + assertEquals(createView.getAutoRefresh(), AutoRefreshOption.NONE); + } + + @Test + public void testCreateViewAutoRefreshYes() throws JSQLParserException { + String stmt = "CREATE VIEW myview AUTO REFRESH YES AS SELECT * FROM mytab"; + CreateView createView = (CreateView) assertSqlCanBeParsedAndDeparsed(stmt); + assertEquals(createView.getAutoRefresh(), AutoRefreshOption.YES); + } + + @Test + public void testCreateViewAutoRefreshNo() throws JSQLParserException { + String stmt = "CREATE VIEW myview AUTO REFRESH NO AS SELECT * FROM mytab"; + CreateView createView = (CreateView) assertSqlCanBeParsedAndDeparsed(stmt); + assertEquals(createView.getAutoRefresh(), AutoRefreshOption.NO); + } + + @Test + public void testCreateViewAutoFails() throws JSQLParserException { + String stmt = "CREATE VIEW myview AUTO AS SELECT * FROM mytab"; + + ThrowingCallable throwingCallable = () -> CCJSqlParserUtil.parse(stmt); + + assertThatThrownBy(throwingCallable).isInstanceOf(JSQLParserException.class) + .hasRootCauseInstanceOf(ParseException.class).rootCause() + .hasMessageStartingWith("Encountered unexpected token"); + } + + @Test + public void testCreateViewRefreshFails() throws JSQLParserException { + String stmt = "CREATE VIEW myview REFRESH AS SELECT * FROM mytab"; + + ThrowingCallable throwingCallable = () -> CCJSqlParserUtil.parse(stmt); + + assertThatThrownBy(throwingCallable).isInstanceOf(JSQLParserException.class) + .hasRootCauseInstanceOf(ParseException.class).rootCause() + .hasMessageStartingWith("Encountered unexpected token"); + } + + @Test + public void testCreateViewAutoRefreshFails() throws JSQLParserException { + String stmt = "CREATE VIEW myview AUTO REFRESH AS SELECT * FROM mytab"; + + ThrowingCallable throwingCallable = () -> CCJSqlParserUtil.parse(stmt); + + assertThatThrownBy(throwingCallable).isInstanceOf(JSQLParserException.class) + .hasRootCauseInstanceOf(ParseException.class).rootCause() + .hasMessageStartingWith("Encountered unexpected token"); + } + @Test public void testCreateViewIfNotExists() throws JSQLParserException { String stmt = "CREATE VIEW myview IF NOT EXISTS AS SELECT * FROM mytab"; @@ -138,4 +199,5 @@ public void testCreateMaterializedViewIfNotExists() throws JSQLParserException { assertTrue(createView.isMaterialized()); assertTrue(createView.isIfNotExists()); } + }