Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class CreateView implements Statement {
private boolean secure = false;
private TemporaryOption temp = TemporaryOption.NONE;
private AutoRefreshOption autoRefresh = AutoRefreshOption.NONE;
private Boolean backup;
private boolean withReadOnly = false;
private boolean ifNotExists = false;
private List<String> viewCommentOptions = null;
Expand Down Expand Up @@ -122,8 +123,11 @@ public void setWithData(Boolean withData) {
this.withData = withData;
}

/** Checks combinations introduced by the PostgreSQL view clauses. */
/** Checks combinations requiring ordinary, recursive or materialized views. */
public void validateOptions() {
if (backup != null && !materialized) {
throw new IllegalArgumentException("BACKUP requires a materialized view");
}
if (recursive && (materialized || columnNames == null || columnNames.isEmpty()
|| checkOption != null)) {
throw new IllegalArgumentException(
Expand Down Expand Up @@ -220,6 +224,30 @@ public void setAutoRefresh(AutoRefreshOption autoRefresh) {
this.autoRefresh = autoRefresh;
}

/** Null means omitted; true and false preserve Redshift's BACKUP YES and BACKUP NO. */
public Boolean getBackup() {
return backup;
}

public void setBackup(Boolean backup) {
this.backup = backup;
}

public CreateView withBackup(Boolean backup) {
setBackup(backup);
return this;
}

/** Shared SQL rendering for options immediately following the materialized view name. */
public void appendMaterializationOptionsTo(StringBuilder sql) {
if (backup != null) {
sql.append(backup ? " BACKUP YES" : " BACKUP NO");
}
if (autoRefresh != AutoRefreshOption.NONE) {
sql.append(" AUTO REFRESH ").append(autoRefresh.name());
}
}

public boolean isWithReadOnly() {
return withReadOnly;
}
Expand Down Expand Up @@ -266,9 +294,7 @@ public String toString() {
if (ifNotExists && ifNotExistsAfterViewName) {
sql.append(" IF NOT EXISTS");
}
if (autoRefresh != AutoRefreshOption.NONE) {
sql.append(" AUTO REFRESH ").append(autoRefresh.name());
}
appendMaterializationOptionsTo(sql);
if (columnNames != null) {
sql.append("(");
sql.append(columnNames);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
*/
package net.sf.jsqlparser.util.deparser;

import net.sf.jsqlparser.statement.create.view.AutoRefreshOption;
import net.sf.jsqlparser.statement.create.view.CreateView;
import net.sf.jsqlparser.statement.create.view.TemporaryOption;
import net.sf.jsqlparser.statement.select.PlainSelect;
Expand Down Expand Up @@ -74,9 +73,7 @@ public void deParse(CreateView createView) {
if (createView.isIfNotExists() && createView.isIfNotExistsAfterViewName()) {
builder.append(" IF NOT EXISTS");
}
if (createView.getAutoRefresh() != AutoRefreshOption.NONE) {
builder.append(" AUTO REFRESH ").append(createView.getAutoRefresh().name());
}
createView.appendMaterializationOptionsTo(builder);
if (createView.getColumnNames() != null) {
builder.append("(");
builder.append(createView.getColumnNames());
Expand Down
2 changes: 2 additions & 0 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -12907,6 +12907,8 @@ CreateView CreateView(boolean isUsingOrReplace):
<K_VIEW>
[ LOOKAHEAD(3) <K_IF> <K_NOT> <K_EXISTS> { createView.setIfNotExists(true); } ]
view=Table() { createView.setView(view); }
[ LOOKAHEAD({ isKeywordAhead("BACKUP") }) tk=<S_IDENTIFIER>
(tk=<K_YES> | tk=<K_NO>) { createView.setBackup(tk.kind == K_YES); } ]
[LOOKAHEAD(3) <K_AUTO> <K_REFRESH> (tk=<K_YES> | tk=<K_NO>) { createView.setAutoRefresh(AutoRefreshOption.from(tk.image)); } ]
[LOOKAHEAD(3) <K_IF> <K_NOT> <K_EXISTS> {
createView.setIfNotExists(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.create;

import static org.junit.jupiter.api.Assertions.*;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
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.Select;
import net.sf.jsqlparser.test.TestUtils;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class RedshiftCreateViewTest {
@ParameterizedTest
@ValueSource(strings = {"", " BACKUP YES", " BACKUP NO"})
void preserveBackupAndOmission(String clause) throws JSQLParserException {
String sql = "CREATE MATERIALIZED VIEW myview" + clause + " AS SELECT * FROM mytab";
CreateView view = (CreateView) TestUtils.assertSqlCanBeParsedAndDeparsed(sql);
assertEquals(clause.isEmpty() ? null : clause.endsWith("YES"), view.getBackup());
CreateView reparsed = (CreateView) CCJSqlParserUtil.parse(view.toString());
assertEquals(view.getBackup(), reparsed.getBackup());
assertTrue(TablesNamesFinder.findTables(sql).contains("mytab"));
}

@ParameterizedTest
@ValueSource(strings = {"YES", "NO"})
void combineWithAutoRefresh(String value) throws JSQLParserException {
CreateView view = (CreateView) TestUtils.assertSqlCanBeParsedAndDeparsed(
"CREATE MATERIALIZED VIEW report.mv BACKUP NO AUTO REFRESH " + value
+ " AS SELECT * FROM source");
assertEquals(Boolean.FALSE, view.getBackup());
assertEquals(AutoRefreshOption.from(value), view.getAutoRefresh());
}

@ParameterizedTest
@ValueSource(strings = {
"CREATE MATERIALIZED VIEW mv BACKUP AS SELECT 1",
"CREATE MATERIALIZED VIEW mv BACKUP TRUE AS SELECT 1",
"CREATE MATERIALIZED VIEW mv BACKUP YES BACKUP NO AS SELECT 1",
"CREATE MATERIALIZED VIEW mv AUTO REFRESH YES BACKUP NO AS SELECT 1",
"CREATE VIEW mv BACKUP NO AS SELECT 1"
})
void rejectMalformedOptions(String sql) {
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql,
parser -> parser.withUnsupportedStatements(false)));
}

@Test
void constructAndClearBackup() throws JSQLParserException {
CreateView view = new CreateView().withMaterialized(true).withBackup(false)
.withView(new Table("mv"))
.withSelect((Select) CCJSqlParserUtil.parse("SELECT * FROM source"));
TestUtils.assertStatementCanBeDeparsedAs(view,
"CREATE MATERIALIZED VIEW mv BACKUP NO AS SELECT * FROM source");
view.setBackup(null);
TestUtils.assertStatementCanBeDeparsedAs(view,
"CREATE MATERIALIZED VIEW mv AS SELECT * FROM source");
view.setMaterialized(false);
view.setBackup(true);
assertThrows(IllegalArgumentException.class, view::validateOptions);
}

@Test
void backupRemainsAnIdentifier() throws JSQLParserException {
TestUtils.assertSqlCanBeParsedAndDeparsed(
"CREATE MATERIALIZED VIEW backup BACKUP YES AS SELECT backup FROM backup");
TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT backup FROM backup");
}
}
Loading