in tools/query_breakdown/src/main/java/com/google/bigquery/QueryBreakdown.java [128:210]
private void loop(String inputQuery, int replacementLimit, Node parent, int depth,
LocationTracker locationTracker) {
// termination for branch if it is deeper than the current solution
if (depth > minimumUnparseableComp) {
return;
}
try {
// parses the query
parser.parseQuery(inputQuery);
} catch (SqlParseException e) {
/* generates new queries through deletion and replacement */
SqlParserPos pos = e.getPos();
// if statement checks for EOF and validator
if ((pos.getLineNum() != 0 && pos.getColumnNum() != 0)
&& !(e.getCause().toString().contains("Encountered \"<EOF>\""))
&& !(e.getCause().toString().contains("Encountered: <EOF>"))
&& !e.getCause().toString().contains("SqlValidatorException")) {
// gets the error location in the original query
Pair originalStart =
locationTracker.getOriginalPosition(pos.getLineNum(), pos.getColumnNum());
Pair originalEnd =
locationTracker.getOriginalPosition(pos.getEndLineNum(), pos.getEndColumnNum());
/* deletion: gets the new query, creates a node, and calls the loop again */
// gets the new query
String deletionQuery = deletion(inputQuery, pos.getLineNum(), pos.getColumnNum(),
pos.getEndLineNum(), pos.getEndColumnNum());
// updates the location tracker to reflect the deletion
LocationTracker deletedLt = locationTracker.delete
(pos.getLineNum(), pos.getColumnNum(), pos.getEndLineNum(), pos.getEndColumnNum());
// counts number of characters deleted keeping in mind multi-line new line addition
int deletionNumber = (pos.getLineNum() == pos.getEndLineNum()) ? inputQuery.length() -
deletionQuery.length() : inputQuery.length() - deletionQuery.length() + 1;
// creates a node for this deletion
Node deletionNode = new Node(parent, originalStart.getX(), originalStart.getY(),
originalEnd.getX(), originalEnd.getY(), deletionNumber);
// calls the loop again
loop(deletionQuery, replacementLimit, deletionNode, depth + 1, deletedLt);
/* replacement: gets the new queries, creates nodes, and calls the loop for each of them */
ArrayList<ReplacedComponent> replacementQueries = replacement(inputQuery, replacementLimit,
pos.getLineNum(), pos.getColumnNum(), pos.getEndLineNum(), pos.getEndColumnNum(),
e.getExpectedTokenNames());
// recursively loops through the new queries
for (ReplacedComponent r: replacementQueries) {
// updates the location tracker to reflect the replacement
LocationTracker replacedLt = locationTracker.replace(pos.getLineNum(), pos.getColumnNum(),
pos.getEndLineNum(), pos.getEndColumnNum(), r.getOriginal(), r.getReplacement());
// creates the node
Node replacementNode = new Node(parent, originalStart.getX(), originalStart.getY(),
originalEnd.getX(), originalEnd.getY(), r.getOriginal(), r.getReplacement(),
r.getOriginal().length());
// calls the loop again
loop(r.getQuery(), replacementLimit, replacementNode, depth + 1, replacedLt);
}
/* termination to end the loop if the instance was not a full run through the query.
In other words, it ensures that the termination condition is not hit on the way back
up the tree */
return;
}
} catch (Exception e) {
/* this is boiler plate code when a different exception is thrown from using
a different parser
*/
return;
}
// termination condition: if the parsing doesn't throw exceptions, then the leaf is reached
if (depth < minimumUnparseableComp) {
minimumUnparseableComp = depth;
solution = parent;
finalString = inputQuery;
}
}