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
139 changes: 85 additions & 54 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package net.sf.jsqlparser.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
Expand Down Expand Up @@ -287,6 +288,9 @@ public class TablesNamesFinder<Void>
private Set<String> rowPatternVariables = java.util.Collections.emptySet();

private Set<String> otherItemNames;
// the WITH items a table reference resolves to at the current position, counted because a
// nested WITH may repeat a name of an enclosing one
private Map<String, Integer> withItemsInScope;

public static Set<String> findTables(String sqlStr) throws JSQLParserException {
TablesNamesFinder<?> tablesNamesFinder = new TablesNamesFinder<>();
Expand Down Expand Up @@ -317,10 +321,6 @@ public Set<String> getTables(Statement statement) {
init(false);
statement.accept(this, null);

// @todo: assess this carefully, maybe we want to remove more specifically
// only Aliases on WithItems, Parenthesed Selects and Lateral Selects
otherItemNames.forEach(tables::remove);

return tables;
}

Expand All @@ -336,13 +336,9 @@ public Set<String> getTablesOrOtherSources(Statement statement) {

@Override
public <S> Void visit(Select select, S context) {
List<WithItem<?>> withItemsList = select.getWithItemsList();
if (withItemsList != null && !withItemsList.isEmpty()) {
for (WithItem<?> withItem : withItemsList) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visitWithItems(select.getWithItemsList(), context);
select.accept((SelectVisitor<?>) this, context);
leaveWithItems(select.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -401,6 +397,56 @@ public Set<String> getTables(Expression expr) {
return tables;
}

/**
* Visits the WITH items of a query and brings their names into scope, which the caller ends
* with {@link #leaveWithItems(List)} after the query. An item only sees the items before it, so
* a reference to its own or a later name is a table, unless RECURSIVE makes all of them visible
* to each other. The parser sets RECURSIVE on the first item, but it covers the list.
*/
@Override
public <S> Void visitWithItems(List<WithItem<?>> withItemsList, S context) {
if (withItemsList != null) {
boolean recursive = withItemsList.stream().anyMatch(WithItem::isRecursive);
if (recursive) {
withItemsList.forEach(this::enterWithItem);
}
for (WithItem<?> withItem : withItemsList) {
withItem.accept((SelectVisitor<?>) this, context);
if (!recursive) {
enterWithItem(withItem);
}
}
}
return null;
}

private void enterWithItem(WithItem<?> withItem) {
if (withItem.getAlias() != null) {
withItemsInScope.merge(withItem.getAlias().getName(), 1, Integer::sum);
}
}

private void leaveWithItems(List<WithItem<?>> withItemsList) {
if (withItemsList != null) {
for (WithItem<?> withItem : withItemsList) {
if (withItem.getAlias() != null) {
withItemsInScope.computeIfPresent(withItem.getAlias().getName(),
(name, count) -> count > 1 ? count - 1 : null);
}
}
}
}

/**
* The target of a DML statement is a table even if a WITH item in scope has the same name.
*/
private <S> void visitTargetTable(Table table, S context) {
Map<String, Integer> scope = withItemsInScope;
withItemsInScope = new HashMap<>();
visit(table, context);
withItemsInScope = scope;
}

@Override
public <S> Void visit(WithItem<?> withItem, S context) {
if (withItem.getAlias() != null) {
Expand Down Expand Up @@ -428,12 +474,7 @@ public <S> Void visit(ParenthesedSelect select, S context) {
if (select.getAlias() != null) {
otherItemNames.add(select.getAlias().getName());
}
List<WithItem<?>> withItemsList = select.getWithItemsList();
if (withItemsList != null && !withItemsList.isEmpty()) {
for (WithItem<?> withItem : withItemsList) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visitWithItems(select.getWithItemsList(), context);
select.getSelect().accept((SelectVisitor<?>) this, context);
visitOrderBy(select.getOrderByElements(), context);
if (select.getPivot() != null) {
Expand All @@ -449,6 +490,7 @@ public <S> Void visit(ParenthesedSelect select, S context) {
if (select.getFetch() != null) {
select.getFetch().getExpression().accept(this, context);
}
leaveWithItems(select.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -517,6 +559,7 @@ private <S> Void visitPlainSelectBody(PlainSelect plainSelect, S context) {
visitSelectPagination(plainSelect, context);
visitUpdateSets(plainSelect.getSettings(), context);
visitFromItem(plainSelect.getIntoTempTable(), context);
leaveWithItems(plainSelect.getWithItemsList());
return null;
}

Expand All @@ -531,6 +574,7 @@ public <S> Void visit(PivotQuery pivotQuery, S context) {
visitLimit(pivotQuery.getLimit(), context);

visitSelectPagination(pivotQuery, context);
leaveWithItems(pivotQuery.getWithItemsList());
return null;
}

Expand All @@ -540,6 +584,7 @@ public <S> Void visit(UnPivotQuery unPivotQuery, S context) {
visitFromItem(unPivotQuery.getFromItem(), context);
visitExpressions(unPivotQuery.getOnExpressions(), context);
visitExpressions(unPivotQuery.getValueColumns(), context);
leaveWithItems(unPivotQuery.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -617,7 +662,7 @@ public <S> Void visit(Table table, S context) {
String tableWholeName = extractTableName(table);
if (table.isTableVariable()) {
otherItemNames.add(tableWholeName);
} else if (!otherItemNames.contains(tableWholeName)) {
} else if (!withItemsInScope.containsKey(table.getFullyQualifiedName())) {
tables.add(tableWholeName);
}
if (table.getPivot() != null) {
Expand Down Expand Up @@ -665,6 +710,7 @@ public <S> Void visit(OverlapsCondition overlapsCondition, S context) {
public <S> Void visit(Column tableColumn, S context) {
if (allowColumnProcessing && tableColumn.getTable() != null
&& tableColumn.getTable().getName() != null
&& !otherItemNames.contains(extractTableName(tableColumn.getTable()))
&& !rowPatternVariables.contains(
MatchRecognize.normalizeVariableName(tableColumn.getTable().getName()))) {
visit(tableColumn.getTable(), context);
Expand Down Expand Up @@ -1108,12 +1154,7 @@ private <S> void visitWindowElement(WindowElement windowElement, S context) {

@Override
public <S> Void visit(SetOperationList list, S context) {
List<WithItem<?>> withItemsList = list.getWithItemsList();
if (withItemsList != null && !withItemsList.isEmpty()) {
for (WithItem<?> withItem : withItemsList) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visitWithItems(list.getWithItemsList(), context);
for (Select selectBody : list.getSelects()) {
selectBody.accept((SelectVisitor<?>) this, context);
}
Expand All @@ -1125,6 +1166,7 @@ public <S> Void visit(SetOperationList list, S context) {
if (list.getFetch() != null) {
list.getFetch().getExpression().accept(this, context);
}
leaveWithItems(list.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -1157,7 +1199,9 @@ public void visit(LateralSubSelect lateralSubSelect) {

@Override
public <S> Void visit(TableStatement tableStatement, S context) {
visitWithItems(tableStatement.getWithItemsList(), context);
tableStatement.getTable().accept(this, null);
leaveWithItems(tableStatement.getWithItemsList());
return null;
}

Expand All @@ -1168,12 +1212,7 @@ public void visit(TableStatement tableStatement) {

@Override
public <S> Void visit(FromQuery fromQuery, S context) {
List<WithItem<?>> withItemsList = fromQuery.getWithItemsList();
if (withItemsList != null && !withItemsList.isEmpty()) {
for (WithItem<?> withItem : withItemsList) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visitWithItems(fromQuery.getWithItemsList(), context);
if (fromQuery.getFromItem() != null) {
fromQuery.getFromItem().accept(this, context);
}
Expand All @@ -1186,6 +1225,7 @@ public <S> Void visit(FromQuery fromQuery, S context) {
for (PipeOperator pipeOperator : fromQuery.getPipeOperators()) {
pipeOperator.accept(this, null);
}
leaveWithItems(fromQuery.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -1356,6 +1396,7 @@ public <S> Void visit(DateUnitExpression dateUnitExpression, S context) {
*/
protected void init(boolean allowColumnProcessing) {
otherItemNames = new LinkedHashSet<String>();
withItemsInScope = new HashMap<>();
tables = new HashSet<>();
this.allowColumnProcessing = allowColumnProcessing;
this.rowPatternVariables = java.util.Collections.emptySet();
Expand Down Expand Up @@ -1485,12 +1526,8 @@ public <S> Void visit(MySQLGroupConcat groupConcat, S context) {

@Override
public <S> Void visit(Delete delete, S context) {
if (delete.getWithItemsList() != null) {
for (WithItem<?> withItem : delete.getWithItemsList()) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visit(delete.getTable(), context);
visitWithItems(delete.getWithItemsList(), context);
visitTargetTable(delete.getTable(), context);

if (delete.getUsingFromItemList() != null) {
for (FromItem usingFromItem : delete.getUsingFromItemList()) {
Expand All @@ -1510,6 +1547,7 @@ public <S> Void visit(Delete delete, S context) {
visitLimit(delete.getLimit(), context);
visitOutputClause(delete.getOutputClause(), context);
visitReturningClause(delete.getReturningClause(), context);
leaveWithItems(delete.getWithItemsList());
return null;
}

Expand All @@ -1530,14 +1568,10 @@ public <S> Void visit(SessionStatement sessionStatement, S context) {

@Override
public <S> Void visit(Update update, S context) {
if (update.getWithItemsList() != null) {
for (WithItem<?> withItem : update.getWithItemsList()) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visitWithItems(update.getWithItemsList(), context);

if (!update.isTargetTableAlias()) {
visit(update.getTable(), context);
visitTargetTable(update.getTable(), context);
}

visitJoins(update.getStartJoins(), context);
Expand Down Expand Up @@ -1565,6 +1599,7 @@ public <S> Void visit(Update update, S context) {
visitLimit(update.getLimit(), context);
visitOutputClause(update.getOutputClause(), context);
visitReturningClause(update.getReturningClause(), context);
leaveWithItems(update.getWithItemsList());
return null;
}

Expand All @@ -1589,20 +1624,16 @@ public <S> Void visit(Insert insert, S context) {
continue;
}
for (OracleMultiInsertClause clause : branch.getClauses()) {
visit(clause.getTable(), context);
visitTargetTable(clause.getTable(), context);
if (clause.getSelect() != null) {
visit(clause.getSelect(), context);
}
}
}
} else if (insert.getTable() != null) {
visit(insert.getTable(), context);
}
if (insert.getWithItemsList() != null) {
for (WithItem<?> withItem : insert.getWithItemsList()) {
withItem.accept((SelectVisitor<?>) this, context);
}
visitTargetTable(insert.getTable(), context);
}
visitWithItems(insert.getWithItemsList(), context);
if (insert.getSetUpdateSets() != null) {
visitUpdateSets(insert.getSetUpdateSets(), context);
}
Expand All @@ -1620,6 +1651,7 @@ public <S> Void visit(Insert insert, S context) {
if (insert.getSelect() != null) {
visit(insert.getSelect(), context);
}
leaveWithItems(insert.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -2032,12 +2064,8 @@ public <S> Void visit(HexValue hexValue, S context) {

@Override
public <S> Void visit(Merge merge, S context) {
visit(merge.getTable(), context);
if (merge.getWithItemsList() != null) {
for (WithItem<?> withItem : merge.getWithItemsList()) {
withItem.accept((SelectVisitor<?>) this, context);
}
}
visitTargetTable(merge.getTable(), context);
visitWithItems(merge.getWithItemsList(), context);

if (merge.getFromItem() != null) {
merge.getFromItem().accept(this, context);
Expand All @@ -2054,6 +2082,7 @@ public <S> Void visit(Merge merge, S context) {
}
visitOutputClause(merge.getOutputClause(), context);
visitReturningClause(merge.getReturningClause(), context);
leaveWithItems(merge.getWithItemsList());
return null;
}

Expand Down Expand Up @@ -2266,6 +2295,7 @@ public void visit(Comment comment) {

@Override
public <S> Void visit(Values values, S context) {
visitWithItems(values.getWithItemsList(), context);
values.getExpressions().accept(this, context);
visitOrderBy(values.getOrderByElements(), context);
visitLimit(values.getLimit(), context);
Expand All @@ -2275,6 +2305,7 @@ public <S> Void visit(Values values, S context) {
if (values.getFetch() != null) {
values.getFetch().getExpression().accept(this, context);
}
leaveWithItems(values.getWithItemsList());
return null;
}

Expand Down
2 changes: 2 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,8 @@ Find Table Names
tableNames = TablesNamesFinder.findTablesInExpression(exprStr);
assertThat( tableNames ).containsExactlyInAnyOrder("A", "B", "C");

A name is left out only where it resolves to a ``WITH`` item in scope. An alias hides no table, and without ``RECURSIVE`` a ``WITH`` item does not see itself or the items after it. The target of a DML statement is always reported. So the result may hold names that are no tables on some databases, such as a ``WITH`` item that recurses without ``RECURSIVE``: check it against the metadata of the database if that matters.


Use the Visitor Patterns
==============================
Expand Down
Loading
Loading