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
26 changes: 25 additions & 1 deletion src/main/java/net/sf/jsqlparser/schema/TableReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@

import java.io.Serializable;

/** A relation target with optional PostgreSQL inheritance scope, shared by LOCK and TRUNCATE. */
/**
* A relation target with optional PostgreSQL inheritance scope, shared by LOCK, TRUNCATE and the
* targets of UPDATE, DELETE and MERGE.
*/
public class TableReference implements Serializable {
private Table table;
private boolean only;
Expand Down Expand Up @@ -84,6 +87,27 @@ public StringBuilder appendTo(StringBuilder builder) {
return builder;
}

/**
* Appends the target of UPDATE, DELETE or MERGE (PostgreSQL's relation_expr_opt_alias): the
* inheritance scope surrounds the table name and the alias of the table follows it. A target
* without a scope is the table as it renders itself, with its alias and hints.
*/
public StringBuilder appendWithAliasTo(StringBuilder builder) {
if (!only && !includeDescendants || table == null) {
return builder.append(table);
}
appendTo(builder);
if (table.getAlias() != null) {
builder.append(table.getAlias());
}
return builder;
}

/** Whether ONLY or * describes the inheritance scope of the table. */
public boolean hasInheritanceScope() {
return only || includeDescendants;
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
Expand Down
50 changes: 45 additions & 5 deletions src/main/java/net/sf/jsqlparser/statement/delete/Delete.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import net.sf.jsqlparser.expression.PreferringClause;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.schema.TableReference;
import net.sf.jsqlparser.statement.ForPortionClause;
import net.sf.jsqlparser.statement.OutputClause;
import net.sf.jsqlparser.statement.ReturningClause;
Expand All @@ -32,6 +33,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

Expand All @@ -40,14 +42,15 @@
public class Delete extends ASTNodeAccessImpl implements Statement {

private List<WithItem<?>> withItemsList;
private Table table;
private TableReference tableReference = new TableReference(null);
private ForPortionClause forPortionClause;
private OracleHint oracleHint = null;
private List<Table> tables;
private List<FromItem> usingFromItemList;
private List<Join> usingJoins;
private List<Join> joins;
private Expression where;
private String cursorName;
private PreferringClause preferringClause;
private Limit limit;
private OptionClause option;
Expand Down Expand Up @@ -131,11 +134,46 @@ public Delete withForPortionClause(ForPortionClause forPortionClause) {
}

public Table getTable() {
return table;
return tableReference.getTable();
}

/** Replaces the target table and keeps the inheritance scope of the target. */
public void setTable(Table name) {
table = name;
tableReference.setTable(name);
}

/**
* The target as PostgreSQL's relation_expr_opt_alias: the table of {@link #getTable()} with its
* alias, and whether ONLY or * describes its inheritance scope.
*/
public TableReference getTableReference() {
return tableReference;
}

public void setTableReference(TableReference tableReference) {
this.tableReference = Objects.requireNonNull(tableReference);
}

public Delete withTableReference(TableReference tableReference) {
this.setTableReference(tableReference);
return this;
}

/**
* The cursor of {@code WHERE CURRENT OF cursor}, which is written in place of the condition of
* {@link #getWhere()}.
*/
public String getCursorName() {
return cursorName;
}

public void setCursorName(String cursorName) {
this.cursorName = cursorName;
}

public Delete withCursorName(String cursorName) {
this.setCursorName(cursorName);
return this;
}

public Expression getWhere() {
Expand Down Expand Up @@ -277,7 +315,7 @@ public String toString() {
if (hasFrom) {
b.append(" FROM");
}
b.append(" ").append(table);
tableReference.appendWithAliasTo(b.append(" "));
if (forPortionClause != null) {
b.append(' ').append(forPortionClause);
}
Expand All @@ -292,7 +330,9 @@ public String toString() {
appendJoinsTo(b, usingJoins);
}

if (where != null) {
if (cursorName != null) {
b.append(" WHERE CURRENT OF ").append(cursorName);
} else if (where != null) {
b.append(" WHERE ").append(where);
}

Expand Down
28 changes: 24 additions & 4 deletions src/main/java/net/sf/jsqlparser/statement/merge/Merge.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import net.sf.jsqlparser.expression.OracleHint;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.schema.TableReference;
import net.sf.jsqlparser.statement.OutputClause;
import net.sf.jsqlparser.statement.ReturningClause;
import net.sf.jsqlparser.statement.Statement;
Expand All @@ -28,13 +29,14 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

public class Merge extends ASTNodeAccessImpl implements Statement {

private List<WithItem<?>> withItemsList;
private Table table;
private TableReference tableReference = new TableReference(null);
private OracleHint oracleHint = null;
private FromItem fromItem;
private Expression onCondition;
Expand Down Expand Up @@ -121,11 +123,29 @@ public Merge addWithItemsList(Collection<? extends WithItem<?>> withItemsList) {
}

public Table getTable() {
return table;
return tableReference.getTable();
}

/** Replaces the target table and keeps the inheritance scope of the target. */
public void setTable(Table name) {
table = name;
tableReference.setTable(name);
}

/**
* The target as PostgreSQL's relation_expr_opt_alias: the table of {@link #getTable()} with its
* alias, and whether ONLY or * describes its inheritance scope.
*/
public TableReference getTableReference() {
return tableReference;
}

public void setTableReference(TableReference tableReference) {
this.tableReference = Objects.requireNonNull(tableReference);
}

public Merge withTableReference(TableReference tableReference) {
this.setTableReference(tableReference);
return this;
}

public OracleHint getOracleHint() {
Expand Down Expand Up @@ -265,7 +285,7 @@ public String toString() {
b.append(oracleHint).append(" ");
}
b.append("INTO ");
b.append(table);
tableReference.appendWithAliasTo(b);
b.append(" USING ");
b.append(fromItem);
b.append(" ON ");
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public MergeDelete setSide(MergeSide side) {
return this;
}

/** {@code WHEN MATCHED}, or {@code WHEN NOT MATCHED BY SOURCE} with that side. */
public MergeWhen getWhen() {
return MergeWhen.ofTargetRow(side);
}

public MergeDelete withAndPredicate(Expression andPredicate) {
this.setAndPredicate(andPredicate);
return this;
Expand All @@ -52,11 +57,7 @@ public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(side == MergeSide.SOURCE ? " WHEN NOT MATCHED BY SOURCE" : " WHEN MATCHED");
if (andPredicate != null) {
b.append(" AND ").append(andPredicate.toString());
}
b.append(" THEN DELETE");
getWhen().appendTo(b, andPredicate, b::append).append("DELETE");
return b.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2024 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.merge;

import net.sf.jsqlparser.expression.Expression;

import java.io.Serializable;
import java.util.Objects;

/** PostgreSQL's {@code WHEN [NOT] MATCHED [BY SOURCE | BY TARGET] [AND ...] THEN DO NOTHING}. */
public class MergeDoNothing implements Serializable, MergeOperation {
private MergeWhen when = MergeWhen.MATCHED;
private Expression andPredicate;

public MergeDoNothing() {}

public MergeDoNothing(MergeWhen when) {
setWhen(when);
}

public MergeWhen getWhen() {
return when;
}

public void setWhen(MergeWhen when) {
this.when = Objects.requireNonNull(when);
}

public MergeDoNothing withWhen(MergeWhen when) {
this.setWhen(when);
return this;
}

public Expression getAndPredicate() {
return andPredicate;
}

public void setAndPredicate(Expression andPredicate) {
this.andPredicate = andPredicate;
}

public MergeDoNothing withAndPredicate(Expression andPredicate) {
this.setAndPredicate(andPredicate);
return this;
}

@Override
public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context) {
return mergeOperationVisitor.visit(this, context);
}

@Override
public String toString() {
StringBuilder b = new StringBuilder();
when.appendTo(b, andPredicate, b::append).append("DO NOTHING");
return b.toString();
}
}
64 changes: 52 additions & 12 deletions src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.insert.Insert;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

public class MergeInsert implements Serializable, MergeOperation {

Expand All @@ -25,6 +28,7 @@ public class MergeInsert implements Serializable, MergeOperation {
private ExpressionList<Expression> values;
private Expression whereCondition;
private MergeSide side;
private Insert.OverridingMode overridingMode = Insert.OverridingMode.NONE;

public MergeSide getSide() {
return side;
Expand Down Expand Up @@ -66,6 +70,52 @@ public void setWhereCondition(Expression whereCondition) {
this.whereCondition = whereCondition;
}

/** {@code WHEN NOT MATCHED}, or {@code WHEN NOT MATCHED BY TARGET} with that side. */
public MergeWhen getWhen() {
return MergeWhen.ofSourceRow(side);
}

/** The mode of {@code OVERRIDING {SYSTEM | USER} VALUE}, {@code NONE} without the clause. */
public Insert.OverridingMode getOverridingMode() {
return overridingMode;
}

public void setOverridingMode(Insert.OverridingMode overridingMode) {
this.overridingMode = Objects.requireNonNull(overridingMode);
}

public MergeInsert withOverridingMode(Insert.OverridingMode overridingMode) {
this.setOverridingMode(overridingMode);
return this;
}

/** Whether the clause is {@code INSERT DEFAULT VALUES}: an insert without a values list. */
public boolean isDefaultValues() {
return values == null;
}

/**
* Appends {@code INSERT DEFAULT VALUES} or {@code INSERT [(columns)] [OVERRIDING ... VALUE]
* VALUES (...)}, letting the renderer write the column and value lists.
*/
public StringBuilder appendInsertTo(StringBuilder builder,
Consumer<Expression> expressionRenderer) {
builder.append("INSERT ");
if (isDefaultValues()) {
return builder.append("DEFAULT VALUES");
}
if (columns != null) {
expressionRenderer.accept(columns);
}
if (overridingMode != Insert.OverridingMode.NONE) {
builder.append(columns != null ? " " : "").append("OVERRIDING ")
.append(overridingMode).append(" VALUE");
}
builder.append(" VALUES ");
expressionRenderer.accept(values);
return builder;
}

@Override
public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context) {
return mergeOperationVisitor.visit(this, context);
Expand All @@ -74,18 +124,8 @@ public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(" WHEN NOT MATCHED");
if (side == MergeSide.TARGET) {
b.append(" BY TARGET");
}
if (andPredicate != null) {
b.append(" AND ").append(andPredicate);
}
b.append(" THEN INSERT ");
if (columns != null) {
b.append(columns);
}
b.append(" VALUES ").append(values.toString());
getWhen().appendTo(b, andPredicate, b::append);
appendInsertTo(b, b::append);
if (whereCondition != null) {
b.append(" WHERE ").append(whereCondition);
}
Expand Down
Loading
Loading