Skip to content
Merged
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 @@ -85,6 +85,7 @@
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.drop.DropPolicy;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.execute.Execute;
import net.sf.jsqlparser.statement.export.Export;
import net.sf.jsqlparser.statement.export.ExportDataStatement;
Expand Down Expand Up @@ -1365,6 +1366,11 @@ public <S> Void visit(AlterUserMapping statement, S context) {
return schemaOnly();
}

@Override
public <S> Void visit(DropUserMapping statement, S context) {
return schemaOnly();
}

@Override
public <S> Void visit(CreateRule statement, S context) {
return schemaOnly();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import net.sf.jsqlparser.statement.alter.AlterTrigger;
import net.sf.jsqlparser.statement.alter.AlterPolicy;
import net.sf.jsqlparser.statement.drop.DropPolicy;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.create.statistics.CreateStatistics;
import net.sf.jsqlparser.statement.alter.AlterStatistics;
import net.sf.jsqlparser.statement.alter.AlterRelation;
Expand Down Expand Up @@ -945,6 +946,14 @@ default void visit(AlterUserMapping statement) {
visit(statement, null);
}

default <S> T visit(DropUserMapping statement, S context) {
return null;
}

default void visit(DropUserMapping statement) {
visit(statement, null);
}

default <S> T visit(CreateAccessMethod statement, S context) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.sf.jsqlparser.statement.alter.AlterRoutine;
import net.sf.jsqlparser.statement.alter.AlterTrigger;
import net.sf.jsqlparser.statement.drop.DropPolicy;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.create.statistics.CreateStatistics;
import net.sf.jsqlparser.statement.create.fdw.CreateForeignDataWrapper;
import net.sf.jsqlparser.statement.alter.AlterForeignDataWrapper;
Expand Down Expand Up @@ -1087,6 +1088,11 @@ public <S> T visit(AlterUserMapping statement, S context) {
return visitForeignDataStatement(statement, context);
}

@Override
public <S> T visit(DropUserMapping statement, S context) {
return null;
}

@Override
public <S> T visit(CreateRule statement, S context) {
statement.visitTables(table -> table.accept(fromItemVisitor, context));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/net/sf/jsqlparser/statement/drop/Drop.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
public class Drop implements Statement {

public enum ObjectType {
DATABASE, EVENT, FUNCTION, INDEX, PROCEDURE, SCHEMA, SEQUENCE, SERVER, TABLE, TABLESPACE, TRIGGER, VIEW, FOREIGN_TABLE, AGGREGATE, DOMAIN, EVENT_TRIGGER, GROUP, OWNED, ROLE, ROUTINE, RULE, TYPE, USER, SYNONYM, OTHER;
DATABASE, EVENT, FUNCTION, INDEX, PROCEDURE, SCHEMA, SEQUENCE, SERVER, TABLE, TABLESPACE, TRIGGER, VIEW, FOREIGN_TABLE, FOREIGN_DATA_WRAPPER, AGGREGATE, DOMAIN, EVENT_TRIGGER, GROUP, OWNED, ROLE, ROUTINE, RULE, TYPE, USER, SYNONYM, OTHER;

/** Targets are {@link RoutineReference}s, each with an optional argument signature. */
public boolean hasRoutineTargets() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*-
* #%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.drop;

import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;

/**
* PostgreSQL {@code DROP USER MAPPING [IF EXISTS] FOR user SERVER server}. A user mapping has no
* name of its own, so it is identified by the user and the server, as in
* {@link net.sf.jsqlparser.statement.create.usermapping.CreateUserMapping}.
*/
public class DropUserMapping implements Statement {
private String user;
private String server;
private boolean ifExists;

public String getUser() {
return user;
}

public void setUser(String value) {
user = value;
}

public String getServer() {
return server;
}

public void setServer(String value) {
server = value;
}

public boolean isIfExists() {
return ifExists;
}

public void setIfExists(boolean value) {
ifExists = value;
}

public StringBuilder appendTo(StringBuilder sql) {
sql.append("DROP USER MAPPING ");
if (ifExists) {
sql.append("IF EXISTS ");
}
return sql.append("FOR ").append(user).append(" SERVER ").append(server);
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
}

@Override
public <T, S> T accept(StatementVisitor<T> visitor, S context) {
return visitor.visit(this, context);
}
}
7 changes: 7 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
import net.sf.jsqlparser.statement.delete.ParenthesedDelete;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.drop.DropPolicy;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.execute.Execute;
import net.sf.jsqlparser.statement.execute.ExecuteArgument;
import net.sf.jsqlparser.statement.export.Export;
Expand Down Expand Up @@ -3247,6 +3248,12 @@ public <S> Void visit(AlterUserMapping statement, S context) {
return null;
}

@Override
public <S> Void visit(DropUserMapping statement, S context) {
// User and server names do not refer to tables.
return null;
}

@Override
public <S> Void visit(CreateAccessMethod statement, S context) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import net.sf.jsqlparser.statement.alter.AlterTrigger;
import net.sf.jsqlparser.statement.alter.AlterPolicy;
import net.sf.jsqlparser.statement.drop.DropPolicy;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.create.statistics.CreateStatistics;
import net.sf.jsqlparser.statement.alter.AlterStatistics;
import net.sf.jsqlparser.statement.alter.AlterRelation;
Expand Down Expand Up @@ -1003,6 +1004,11 @@ public <S> StringBuilder visit(AlterUserMapping statement, S context) {
expression -> expression.accept(expressionDeParser, context));
}

@Override
public <S> StringBuilder visit(DropUserMapping statement, S context) {
return statement.appendTo(builder);
}

@Override
public <S> StringBuilder visit(CreateAccessMethod statement, S context) {
return statement.appendTo(builder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import net.sf.jsqlparser.statement.alter.AlterTablespace;
import net.sf.jsqlparser.statement.alter.AlterPolicy;
import net.sf.jsqlparser.statement.drop.DropPolicy;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.create.statistics.CreateStatistics;

import net.sf.jsqlparser.statement.create.accessmethod.CreateAccessMethod;
Expand Down Expand Up @@ -1292,6 +1293,12 @@ public <S> Void visit(AlterUserMapping statement, S context) {
return null;
}

@Override
public <S> Void visit(DropUserMapping statement, S context) {
validateFeature(Feature.drop);
return null;
}

@Override
public <S> Void visit(CreateAccessMethod statement, S context) {
validateFeature(Feature.createAccessMethod);
Expand Down
27 changes: 24 additions & 3 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -4868,6 +4868,11 @@ Statement SingleStatement() :
|
LOOKAHEAD(<K_DROP> <K_POLICY>) stm = DropPolicy()
|
LOOKAHEAD({ getToken(1).kind == K_DROP && getToken(2).kind == K_USER
&& "MAPPING".equalsIgnoreCase(getToken(3).image)
&& (getToken(4).kind == K_FOR || getToken(4).kind == K_IF) })
stm = DropUserMapping()
|
stm = Drop()
|
LOOKAHEAD({ getToken(1).kind == K_REASSIGN && "OWNED".equalsIgnoreCase(getToken(2).image) })
Expand Down Expand Up @@ -19994,7 +19999,7 @@ Drop Drop():
List<AlgorithmOrLockOption> indexOptions = new ArrayList<AlgorithmOrLockOption>();
int indexOptionPosition = 0;
boolean useTemporary = false;
boolean foreign = false;
String foreignType = null;
boolean eventTrigger = false;
CascadeBehavior behavior = null;
String engine;
Expand All @@ -20003,7 +20008,11 @@ Drop Drop():
<K_DROP>
[ <K_MATERIALIZED> { drop.setMaterialized(true);} ]
(
<K_FOREIGN> tk=<K_TABLE> { foreign = true; }
<K_FOREIGN> (
<K_TABLE> { foreignType = "FOREIGN TABLE"; }
|
<K_DATA> ContextualKeyword("WRAPPER") { foreignType = "FOREIGN DATA WRAPPER"; }
)
|
// PostgreSQL DROP OWNED BY role [, ...]
LOOKAHEAD({ isKeywordAhead("OWNED") && getToken(2).kind == K_BY }) tk=<S_IDENTIFIER> <K_BY>
Expand Down Expand Up @@ -20051,7 +20060,7 @@ Drop Drop():
|
tk=<K_GROUP>
)
{ drop.setType(foreign ? "FOREIGN TABLE" : eventTrigger ? "EVENT TRIGGER" : tk.image); }
{ drop.setType(foreignType != null ? foreignType : eventTrigger ? "EVENT TRIGGER" : tk.image); }
[ LOOKAHEAD({ drop.getObjectType() == Drop.ObjectType.INDEX
&& getToken(1).kind == K_CONCURRENTLY })
<K_CONCURRENTLY> { drop.setConcurrently(true); } ]
Expand Down Expand Up @@ -25252,6 +25261,18 @@ CreateUserMapping CreateUserMapping():
{ return statement; }
}

DropUserMapping DropUserMapping():
{
DropUserMapping statement = new DropUserMapping(); String name;
}
{
<K_DROP> <K_USER> ContextualKeyword("MAPPING")
[ LOOKAHEAD(2) <K_IF> <K_EXISTS> { statement.setIfExists(true); } ]
<K_FOR> name=RelObjectName() { statement.setUser(name); }
ContextualKeyword("SERVER") name=RelObjectName() { statement.setServer(name); }
{ return statement; }
}

AlterUserMapping AlterUserMapping():
{
AlterUserMapping statement = new AlterUserMapping(); String name; List<ForeignDataOption> options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.CascadeBehavior;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitorAdapter;
import net.sf.jsqlparser.statement.alter.AlterForeignDataWrapper;
Expand All @@ -27,6 +30,8 @@
import net.sf.jsqlparser.statement.create.server.CreateServer;
import net.sf.jsqlparser.statement.create.table.ForeignDataOption;
import net.sf.jsqlparser.statement.create.usermapping.CreateUserMapping;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.drop.DropUserMapping;
import net.sf.jsqlparser.statement.foreign.ForeignDataStatement;
import net.sf.jsqlparser.util.TablesNamesFinder;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
Expand Down Expand Up @@ -70,6 +75,50 @@ void foreignObjectsShareOptionsAndKeepBoundaries(String sql) throws JSQLParserEx
assertFalse(new Validation(List.of(FeaturesAllowed.SELECT), sql).validate().isEmpty());
}

@ParameterizedTest
@ValueSource(strings = {"DROP FOREIGN DATA WRAPPER w",
"DROP FOREIGN DATA WRAPPER IF EXISTS w1, w2 CASCADE",
"DROP FOREIGN DATA WRAPPER w RESTRICT",
"DROP USER MAPPING FOR CURRENT_USER SERVER s",
"DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER s",
"DROP USER MAPPING FOR USER SERVER s",
"DROP USER MAPPING FOR \"user name\" SERVER \"server name\""})
void dropsForeignDataObjects(String sql) throws JSQLParserException {
Statement statement = CCJSqlParserUtil.parse(sql);
assertEquals(sql, statement.toString());
assertTrue(new TablesNamesFinder().getTables(statement).isEmpty());
roundTrip(statement);
assertEquals(2, CCJSqlParserUtil.parseStatements(sql + "; SELECT 1").size());
assertTrue(new Validation(List.of(FeaturesAllowed.DDL), sql).validate().isEmpty());
assertFalse(new Validation(List.of(FeaturesAllowed.SELECT), sql).validate().isEmpty());
}

@Test
void dropStatementsKeepObjectKindAndMappingKey() throws JSQLParserException {
Drop wrapper = (Drop) CCJSqlParserUtil.parse(
"DROP FOREIGN DATA WRAPPER IF EXISTS w1, w2 CASCADE");
assertEquals(Drop.ObjectType.FOREIGN_DATA_WRAPPER, wrapper.getObjectType());
assertEquals(List.of("w1", "w2"),
wrapper.getNames().stream().map(Table::getFullyQualifiedName)
.collect(Collectors.toList()));
assertTrue(wrapper.isIfExists());
assertEquals(CascadeBehavior.CASCADE, wrapper.getBehavior());

DropUserMapping mapping = (DropUserMapping) CCJSqlParserUtil.parse(
"DROP USER MAPPING IF EXISTS FOR CURRENT_USER SERVER s");
assertTrue(mapping.isIfExists());
assertEquals("CURRENT_USER", mapping.getUser());
assertEquals("s", mapping.getServer());
mapping.setIfExists(false);
mapping.setServer("s2");
assertEquals("DROP USER MAPPING FOR CURRENT_USER SERVER s2", mapping.toString());
roundTrip(mapping);

// MAPPING followed by neither FOR nor IF is still a user name
Drop user = (Drop) CCJSqlParserUtil.parse("DROP USER mapping");
assertEquals(Drop.ObjectType.USER, user.getObjectType());
}

@Test
void omittedAndRemovedHandlersStayDistinct() throws JSQLParserException {
CreateForeignDataWrapper create = (CreateForeignDataWrapper) CCJSqlParserUtil.parse(
Expand Down Expand Up @@ -210,7 +259,9 @@ public <S> String visit(CreateUserMapping statement, S context) {
"ALTER SERVER s OPTIONS(SET x)", "ALTER SERVER s OPTIONS(DROP x 'y')",
"ALTER SERVER s OPTIONS()", "ALTER SERVER s OPTIONS(host = 'localhost')",
"CREATE SERVER s TYPE NULL FOREIGN DATA WRAPPER w", "ALTER SERVER s VERSION 18",
"ALTER SERVER s RENAME TO s2 OPTIONS(x 'y')"})
"ALTER SERVER s RENAME TO s2 OPTIONS(x 'y')",
"DROP USER MAPPING FOR u", "DROP USER MAPPING FOR u SERVER s CASCADE",
"DROP USER MAPPING IF EXISTS u SERVER s"})
void rejectsWrongObjectAndOptionForms(String sql) {
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql));
}
Expand Down
Loading