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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ Also I would like to know about needed examples or documentation stuff.

## Extensions in the latest SNAPSHOT version 4.1

* support for **with** (ctl) for **delete**, **update** and **merge**
* support for nested `WITH` CTEs
* support for **with** (cte) for **delete**, **update** and **merge**
* introduce a max depth to allow parsing complex expression lists without performance loss (thx to @manticore-projects)
* allow all functions to have complex expressions as parameters (thx to @manticore-projects)
** API change FunctionWithCondParams production removed
* API change FunctionWithCondParams production removed
* API change in ValuesStatement: the expression list is now hold as a ItemList and not as a List<Expression>
* support for parser modification within **parseExpression** and **parseCondExpression**
* support for table schema for foreign keys
Expand Down
91 changes: 77 additions & 14 deletions src/main/java/net/sf/jsqlparser/statement/select/WithItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,64 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;

public class WithItem implements SelectBody {

private String name;
private List<SelectItem> withItemList;
private SelectBody selectBody;
private ItemsList itemsList;
private boolean useValues = true;
private boolean useBracketsForValues = false;

private SubSelect subSelect;
private boolean recursive;

/**
* Get the values (as VALUES (...) or SELECT)
*
* @return the values of the insert
*/
public ItemsList getItemsList() {
return itemsList;
}

public void setItemsList(ItemsList list) {
itemsList = list;
}

public boolean isUseValues() {
return useValues;
}

public void setUseValues(boolean useValues) {
this.useValues = useValues;
}

public WithItem withItemsList(ItemsList itemsList) {
this.setItemsList(itemsList);
return this;
}

public WithItem withUseValues(boolean useValues) {
this.setUseValues(useValues);
return this;
}

public boolean isUsingBracketsForValues() {
return useBracketsForValues;
}

public void setUseBracketsForValues(boolean useBracketsForValues) {
this.useBracketsForValues = useBracketsForValues;
}

public WithItem withUseBracketsForValues(boolean useBracketsForValues) {
this.setUseBracketsForValues(useBracketsForValues);
return this;
}

public String getName() {
return name;
}
Expand All @@ -38,12 +88,12 @@ public void setRecursive(boolean recursive) {
this.recursive = recursive;
}

public SelectBody getSelectBody() {
return selectBody;
public SubSelect getSubSelect() {
return subSelect.withUseBrackets(false);
}

public void setSelectBody(SelectBody selectBody) {
this.selectBody = selectBody;
public void setSubSelect(SubSelect subSelect) {
this.subSelect = subSelect.withUseBrackets(false);
}

/**
Expand All @@ -62,9 +112,26 @@ public void setWithItemList(List<SelectItem> withItemList) {
@Override
@SuppressWarnings({"PMD.CyclomaticComplexity"})
public String toString() {
return (recursive ? "RECURSIVE " : "") + name + ((withItemList != null) ? " " + PlainSelect.
getStringList(withItemList, true, true) : "")
+ " AS (" + selectBody + ")";
StringBuilder builder = new StringBuilder();
builder.append(recursive ? "RECURSIVE " : "");
builder.append(name);
builder.append(
(withItemList != null) ? " " + PlainSelect.getStringList(withItemList, true, true) : "");
builder.append(" AS ");

if (useValues) {
builder.append("(VALUES ");
ExpressionList expressionList = (ExpressionList) itemsList;
builder.append(
PlainSelect.getStringList(expressionList.getExpressions(), true, useBracketsForValues));
builder.append(")");
} else {
builder.append(subSelect.isUseBrackets() ? "" : "(");
builder.append(subSelect);

builder.append(subSelect.isUseBrackets() ? "" : ")");
}
return builder.toString();
}

@Override
Expand All @@ -82,8 +149,8 @@ public WithItem withWithItemList(List<SelectItem> withItemList) {
return this;
}

public WithItem withSelectBody(SelectBody selectBody) {
this.setSelectBody(selectBody);
public WithItem withSubSelect(SubSelect subSelect) {
this.setSubSelect(subSelect);
return this;
}

Expand All @@ -103,8 +170,4 @@ public WithItem addWithItemList(Collection<? extends SelectItem> withItemList) {
collection.addAll(withItemList);
return this.withWithItemList(collection);
}

public <E extends SelectBody> E getSelectBody(Class<E> type) {
return type.cast(getSelectBody());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public List<String> getTableList(Expression expr) {
@Override
public void visit(WithItem withItem) {
otherItemNames.add(withItem.getName().toLowerCase());
withItem.getSelectBody().accept(this);
withItem.getSubSelect().accept((ItemsListVisitor) this);
}

@Override
Expand Down
29 changes: 24 additions & 5 deletions src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import net.sf.jsqlparser.expression.OracleHint;
import net.sf.jsqlparser.expression.SQLServerHints;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList;
Expand Down Expand Up @@ -240,7 +241,7 @@ public void visit(SelectExpressionItem selectExpressionItem) {

@Override
public void visit(SubSelect subSelect) {
buffer.append("(");
buffer.append(subSelect.isUseBrackets() ? "(" : "");
if (subSelect.getWithItemsList() != null && !subSelect.getWithItemsList().isEmpty()) {
buffer.append("WITH ");
for (Iterator<WithItem> iter = subSelect.getWithItemsList().iterator(); iter.hasNext();) {
Expand All @@ -253,7 +254,7 @@ public void visit(SubSelect subSelect) {
}
}
subSelect.getSelectBody().accept(this);
buffer.append(")");
buffer.append(subSelect.isUseBrackets() ? ")" : "");
Alias alias = subSelect.getAlias();
if (alias != null) {
buffer.append(alias.toString());
Expand Down Expand Up @@ -484,9 +485,27 @@ public void visit(WithItem withItem) {
if (withItem.getWithItemList() != null) {
buffer.append(" ").append(PlainSelect.getStringList(withItem.getWithItemList(), true, true));
}
buffer.append(" AS (");
withItem.getSelectBody().accept(this);
buffer.append(")");
buffer.append(" AS ");

if (withItem.isUseValues()) {
ItemsList itemsList = withItem.getItemsList();
boolean useBracketsForValues = withItem.isUsingBracketsForValues();
buffer.append("(VALUES ");

ExpressionList expressionList = (ExpressionList) itemsList;
buffer.append(
PlainSelect.getStringList(expressionList.getExpressions(), true, useBracketsForValues));
buffer.append(")");
} else {
SubSelect subSelect = withItem.getSubSelect();
if (!subSelect.isUseBrackets()) {
buffer.append("(");
}
subSelect.accept((FromItemVisitor) this);
if (!subSelect.isUseBrackets()) {
buffer.append(")");
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public void visit(WithItem withItem) {
if (isNotEmpty(withItem.getWithItemList())) {
withItem.getWithItemList().forEach(wi -> wi.accept(this));
}
withItem.getSelectBody().accept(this);
withItem.getSubSelect().accept(this);
}

@Override
Expand Down
15 changes: 13 additions & 2 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1691,13 +1691,24 @@ WithItem WithItem() #WithItem:
WithItem with = new WithItem();
String name = null;
List<SelectItem> selectItems = null;
SelectBody selectBody = null;
SubSelect select = null;

ExpressionList simpleExpressionList;
}
{
[ <K_RECURSIVE> { with.setRecursive(true); } ] name=RelObjectName() { with.setName(name); }
[ "(" selectItems=SelectItemsList() ")" { with.setWithItemList(selectItems); } ]
<K_AS>
"(" selectBody = SelectBody() { with.setSelectBody(selectBody); } ")"
// if the next block looks alike an ExpressionList without Brackets, then parse as List
( LOOKAHEAD( "(" <K_VALUES> SimpleExpressionList(true) ")" )
"(" <K_VALUES>
simpleExpressionList = SimpleExpressionList(true) { with.withUseBracketsForValues(false).setItemsList(simpleExpressionList); }
")"

// Otherwise parse it as a SubSelect
| "(" select = SubSelect() { with.setSubSelect(select.withUseBrackets(false)); with.setUseValues(false); } ")"

)
{ return with; }
}

Expand Down
Loading